Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport/backport 333 to 1.x #398

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 52 additions & 5 deletions lib/aws/AwsSigv4Signer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,66 @@ const Transport = require('../Transport')
const aws4 = require('aws4')
const AwsSigv4SignerError = require('./errors')

function AwsSigv4Signer (opts) {
const getAwsSDKCredentialsProvider = async () => {
// First try V3
try {
const awsV3 = await import('@aws-sdk/credential-provider-node')
if (typeof awsV3.defaultProvider === 'function') {
return awsV3.defaultProvider()
}
} catch (err) {
// Ignore
}
try {
const awsV2 = await import('aws-sdk')
if (awsV2.default && typeof awsV2.default.config.getCredentials === 'function') {
return () =>
new Promise((resolve, reject) => {
awsV2.default.config.getCredentials((err, credentials) => {
if (err) {
reject(err)
} else {
resolve(credentials)
}
})
})
}
} catch (err) {
// Ignore
}

throw new AwsSigv4SignerError(
'Unable to find a valid AWS SDK, please provide a valid getCredentials function to AwsSigv4Signer options.'
)
}

const awsDefaultCredentialsProvider = () =>
new Promise((resolve, reject) => {
getAwsSDKCredentialsProvider()
.then((provider) => {
provider().then(resolve).catch(reject)
})
.catch((err) => {
reject(err)
})
})

function AwsSigv4Signer (opts = {}) {
const credentialsState = {
credentials: null
}
if (opts && (!opts.region || opts.region === null || opts.region === '')) {
if (!opts.region) {
throw new AwsSigv4SignerError('Region cannot be empty')
}
if (opts && typeof opts.getCredentials !== 'function') {
throw new AwsSigv4SignerError('getCredentials function is required')
if (!opts.service) {
opts.service = 'es'
}
if (typeof opts.getCredentials !== 'function') {
opts.getCredentials = awsDefaultCredentialsProvider
}

function buildSignedRequestObject (request = {}) {
request.service = 'es'
request.service = opts.service
request.region = opts.region
request.headers = request.headers || {}
request.headers.host = request.hostname
Expand Down
64 changes: 44 additions & 20 deletions test/unit/lib/aws/awssigv4signer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const { Connection } = require('../../../../index')
const { Client, buildServer } = require('../../../utils')

test('Sign with SigV4', (t) => {
t.plan(2)
t.plan(3)

const mockCreds = {
accessKeyId: uuidv4(),
Expand Down Expand Up @@ -51,46 +51,70 @@ test('Sign with SigV4', (t) => {
const signedRequest = auth.buildSignedRequestObject(request)
t.hasProp(signedRequest.headers, 'X-Amz-Date')
t.hasProp(signedRequest.headers, 'Authorization')
t.same(signedRequest.service, 'es')
})

test('Sign with SigV4 failure (with empty region)', (t) => {
t.plan(2)

const mockCreds = {
accessKeyId: uuidv4(),
secretAccessKey: uuidv4()
}

const mockRegions = [{ region: undefined }, { region: null }, { region: '' }, {}]

const AwsSigv4SignerOptions = {
getCredentials: () =>
new Promise((resolve) => {
setTimeout(() => resolve(mockCreds), 100)
})
}

try {
AwsSigv4Signer(AwsSigv4SignerOptions)
t.fail('Should fail')
} catch (err) {
t.ok(err instanceof AwsSigv4SignerError)
t.same(err.message, 'Region cannot be empty')
}
mockRegions.forEach((region) => {
try {
AwsSigv4Signer(Object.assign({}, AwsSigv4SignerOptions, region))
t.fail('Should fail')
} catch (err) {
t.ok(err instanceof AwsSigv4SignerError)
t.same(err.message, 'Region cannot be empty')
}
})

t.end()
})

test('Sign with SigV4 failure (without getCredentials function)', (t) => {
t.plan(2)
test('Sign with SigV4 and provided service', (t) => {
t.plan(1)

const mockRegion = 'us-west-2'
const mockCreds = {
accessKeyId: uuidv4(),
secretAccessKey: uuidv4()
}

const AwsSigv4SignerOptions = { region: mockRegion }
const mockRegion = 'us-west-2'
const mockService = 'foo'

try {
AwsSigv4Signer(AwsSigv4SignerOptions)
t.fail('Should fail')
} catch (err) {
t.ok(err instanceof AwsSigv4SignerError)
t.same(err.message, 'getCredentials function is required')
const AwsSigv4SignerOptions = {
getCredentials: () =>
new Promise((resolve) => {
setTimeout(() => resolve(mockCreds), 100)
}),
region: mockRegion,
service: mockService
}

const auth = AwsSigv4Signer(AwsSigv4SignerOptions)

const connection = new Connection({
url: new URL('https://localhost:9200')
})

const request = connection.buildRequestObject({
path: '/hello',
method: 'GET'
})

const signedRequest = auth.buildSignedRequestObject(request)
t.same(signedRequest.service, mockService)
})

test('Basic aws (promises)', (t) => {
Expand Down