Compare commits
No commits in common. "321e449a12dcaef9c0d39586bfa8a41571d7a2cb" and "fe656e511dfc8cae191ec5b2cf3cd23c5e458aaf" have entirely different histories.
321e449a12
...
fe656e511d
10
index.ts
10
index.ts
|
@ -1,14 +1,18 @@
|
|||
export * as auth from './lib/auth/auth'
|
||||
export const auth = require('./lib/auth/auth');
|
||||
export * as constants from './lib/constants';
|
||||
export * as https from './lib/https'
|
||||
export const db = require('./lib/db');
|
||||
export const errors = require('./lib/errors.js')
|
||||
export const errors = require('./lib/errors.js');
|
||||
export const shuffle = require('./lib/shuffle');
|
||||
export const stringHash = require('./lib/stringHash');
|
||||
export const ipCheck = require('./lib/ipCheck');
|
||||
export const jsutil = require('./lib/jsutil');
|
||||
export const Clustering = require('./lib/Clustering');
|
||||
|
||||
export const https = {
|
||||
ciphers: require('./lib/https/ciphers.js'),
|
||||
dhparam: require('./lib/https/dh2048.js'),
|
||||
};
|
||||
|
||||
export const algorithms = {
|
||||
list: {
|
||||
Basic: require('./lib/algos/list/basic').List,
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import * as constants from '../constants';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const constants = require('../constants');
|
||||
|
||||
/**
|
||||
* Class containing requester's information received from Vault
|
||||
|
@ -6,15 +8,9 @@ import * as constants from '../constants';
|
|||
* shortid, email, accountDisplayName and IAMdisplayName (if applicable)
|
||||
* @return {AuthInfo} an AuthInfo instance
|
||||
*/
|
||||
export default class AuthInfo {
|
||||
arn: string;
|
||||
canonicalID: string;
|
||||
shortid: string;
|
||||
email: string;
|
||||
accountDisplayName: string;
|
||||
IAMdisplayName: string;
|
||||
|
||||
constructor(objectFromVault: any) {
|
||||
class AuthInfo {
|
||||
constructor(objectFromVault) {
|
||||
// amazon resource name for IAM user (if applicable)
|
||||
this.arn = objectFromVault.arn;
|
||||
// account canonicalID
|
||||
|
@ -57,8 +53,10 @@ export default class AuthInfo {
|
|||
return this.canonicalID.startsWith(
|
||||
`${constants.zenkoServiceAccount}/`);
|
||||
}
|
||||
isRequesterThisServiceAccount(serviceName: string) {
|
||||
const computedCanonicalID = `${constants.zenkoServiceAccount}/${serviceName}`;
|
||||
return this.canonicalID === computedCanonicalID;
|
||||
isRequesterThisServiceAccount(serviceName) {
|
||||
return this.canonicalID ===
|
||||
`${constants.zenkoServiceAccount}/${serviceName}`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AuthInfo;
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
import { Logger } from 'werelogs';
|
||||
import errors from '../errors';
|
||||
import AuthInfo from './AuthInfo';
|
||||
const errors = require('../errors');
|
||||
const AuthInfo = require('./AuthInfo');
|
||||
|
||||
/** vaultSignatureCb parses message from Vault and instantiates
|
||||
* @param err - error from vault
|
||||
* @param authInfo - info from vault
|
||||
* @param log - log for request
|
||||
* @param callback - callback to authCheck functions
|
||||
* @param [streamingV4Params] - present if v4 signature;
|
||||
* @param {object} err - error from vault
|
||||
* @param {object} authInfo - info from vault
|
||||
* @param {object} log - log for request
|
||||
* @param {function} callback - callback to authCheck functions
|
||||
* @param {object} [streamingV4Params] - present if v4 signature;
|
||||
* items used to calculate signature on chunks if streaming auth
|
||||
* @return {undefined}
|
||||
*/
|
||||
function vaultSignatureCb(
|
||||
err: Error | null,
|
||||
authInfo: { message: { body: any } },
|
||||
log: Logger,
|
||||
callback: (err: Error | null, data?: any, results?: any, params?: any) => void,
|
||||
streamingV4Params?: any
|
||||
) {
|
||||
function vaultSignatureCb(err, authInfo, log, callback, streamingV4Params) {
|
||||
// vaultclient API guarantees that it returns:
|
||||
// - either `err`, an Error object with `code` and `message` properties set
|
||||
// - or `err == null` and `info` is an object with `message.code` and
|
||||
|
@ -30,13 +24,11 @@ function vaultSignatureCb(
|
|||
const info = authInfo.message.body;
|
||||
const userInfo = new AuthInfo(info.userInfo);
|
||||
const authorizationResults = info.authorizationResults;
|
||||
const auditLog: { accountDisplayName: string, IAMdisplayName?: string } =
|
||||
{ accountDisplayName: userInfo.getAccountDisplayName() };
|
||||
const auditLog = { accountDisplayName: userInfo.getAccountDisplayName() };
|
||||
const iamDisplayName = userInfo.getIAMdisplayName();
|
||||
if (iamDisplayName) {
|
||||
auditLog.IAMdisplayName = iamDisplayName;
|
||||
}
|
||||
// @ts-ignore
|
||||
log.addDefaultFields(auditLog);
|
||||
return callback(null, userInfo, authorizationResults, streamingV4Params);
|
||||
}
|
||||
|
@ -47,63 +39,43 @@ function vaultSignatureCb(
|
|||
* authentication backends.
|
||||
* @class Vault
|
||||
*/
|
||||
export default class Vault {
|
||||
client: any;
|
||||
implName: string;
|
||||
|
||||
class Vault {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {object} client - authentication backend or vault client
|
||||
* @param {string} implName - implementation name for auth backend
|
||||
*/
|
||||
constructor(client: any, implName: string) {
|
||||
constructor(client, implName) {
|
||||
this.client = client;
|
||||
this.implName = implName;
|
||||
}
|
||||
/**
|
||||
* authenticateV2Request
|
||||
*
|
||||
* @param params - the authentication parameters as returned by
|
||||
* @param {string} params - the authentication parameters as returned by
|
||||
* auth.extractParams
|
||||
* @param params.version - shall equal 2
|
||||
* @param params.data.accessKey - the user's accessKey
|
||||
* @param params.data.signatureFromRequest - the signature read
|
||||
* @param {number} params.version - shall equal 2
|
||||
* @param {string} params.data.accessKey - the user's accessKey
|
||||
* @param {string} params.data.signatureFromRequest - the signature read
|
||||
* from the request
|
||||
* @param params.data.stringToSign - the stringToSign
|
||||
* @param params.data.algo - the hashing algorithm used for the
|
||||
* @param {string} params.data.stringToSign - the stringToSign
|
||||
* @param {string} params.data.algo - the hashing algorithm used for the
|
||||
* signature
|
||||
* @param params.data.authType - the type of authentication (query
|
||||
* @param {string} params.data.authType - the type of authentication (query
|
||||
* or header)
|
||||
* @param params.data.signatureVersion - the version of the
|
||||
* @param {string} params.data.signatureVersion - the version of the
|
||||
* signature (AWS or AWS4)
|
||||
* @param [params.data.signatureAge] - the age of the signature in
|
||||
* @param {number} [params.data.signatureAge] - the age of the signature in
|
||||
* ms
|
||||
* @param params.data.log - the logger object
|
||||
* @param {string} params.data.log - the logger object
|
||||
* @param {RequestContext []} requestContexts - an array of RequestContext
|
||||
* instances which contain information for policy authorization check
|
||||
* @param callback - callback with either error or user info
|
||||
* @param {function} callback - callback with either error or user info
|
||||
* @returns {undefined}
|
||||
*/
|
||||
authenticateV2Request(
|
||||
params: {
|
||||
version: 2;
|
||||
log: Logger;
|
||||
data: {
|
||||
securityToken: string;
|
||||
accessKey: string;
|
||||
signatureFromRequest: string;
|
||||
stringToSign: string;
|
||||
algo: string;
|
||||
authType: 'query' | 'header';
|
||||
signatureVersion: string;
|
||||
signatureAge?: number;
|
||||
log: Logger;
|
||||
};
|
||||
},
|
||||
requestContexts: any[],
|
||||
callback: (err: Error | null, data?: any) => void
|
||||
) {
|
||||
authenticateV2Request(params, requestContexts, callback) {
|
||||
params.log.debug('authenticating V2 request');
|
||||
let serializedRCsArr: any;
|
||||
let serializedRCsArr;
|
||||
if (requestContexts) {
|
||||
serializedRCsArr = requestContexts.map(rc => rc.serialize());
|
||||
}
|
||||
|
@ -113,66 +85,44 @@ export default class Vault {
|
|||
params.data.accessKey,
|
||||
{
|
||||
algo: params.data.algo,
|
||||
// @ts-ignore
|
||||
reqUid: params.log.getSerializedUids(),
|
||||
logger: params.log,
|
||||
securityToken: params.data.securityToken,
|
||||
requestContext: serializedRCsArr,
|
||||
},
|
||||
(err: Error | null, userInfo?: any) => vaultSignatureCb(err, userInfo,
|
||||
params.log, callback),
|
||||
(err, userInfo) => vaultSignatureCb(err, userInfo,
|
||||
params.log, callback)
|
||||
);
|
||||
}
|
||||
|
||||
/** authenticateV4Request
|
||||
* @param params - the authentication parameters as returned by
|
||||
* @param {object} params - the authentication parameters as returned by
|
||||
* auth.extractParams
|
||||
* @param params.version - shall equal 4
|
||||
* @param params.data.log - the logger object
|
||||
* @param params.data.accessKey - the user's accessKey
|
||||
* @param params.data.signatureFromRequest - the signature read
|
||||
* @param {number} params.version - shall equal 4
|
||||
* @param {string} params.data.log - the logger object
|
||||
* @param {string} params.data.accessKey - the user's accessKey
|
||||
* @param {string} params.data.signatureFromRequest - the signature read
|
||||
* from the request
|
||||
* @param params.data.region - the AWS region
|
||||
* @param params.data.stringToSign - the stringToSign
|
||||
* @param params.data.scopeDate - the timespan to allow the request
|
||||
* @param params.data.authType - the type of authentication (query
|
||||
* @param {string} params.data.region - the AWS region
|
||||
* @param {string} params.data.stringToSign - the stringToSign
|
||||
* @param {string} params.data.scopeDate - the timespan to allow the request
|
||||
* @param {string} params.data.authType - the type of authentication (query
|
||||
* or header)
|
||||
* @param params.data.signatureVersion - the version of the
|
||||
* @param {string} params.data.signatureVersion - the version of the
|
||||
* signature (AWS or AWS4)
|
||||
* @param params.data.signatureAge - the age of the signature in ms
|
||||
* @param params.data.timestamp - signaure timestamp
|
||||
* @param params.credentialScope - credentialScope for signature
|
||||
* @param {number} params.data.signatureAge - the age of the signature in ms
|
||||
* @param {number} params.data.timestamp - signaure timestamp
|
||||
* @param {string} params.credentialScope - credentialScope for signature
|
||||
* @param {RequestContext [] | null} requestContexts -
|
||||
* an array of RequestContext or null if authenticaiton of a chunk
|
||||
* in streamingv4 auth
|
||||
* instances which contain information for policy authorization check
|
||||
* @param callback - callback with either error or user info
|
||||
* @param {function} callback - callback with either error or user info
|
||||
* @return {undefined}
|
||||
*/
|
||||
authenticateV4Request(
|
||||
params: {
|
||||
version: 4;
|
||||
log: Logger;
|
||||
data: {
|
||||
accessKey: string;
|
||||
signatureFromRequest: string;
|
||||
region: string;
|
||||
stringToSign: string;
|
||||
scopeDate: string;
|
||||
authType: 'query' | 'header';
|
||||
signatureVersion: string;
|
||||
signatureAge?: number;
|
||||
timestamp: number;
|
||||
credentialScope: string;
|
||||
securityToken: string;
|
||||
algo: string;
|
||||
log: Logger;
|
||||
};
|
||||
},
|
||||
requestContexts: any[],
|
||||
callback: (err: Error | null, data?: any) => void
|
||||
) {
|
||||
authenticateV4Request(params, requestContexts, callback) {
|
||||
params.log.debug('authenticating V4 request');
|
||||
let serializedRCs: any;
|
||||
let serializedRCs;
|
||||
if (requestContexts) {
|
||||
serializedRCs = requestContexts.map(rc => rc.serialize());
|
||||
}
|
||||
|
@ -190,39 +140,31 @@ export default class Vault {
|
|||
params.data.region,
|
||||
params.data.scopeDate,
|
||||
{
|
||||
// @ts-ignore
|
||||
reqUid: params.log.getSerializedUids(),
|
||||
logger: params.log,
|
||||
securityToken: params.data.securityToken,
|
||||
requestContext: serializedRCs,
|
||||
},
|
||||
(err: Error | null, userInfo?: any) => vaultSignatureCb(err, userInfo,
|
||||
params.log, callback, streamingV4Params),
|
||||
(err, userInfo) => vaultSignatureCb(err, userInfo,
|
||||
params.log, callback, streamingV4Params)
|
||||
);
|
||||
}
|
||||
|
||||
/** getCanonicalIds -- call Vault to get canonicalIDs based on email
|
||||
* addresses
|
||||
* @param emailAddresses - list of emailAddresses
|
||||
* @param log - log object
|
||||
* @param callback - callback with either error or an array
|
||||
* @param {array} emailAddresses - list of emailAddresses
|
||||
* @param {object} log - log object
|
||||
* @param {function} callback - callback with either error or an array
|
||||
* of objects with each object containing the canonicalID and emailAddress
|
||||
* of an account as properties
|
||||
* @return {undefined}
|
||||
*/
|
||||
getCanonicalIds(
|
||||
emailAddresses: string[],
|
||||
log: Logger,
|
||||
callback: (
|
||||
err: Error | null,
|
||||
data?: { canonicalID: string; email: string }[]
|
||||
) => void
|
||||
) {
|
||||
getCanonicalIds(emailAddresses, log, callback) {
|
||||
log.trace('getting canonicalIDs from Vault based on emailAddresses',
|
||||
{ emailAddresses });
|
||||
this.client.getCanonicalIds(emailAddresses,
|
||||
// @ts-ignore
|
||||
{ reqUid: log.getSerializedUids() },
|
||||
(err: Error | null, info?: any) => {
|
||||
(err, info) => {
|
||||
if (err) {
|
||||
log.debug('received error message from auth provider',
|
||||
{ errorMessage: err });
|
||||
|
@ -230,17 +172,17 @@ export default class Vault {
|
|||
}
|
||||
const infoFromVault = info.message.body;
|
||||
log.trace('info received from vault', { infoFromVault });
|
||||
const foundIds: { canonicalID: string; email: string }[] = [];
|
||||
const foundIds = [];
|
||||
for (let i = 0; i < Object.keys(infoFromVault).length; i++) {
|
||||
const key = Object.keys(infoFromVault)[i];
|
||||
if (infoFromVault[key] === 'WrongFormat'
|
||||
|| infoFromVault[key] === 'NotFound') {
|
||||
return callback(errors.UnresolvableGrantByEmailAddress);
|
||||
}
|
||||
foundIds.push({
|
||||
email: key,
|
||||
canonicalID: infoFromVault[key],
|
||||
})
|
||||
const obj = {};
|
||||
obj.email = key;
|
||||
obj.canonicalID = infoFromVault[key];
|
||||
foundIds.push(obj);
|
||||
}
|
||||
return callback(null, foundIds);
|
||||
});
|
||||
|
@ -248,22 +190,18 @@ export default class Vault {
|
|||
|
||||
/** getEmailAddresses -- call Vault to get email addresses based on
|
||||
* canonicalIDs
|
||||
* @param canonicalIDs - list of canonicalIDs
|
||||
* @param log - log object
|
||||
* @param callback - callback with either error or an object
|
||||
* @param {array} canonicalIDs - list of canonicalIDs
|
||||
* @param {object} log - log object
|
||||
* @param {function} callback - callback with either error or an object
|
||||
* with canonicalID keys and email address values
|
||||
* @return {undefined}
|
||||
*/
|
||||
getEmailAddresses(
|
||||
canonicalIDs: string[],
|
||||
log: Logger,
|
||||
callback: (err: Error | null, data?: { [key: string]: any }) => void
|
||||
) {
|
||||
getEmailAddresses(canonicalIDs, log, callback) {
|
||||
log.trace('getting emailAddresses from Vault based on canonicalIDs',
|
||||
{ canonicalIDs });
|
||||
this.client.getEmailAddresses(canonicalIDs,
|
||||
// @ts-ignore
|
||||
{ reqUid: log.getSerializedUids() },
|
||||
(err: Error | null, info?: any) => {
|
||||
(err, info) => {
|
||||
if (err) {
|
||||
log.debug('received error message from vault',
|
||||
{ errorMessage: err });
|
||||
|
@ -286,31 +224,27 @@ export default class Vault {
|
|||
|
||||
/** getAccountIds -- call Vault to get accountIds based on
|
||||
* canonicalIDs
|
||||
* @param canonicalIDs - list of canonicalIDs
|
||||
* @param log - log object
|
||||
* @param callback - callback with either error or an object
|
||||
* @param {array} canonicalIDs - list of canonicalIDs
|
||||
* @param {object} log - log object
|
||||
* @param {function} callback - callback with either error or an object
|
||||
* with canonicalID keys and accountId values
|
||||
* @return {undefined}
|
||||
*/
|
||||
getAccountIds(
|
||||
canonicalIDs: string[],
|
||||
log: Logger,
|
||||
callback: (err: Error | null, data?: { [key: string]: string }) => void
|
||||
) {
|
||||
getAccountIds(canonicalIDs, log, callback) {
|
||||
log.trace('getting accountIds from Vault based on canonicalIDs',
|
||||
{ canonicalIDs });
|
||||
this.client.getAccountIds(canonicalIDs,
|
||||
// @ts-expect-error
|
||||
{ reqUid: log.getSerializedUids() },
|
||||
(err: Error | null, info?: any) => {
|
||||
if (err) {
|
||||
log.debug('received error message from vault',
|
||||
{ errorMessage: err });
|
||||
return callback(err);
|
||||
}
|
||||
const infoFromVault = info.message.body;
|
||||
log.trace('info received from vault', { infoFromVault });
|
||||
const result = {};
|
||||
/* If the accountId was not found in Vault, do not
|
||||
{ reqUid: log.getSerializedUids() },
|
||||
(err, info) => {
|
||||
if (err) {
|
||||
log.debug('received error message from vault',
|
||||
{ errorMessage: err });
|
||||
return callback(err);
|
||||
}
|
||||
const infoFromVault = info.message.body;
|
||||
log.trace('info received from vault', { infoFromVault });
|
||||
const result = {};
|
||||
/* If the accountId was not found in Vault, do not
|
||||
send the canonicalID back to the API */
|
||||
Object.keys(infoFromVault).forEach(key => {
|
||||
if (infoFromVault[key] !== 'NotFound' &&
|
||||
|
@ -334,19 +268,14 @@ export default class Vault {
|
|||
* @param {object} log - log object
|
||||
* @param {function} callback - callback with either error or an array
|
||||
* of authorization results
|
||||
* @return {undefined}
|
||||
*/
|
||||
checkPolicies(
|
||||
requestContextParams: any[],
|
||||
userArn: string,
|
||||
log: Logger,
|
||||
callback: (err: Error | null, data?: any[]) => void
|
||||
) {
|
||||
checkPolicies(requestContextParams, userArn, log, callback) {
|
||||
log.trace('sending request context params to vault to evaluate' +
|
||||
'policies');
|
||||
this.client.checkPolicies(requestContextParams, userArn, {
|
||||
// @ts-ignore
|
||||
reqUid: log.getSerializedUids(),
|
||||
}, (err: Error | null, info?: any) => {
|
||||
}, (err, info) => {
|
||||
if (err) {
|
||||
log.debug('received error message from auth provider',
|
||||
{ error: err });
|
||||
|
@ -357,14 +286,13 @@ export default class Vault {
|
|||
});
|
||||
}
|
||||
|
||||
checkHealth(log: Logger, callback: (err: Error | null, data?: any) => void) {
|
||||
checkHealth(log, callback) {
|
||||
if (!this.client.healthcheck) {
|
||||
const defResp = {};
|
||||
defResp[this.implName] = { code: 200, message: 'OK' };
|
||||
return callback(null, defResp);
|
||||
}
|
||||
// @ts-ignore
|
||||
return this.client.healthcheck(log.getSerializedUids(), (err: Error | null, obj?: any) => {
|
||||
return this.client.healthcheck(log.getSerializedUids(), (err, obj) => {
|
||||
const respBody = {};
|
||||
if (err) {
|
||||
log.debug(`error from ${this.implName}`, { error: err });
|
||||
|
@ -384,3 +312,5 @@ export default class Vault {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Vault;
|
||||
|
|
163
lib/auth/auth.ts
163
lib/auth/auth.ts
|
@ -1,21 +1,22 @@
|
|||
import * as crypto from 'crypto';
|
||||
import { Logger } from 'werelogs';
|
||||
import errors from '../errors';
|
||||
import * as queryString from 'querystring';
|
||||
import AuthInfo from './AuthInfo';
|
||||
import * as v2 from './v2/authV2';
|
||||
import * as v4 from './v4/authV4';
|
||||
import * as constants from '../constants';
|
||||
import constructStringToSignV2 from './v2/constructStringToSign';
|
||||
import constructStringToSignV4 from './v4/constructStringToSign';
|
||||
import { convertUTCtoISO8601 } from './v4/timeUtils';
|
||||
import * as vaultUtilities from './in_memory/vaultUtilities';
|
||||
import * as backend from './in_memory/Backend';
|
||||
import validateAuthConfig from './in_memory/validateAuthConfig';
|
||||
import AuthLoader from './in_memory/AuthLoader';
|
||||
import Vault from './Vault';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
let vault: Vault | null = null;
|
||||
const crypto = require('crypto');
|
||||
const errors = require('../errors');
|
||||
const queryString = require('querystring');
|
||||
const AuthInfo = require('./AuthInfo');
|
||||
const v2 = require('./v2/authV2');
|
||||
const v4 = require('./v4/authV4');
|
||||
const constants = require('../constants');
|
||||
const constructStringToSignV2 = require('./v2/constructStringToSign');
|
||||
const constructStringToSignV4 = require('./v4/constructStringToSign');
|
||||
const convertUTCtoISO8601 = require('./v4/timeUtils').convertUTCtoISO8601;
|
||||
const vaultUtilities = require('./in_memory/vaultUtilities');
|
||||
const backend = require('./in_memory/Backend');
|
||||
const validateAuthConfig = require('./in_memory/validateAuthConfig');
|
||||
const AuthLoader = require('./in_memory/AuthLoader');
|
||||
const Vault = require('./Vault');
|
||||
|
||||
let vault = null;
|
||||
const auth = {};
|
||||
const checkFunctions = {
|
||||
v2: {
|
||||
|
@ -32,7 +33,7 @@ const checkFunctions = {
|
|||
// 'All Users Group' so use this group as the canonicalID for the publicUser
|
||||
const publicUserInfo = new AuthInfo({ canonicalID: constants.publicId });
|
||||
|
||||
function setAuthHandler(handler: Vault) {
|
||||
function setAuthHandler(handler) {
|
||||
vault = handler;
|
||||
return auth;
|
||||
}
|
||||
|
@ -40,30 +41,25 @@ function setAuthHandler(handler: Vault) {
|
|||
/**
|
||||
* This function will check validity of request parameters to authenticate
|
||||
*
|
||||
* @param request - Http request object
|
||||
* @param log - Logger object
|
||||
* @param awsService - Aws service related
|
||||
* @param data - Parameters from queryString parsing or body of
|
||||
* @param {Http.Request} request - Http request object
|
||||
* @param {object} log - Logger object
|
||||
* @param {string} awsService - Aws service related
|
||||
* @param {object} data - Parameters from queryString parsing or body of
|
||||
* POST request
|
||||
*
|
||||
* @return ret
|
||||
* @return ret.err - arsenal.errors object if any error was found
|
||||
* @return ret.params - auth parameters to use later on for signature
|
||||
* @return {object} ret
|
||||
* @return {object} ret.err - arsenal.errors object if any error was found
|
||||
* @return {object} ret.params - auth parameters to use later on for signature
|
||||
* computation and check
|
||||
* @return ret.params.version - the auth scheme version
|
||||
* @return {object} ret.params.version - the auth scheme version
|
||||
* (undefined, 2, 4)
|
||||
* @return ret.params.data - the auth scheme's specific data
|
||||
* @return {object} ret.params.data - the auth scheme's specific data
|
||||
*/
|
||||
function extractParams(
|
||||
request: any,
|
||||
log: Logger,
|
||||
awsService: string,
|
||||
data: { [key: string]: string }
|
||||
) {
|
||||
function extractParams(request, log, awsService, data) {
|
||||
log.trace('entered', { method: 'Arsenal.auth.server.extractParams' });
|
||||
const authHeader = request.headers.authorization;
|
||||
let version: 'v2' |'v4' | null = null;
|
||||
let method: 'query' | 'headers' | null = null;
|
||||
let version = null;
|
||||
let method = null;
|
||||
|
||||
// Identify auth version and method to dispatch to the right check function
|
||||
if (authHeader) {
|
||||
|
@ -106,21 +102,16 @@ function extractParams(
|
|||
/**
|
||||
* This function will check validity of request parameters to authenticate
|
||||
*
|
||||
* @param request - Http request object
|
||||
* @param log - Logger object
|
||||
* @param cb - the callback
|
||||
* @param awsService - Aws service related
|
||||
* @param {Http.Request} request - Http request object
|
||||
* @param {object} log - Logger object
|
||||
* @param {function} cb - the callback
|
||||
* @param {string} awsService - Aws service related
|
||||
* @param {RequestContext[] | null} requestContexts - array of RequestContext
|
||||
* or null if no requestContexts to be sent to Vault (for instance,
|
||||
* in multi-object delete request)
|
||||
* @return {undefined}
|
||||
*/
|
||||
function doAuth(
|
||||
request: any,
|
||||
log: Logger,
|
||||
cb: (err: Error | null, data?: any) => void,
|
||||
awsService: string,
|
||||
requestContexts: any[] | null
|
||||
) {
|
||||
function doAuth(request, log, cb, awsService, requestContexts) {
|
||||
const res = extractParams(request, log, awsService, request.query);
|
||||
if (res.err) {
|
||||
return cb(res.err);
|
||||
|
@ -128,31 +119,23 @@ function doAuth(
|
|||
return cb(null, res.params);
|
||||
}
|
||||
if (requestContexts) {
|
||||
requestContexts.forEach((requestContext) => {
|
||||
const { params } = res
|
||||
if ('data' in params) {
|
||||
const { data } = params
|
||||
requestContext.setAuthType(data.authType);
|
||||
requestContext.setSignatureVersion(data.signatureVersion);
|
||||
requestContext.setSecurityToken(data.securityToken);
|
||||
if ('signatureAge' in data) {
|
||||
requestContext.setSignatureAge(data.signatureAge);
|
||||
}
|
||||
}
|
||||
requestContexts.forEach(requestContext => {
|
||||
requestContext.setAuthType(res.params.data.authType);
|
||||
requestContext.setSignatureVersion(res.params
|
||||
.data.signatureVersion);
|
||||
requestContext.setSignatureAge(res.params.data.signatureAge);
|
||||
requestContext.setSecurityToken(res.params.data.securityToken);
|
||||
});
|
||||
}
|
||||
|
||||
// Corner cases managed, we're left with normal auth
|
||||
// TODO What's happening here?
|
||||
// @ts-ignore
|
||||
res.params.log = log;
|
||||
if (res.params.version === 2) {
|
||||
// @ts-ignore
|
||||
return vault!.authenticateV2Request(res.params, requestContexts, cb);
|
||||
return vault.authenticateV2Request(res.params, requestContexts, cb);
|
||||
}
|
||||
if (res.params.version === 4) {
|
||||
// @ts-ignore
|
||||
return vault!.authenticateV4Request(res.params, requestContexts, cb);
|
||||
return vault.authenticateV4Request(res.params, requestContexts, cb,
|
||||
awsService);
|
||||
}
|
||||
|
||||
log.error('authentication method not found', {
|
||||
|
@ -164,25 +147,19 @@ function doAuth(
|
|||
/**
|
||||
* This function will generate a version 4 header
|
||||
*
|
||||
* @param request - Http request object
|
||||
* @param data - Parameters from queryString parsing or body of
|
||||
* @param {Http.Request} request - Http request object
|
||||
* @param {object} data - Parameters from queryString parsing or body of
|
||||
* POST request
|
||||
* @param accessKey - the accessKey
|
||||
* @param secretKeyValue - the secretKey
|
||||
* @param awsService - Aws service related
|
||||
* @param [proxyPath] - path that gets proxied by reverse proxy
|
||||
* @param [sessionToken] - security token if the access/secret keys
|
||||
* @param {string} accessKey - the accessKey
|
||||
* @param {string} secretKeyValue - the secretKey
|
||||
* @param {string} awsService - Aws service related
|
||||
* @param {sting} [proxyPath] - path that gets proxied by reverse proxy
|
||||
* @param {string} [sessionToken] - security token if the access/secret keys
|
||||
* are temporary credentials from STS
|
||||
* @return {undefined}
|
||||
*/
|
||||
function generateV4Headers(
|
||||
request: any,
|
||||
data: { [key: string]: string },
|
||||
accessKey: string,
|
||||
secretKeyValue: string,
|
||||
awsService: string,
|
||||
proxyPath: string,
|
||||
sessionToken: string
|
||||
) {
|
||||
function generateV4Headers(request, data, accessKey, secretKeyValue,
|
||||
awsService, proxyPath, sessionToken) {
|
||||
Object.assign(request, { headers: {} });
|
||||
const amzDate = convertUTCtoISO8601(Date.now());
|
||||
// get date without time
|
||||
|
@ -196,7 +173,7 @@ function generateV4Headers(
|
|||
|
||||
let payload = '';
|
||||
if (request.method === 'POST') {
|
||||
payload = queryString.stringify(data, undefined, undefined, {
|
||||
payload = queryString.stringify(data, null, null, {
|
||||
encodeURIComponent,
|
||||
});
|
||||
}
|
||||
|
@ -226,7 +203,7 @@ function generateV4Headers(
|
|||
scopeDate,
|
||||
service);
|
||||
const signature = crypto.createHmac('sha256', signingKey)
|
||||
.update(stringToSign as string, 'binary').digest('hex');
|
||||
.update(stringToSign, 'binary').digest('hex');
|
||||
const authorizationHeader = `${algorithm} Credential=${accessKey}` +
|
||||
`/${credentialScope}, SignedHeaders=${signedHeaders}, ` +
|
||||
`Signature=${signature}`;
|
||||
|
@ -234,11 +211,21 @@ function generateV4Headers(
|
|||
Object.assign(request, { headers: {} });
|
||||
}
|
||||
|
||||
export const server = { extractParams, doAuth }
|
||||
export const client = { generateV4Headers, constructStringToSignV2 }
|
||||
export const inMemory = { backend, validateAuthConfig, AuthLoader }
|
||||
export {
|
||||
setAuthHandler as setHandler,
|
||||
module.exports = {
|
||||
setHandler: setAuthHandler,
|
||||
server: {
|
||||
extractParams,
|
||||
doAuth,
|
||||
},
|
||||
client: {
|
||||
generateV4Headers,
|
||||
constructStringToSignV2,
|
||||
},
|
||||
inMemory: {
|
||||
backend,
|
||||
validateAuthConfig,
|
||||
AuthLoader,
|
||||
},
|
||||
AuthInfo,
|
||||
Vault
|
||||
}
|
||||
Vault,
|
||||
};
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import * as crypto from 'crypto';
|
||||
import errors from '../../errors';
|
||||
import { calculateSigningKey, hashSignature } from './vaultUtilities';
|
||||
import Indexer from './Indexer';
|
||||
import { Accounts } from './types';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
function _formatResponse(userInfoToSend: any) {
|
||||
const crypto = require('crypto');
|
||||
|
||||
const errors = require('../../errors');
|
||||
const calculateSigningKey = require('./vaultUtilities').calculateSigningKey;
|
||||
const hashSignature = require('./vaultUtilities').hashSignature;
|
||||
const Indexer = require('./Indexer');
|
||||
|
||||
function _formatResponse(userInfoToSend) {
|
||||
return {
|
||||
message: {
|
||||
body: { userInfo: userInfoToSend },
|
||||
|
@ -15,27 +18,33 @@ function _formatResponse(userInfoToSend: any) {
|
|||
/**
|
||||
* Class that provides a memory backend for verifying signatures and getting
|
||||
* emails and canonical ids associated with an account.
|
||||
*
|
||||
* @class Backend
|
||||
*/
|
||||
class Backend {
|
||||
indexer: Indexer;
|
||||
service: string;
|
||||
|
||||
constructor(service: string, indexer: Indexer) {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {string} service - service identifer for construction arn
|
||||
* @param {Indexer} indexer - indexer instance for retrieving account info
|
||||
* @param {function} formatter - function which accepts user info to send
|
||||
* back and returns it in an object
|
||||
*/
|
||||
constructor(service, indexer, formatter) {
|
||||
this.service = service;
|
||||
this.indexer = indexer;
|
||||
this.formatResponse = formatter;
|
||||
}
|
||||
|
||||
// CODEQUALITY-TODO-SYNC Should be synchronous
|
||||
verifySignatureV2(
|
||||
stringToSign: string,
|
||||
signatureFromRequest: string,
|
||||
accessKey: string,
|
||||
options: { algo: 'SHA256' | 'SHA1' },
|
||||
callback: (
|
||||
error: Error | null,
|
||||
data?: ReturnType<typeof _formatResponse>
|
||||
) => void
|
||||
) {
|
||||
/** verifySignatureV2
|
||||
* @param {string} stringToSign - string to sign built per AWS rules
|
||||
* @param {string} signatureFromRequest - signature sent with request
|
||||
* @param {string} accessKey - account accessKey
|
||||
* @param {object} options - contains algorithm (SHA1 or SHA256)
|
||||
* @param {function} callback - callback with either error or user info
|
||||
* @return {function} calls callback
|
||||
*/
|
||||
verifySignatureV2(stringToSign, signatureFromRequest,
|
||||
accessKey, options, callback) {
|
||||
const entity = this.indexer.getEntityByKey(accessKey);
|
||||
if (!entity) {
|
||||
return callback(errors.InvalidAccessKeyId);
|
||||
|
@ -50,28 +59,26 @@ class Backend {
|
|||
accountDisplayName: this.indexer.getAcctDisplayName(entity),
|
||||
canonicalID: entity.canonicalID,
|
||||
arn: entity.arn,
|
||||
// TODO Why?
|
||||
// @ts-ignore
|
||||
IAMdisplayName: entity.IAMdisplayName,
|
||||
};
|
||||
const vaultReturnObject = _formatResponse(userInfoToSend);
|
||||
const vaultReturnObject = this.formatResponse(userInfoToSend);
|
||||
return callback(null, vaultReturnObject);
|
||||
}
|
||||
|
||||
// TODO Options not used. Why ?
|
||||
// CODEQUALITY-TODO-SYNC Should be synchronous
|
||||
verifySignatureV4(
|
||||
stringToSign: string,
|
||||
signatureFromRequest: string,
|
||||
accessKey: string,
|
||||
region: string,
|
||||
scopeDate: string,
|
||||
_options: { algo: 'SHA256' | 'SHA1' },
|
||||
callback: (
|
||||
err: Error | null,
|
||||
data?: ReturnType<typeof _formatResponse>
|
||||
) => void
|
||||
) {
|
||||
|
||||
/** verifySignatureV4
|
||||
* @param {string} stringToSign - string to sign built per AWS rules
|
||||
* @param {string} signatureFromRequest - signature sent with request
|
||||
* @param {string} accessKey - account accessKey
|
||||
* @param {string} region - region specified in request credential
|
||||
* @param {string} scopeDate - date specified in request credential
|
||||
* @param {object} options - options to send to Vault
|
||||
* (just contains reqUid for logging in Vault)
|
||||
* @param {function} callback - callback with either error or user info
|
||||
* @return {function} calls callback
|
||||
*/
|
||||
verifySignatureV4(stringToSign, signatureFromRequest, accessKey,
|
||||
region, scopeDate, options, callback) {
|
||||
const entity = this.indexer.getEntityByKey(accessKey);
|
||||
if (!entity) {
|
||||
return callback(errors.InvalidAccessKeyId);
|
||||
|
@ -87,21 +94,23 @@ class Backend {
|
|||
accountDisplayName: this.indexer.getAcctDisplayName(entity),
|
||||
canonicalID: entity.canonicalID,
|
||||
arn: entity.arn,
|
||||
// TODO Why?
|
||||
// @ts-ignore
|
||||
IAMdisplayName: entity.IAMdisplayName,
|
||||
};
|
||||
const vaultReturnObject = _formatResponse(userInfoToSend);
|
||||
const vaultReturnObject = this.formatResponse(userInfoToSend);
|
||||
return callback(null, vaultReturnObject);
|
||||
}
|
||||
|
||||
// TODO log not used. Why ?
|
||||
// CODEQUALITY-TODO-SYNC Should be synchronous
|
||||
getCanonicalIds(
|
||||
emails: string[],
|
||||
_log: any,
|
||||
cb: (err: null, data: { message: { body: any } }) => void
|
||||
) {
|
||||
/**
|
||||
* Gets canonical ID's for a list of accounts
|
||||
* based on email associated with account
|
||||
* @param {array} emails - list of email addresses
|
||||
* @param {object} log - log object
|
||||
* @param {function} cb - callback to calling function
|
||||
* @returns {function} callback with either error or
|
||||
* object with email addresses as keys and canonical IDs
|
||||
* as values
|
||||
*/
|
||||
getCanonicalIds(emails, log, cb) {
|
||||
const results = {};
|
||||
emails.forEach(email => {
|
||||
const lowercasedEmail = email.toLowerCase();
|
||||
|
@ -121,13 +130,17 @@ class Backend {
|
|||
return cb(null, vaultReturnObject);
|
||||
}
|
||||
|
||||
// TODO options not used. Why ?
|
||||
// CODEQUALITY-TODO-SYNC Should be synchronous
|
||||
getEmailAddresses(
|
||||
canonicalIDs: string[],
|
||||
_options: any,
|
||||
cb: (err: null, data: { message: { body: any } }) => void
|
||||
) {
|
||||
/**
|
||||
* Gets email addresses (referred to as diplay names for getACL's)
|
||||
* for a list of accounts based on canonical IDs associated with account
|
||||
* @param {array} canonicalIDs - list of canonicalIDs
|
||||
* @param {object} options - to send log id to vault
|
||||
* @param {function} cb - callback to calling function
|
||||
* @returns {function} callback with either error or
|
||||
* an object from Vault containing account canonicalID
|
||||
* as each object key and an email address as the value (or "NotFound")
|
||||
*/
|
||||
getEmailAddresses(canonicalIDs, options, cb) {
|
||||
const results = {};
|
||||
canonicalIDs.forEach(canonicalId => {
|
||||
const foundEntity = this.indexer.getEntityByCanId(canonicalId);
|
||||
|
@ -145,24 +158,17 @@ class Backend {
|
|||
return cb(null, vaultReturnObject);
|
||||
}
|
||||
|
||||
// TODO options not used. Why ?
|
||||
// CODEQUALITY-TODO-SYNC Should be synchronous
|
||||
/**
|
||||
* Gets accountIds for a list of accounts based on
|
||||
* the canonical IDs associated with the account
|
||||
* @param canonicalIDs - list of canonicalIDs
|
||||
* @param _options - to send log id to vault
|
||||
* @param cb - callback to calling function
|
||||
* @returns The next is wrong. Here to keep archives.
|
||||
* callback with either error or
|
||||
* @param {array} canonicalIDs - list of canonicalIDs
|
||||
* @param {object} options - to send log id to vault
|
||||
* @param {function} cb - callback to calling function
|
||||
* @returns {function} callback with either error or
|
||||
* an object from Vault containing account canonicalID
|
||||
* as each object key and an accountId as the value (or "NotFound")
|
||||
*/
|
||||
getAccountIds(
|
||||
canonicalIDs: string[],
|
||||
_options: any,
|
||||
cb: (err: null, data: { message: { body: any } }) => void
|
||||
) {
|
||||
getAccountIds(canonicalIDs, options, cb) {
|
||||
const results = {};
|
||||
canonicalIDs.forEach(canonicalID => {
|
||||
const foundEntity = this.indexer.getEntityByCanId(canonicalID);
|
||||
|
@ -181,14 +187,31 @@ class Backend {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
class S3AuthBackend extends Backend {
|
||||
constructor(authdata: Accounts) {
|
||||
super('s3', new Indexer(authdata));
|
||||
/**
|
||||
* @constructor
|
||||
* @param {object} authdata - the authentication config file's data
|
||||
* @param {object[]} authdata.accounts - array of account objects
|
||||
* @param {string=} authdata.accounts[].name - account name
|
||||
* @param {string} authdata.accounts[].email - account email
|
||||
* @param {string} authdata.accounts[].arn - IAM resource name
|
||||
* @param {string} authdata.accounts[].canonicalID - account canonical ID
|
||||
* @param {string} authdata.accounts[].shortid - short account ID
|
||||
* @param {object[]=} authdata.accounts[].keys - array of key objects
|
||||
* @param {string} authdata.accounts[].keys[].access - access key
|
||||
* @param {string} authdata.accounts[].keys[].secret - secret key
|
||||
* @return {undefined}
|
||||
*/
|
||||
constructor(authdata) {
|
||||
super('s3', new Indexer(authdata), _formatResponse);
|
||||
}
|
||||
|
||||
refreshAuthData(authData: Accounts) {
|
||||
refreshAuthData(authData) {
|
||||
this.indexer = new Indexer(authData);
|
||||
}
|
||||
}
|
||||
|
||||
export { S3AuthBackend as s3 };
|
||||
module.exports = {
|
||||
s3: S3AuthBackend,
|
||||
};
|
||||
|
|
|
@ -1,19 +1,27 @@
|
|||
import { Accounts, Account, Entity } from './types';
|
||||
|
||||
/**
|
||||
* Class that provides an internal indexing over the simple data provided by
|
||||
* the authentication configuration file for the memory backend. This allows
|
||||
* accessing the different authentication entities through various types of
|
||||
* keys.
|
||||
*
|
||||
* @class Indexer
|
||||
*/
|
||||
export default class Indexer {
|
||||
accountsBy: {
|
||||
canId: { [id: string]: Entity | undefined },
|
||||
accessKey: { [id: string]: Entity | undefined },
|
||||
email: { [id: string]: Entity | undefined },
|
||||
}
|
||||
|
||||
constructor(authdata?: Accounts) {
|
||||
class Indexer {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {object} authdata - the authentication config file's data
|
||||
* @param {object[]} authdata.accounts - array of account objects
|
||||
* @param {string=} authdata.accounts[].name - account name
|
||||
* @param {string} authdata.accounts[].email - account email
|
||||
* @param {string} authdata.accounts[].arn - IAM resource name
|
||||
* @param {string} authdata.accounts[].canonicalID - account canonical ID
|
||||
* @param {string} authdata.accounts[].shortid - short account ID
|
||||
* @param {object[]=} authdata.accounts[].keys - array of key objects
|
||||
* @param {string} authdata.accounts[].keys[].access - access key
|
||||
* @param {string} authdata.accounts[].keys[].secret - secret key
|
||||
* @return {undefined}
|
||||
*/
|
||||
constructor(authdata) {
|
||||
this.accountsBy = {
|
||||
canId: {},
|
||||
accessKey: {},
|
||||
|
@ -29,11 +37,11 @@ export default class Indexer {
|
|||
return;
|
||||
}
|
||||
|
||||
this.#build(authdata);
|
||||
this._build(authdata);
|
||||
}
|
||||
|
||||
#indexAccount(account: Account) {
|
||||
const accountData: Entity = {
|
||||
_indexAccount(account) {
|
||||
const accountData = {
|
||||
arn: account.arn,
|
||||
canonicalID: account.canonicalID,
|
||||
shortid: account.shortid,
|
||||
|
@ -51,43 +59,87 @@ export default class Indexer {
|
|||
}
|
||||
}
|
||||
|
||||
#build(authdata: Accounts) {
|
||||
_build(authdata) {
|
||||
authdata.accounts.forEach(account => {
|
||||
this.#indexAccount(account);
|
||||
this._indexAccount(account);
|
||||
});
|
||||
}
|
||||
|
||||
/** This method returns the account associated to a canonical ID. */
|
||||
getEntityByCanId(canId: string): Entity | undefined {
|
||||
/**
|
||||
* This method returns the account associated to a canonical ID.
|
||||
*
|
||||
* @param {string} canId - The canonicalId of the account
|
||||
* @return {Object} account - The account object
|
||||
* @return {Object} account.arn - The account's ARN
|
||||
* @return {Object} account.canonicalID - The account's canonical ID
|
||||
* @return {Object} account.shortid - The account's internal shortid
|
||||
* @return {Object} account.accountDisplayName - The account's display name
|
||||
* @return {Object} account.email - The account's lowercased email
|
||||
*/
|
||||
getEntityByCanId(canId) {
|
||||
return this.accountsBy.canId[canId];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the entity (either an account or a user) associated
|
||||
* to a canonical ID.
|
||||
*
|
||||
* @param {string} key - The accessKey of the entity
|
||||
* @return {Object} entity - The entity object
|
||||
* @return {Object} entity.arn - The entity's ARN
|
||||
* @return {Object} entity.canonicalID - The canonical ID for the entity's
|
||||
* account
|
||||
* @return {Object} entity.shortid - The entity's internal shortid
|
||||
* @return {Object} entity.accountDisplayName - The entity's account
|
||||
* display name
|
||||
* @return {Object} entity.IAMDisplayName - The user's display name
|
||||
* (if the entity is an user)
|
||||
* @return {Object} entity.email - The entity's lowercased email
|
||||
*/
|
||||
getEntityByKey(key: string): Entity | undefined {
|
||||
getEntityByKey(key) {
|
||||
return this.accountsBy.accessKey[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the entity (either an account or a user) associated
|
||||
* to an email address.
|
||||
*
|
||||
* @param {string} email - The email address
|
||||
* @return {Object} entity - The entity object
|
||||
* @return {Object} entity.arn - The entity's ARN
|
||||
* @return {Object} entity.canonicalID - The canonical ID for the entity's
|
||||
* account
|
||||
* @return {Object} entity.shortid - The entity's internal shortid
|
||||
* @return {Object} entity.accountDisplayName - The entity's account
|
||||
* display name
|
||||
* @return {Object} entity.IAMDisplayName - The user's display name
|
||||
* (if the entity is an user)
|
||||
* @return {Object} entity.email - The entity's lowercased email
|
||||
*/
|
||||
getEntityByEmail(email: string): Entity | undefined {
|
||||
getEntityByEmail(email) {
|
||||
const lowerCasedEmail = email.toLowerCase();
|
||||
return this.accountsBy.email[lowerCasedEmail];
|
||||
}
|
||||
|
||||
/** This method returns the secret key associated with the entity. */
|
||||
getSecretKey(entity: Entity, accessKey: string) {
|
||||
const keys = entity.keys.filter(kv => kv.access === accessKey);
|
||||
return keys[0].secret;
|
||||
/**
|
||||
* This method returns the secret key associated with the entity.
|
||||
* @param {Object} entity - the entity object
|
||||
* @param {string} accessKey - access key
|
||||
* @returns {string} secret key
|
||||
*/
|
||||
getSecretKey(entity, accessKey) {
|
||||
return entity.keys
|
||||
.filter(kv => kv.access === accessKey)[0].secret;
|
||||
}
|
||||
|
||||
/** This method returns the account display name associated with the entity. */
|
||||
getAcctDisplayName(entity: Entity) {
|
||||
/**
|
||||
* This method returns the account display name associated with the entity.
|
||||
* @param {Object} entity - the entity object
|
||||
* @returns {string} account display name
|
||||
*/
|
||||
getAcctDisplayName(entity) {
|
||||
return entity.accountDisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Indexer;
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import { Logger } from 'werelogs';
|
||||
import AuthLoader from './AuthLoader';
|
||||
import { Accounts } from './types';
|
||||
const AuthLoader = require('./AuthLoader');
|
||||
|
||||
/**
|
||||
* @deprecated please use {@link AuthLoader} class instead
|
||||
* @return true on erroneous data false on success
|
||||
*
|
||||
* @param {object} authdata - the authentication config file's data
|
||||
* @param {werelogs.API} logApi - object providing a constructor function
|
||||
* for the Logger object
|
||||
* @return {boolean} true on erroneous data
|
||||
* false on success
|
||||
*/
|
||||
export default function validateAuthConfig(
|
||||
authdata: Accounts,
|
||||
logApi?: { Logger: typeof Logger }
|
||||
) {
|
||||
function validateAuthConfig(authdata, logApi) {
|
||||
const authLoader = new AuthLoader(logApi);
|
||||
authLoader.addAccounts(authdata);
|
||||
return !authLoader.validate();
|
||||
}
|
||||
|
||||
module.exports = validateAuthConfig;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
export default function algoCheck(signatureLength: number) {
|
||||
let algo: 'sha256' | 'sha1';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
function algoCheck(signatureLength) {
|
||||
let algo;
|
||||
// If the signature sent is 44 characters,
|
||||
// this means that sha256 was used:
|
||||
// 44 characters in base64
|
||||
|
@ -11,6 +13,7 @@ export default function algoCheck(signatureLength: number) {
|
|||
if (signatureLength === SHA1LEN) {
|
||||
algo = 'sha1';
|
||||
}
|
||||
// @ts-ignore
|
||||
return algo;
|
||||
}
|
||||
|
||||
module.exports = algoCheck;
|
||||
|
|
|
@ -1,2 +1,11 @@
|
|||
export * as header from './headerAuthCheck';
|
||||
export * as query from './queryAuthCheck';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const headerAuthCheck = require('./headerAuthCheck');
|
||||
const queryAuthCheck = require('./queryAuthCheck');
|
||||
|
||||
const authV2 = {
|
||||
header: headerAuthCheck,
|
||||
query: queryAuthCheck,
|
||||
};
|
||||
|
||||
module.exports = authV2;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Logger } from 'werelogs';
|
||||
import errors from '../../errors';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
const errors = require('../../errors');
|
||||
|
||||
const epochTime = new Date('1970-01-01').getTime();
|
||||
|
||||
export default function checkRequestExpiry(timestamp: number, log: Logger) {
|
||||
function checkRequestExpiry(timestamp, log) {
|
||||
// If timestamp is before epochTime, the request is invalid and return
|
||||
// errors.AccessDenied
|
||||
if (timestamp < epochTime) {
|
||||
|
@ -32,3 +32,5 @@ export default function checkRequestExpiry(timestamp: number, log: Logger) {
|
|||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
module.exports = checkRequestExpiry;
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
import { Logger } from 'werelogs';
|
||||
import utf8 from 'utf8';
|
||||
import getCanonicalizedAmzHeaders from './getCanonicalizedAmzHeaders';
|
||||
import getCanonicalizedResource from './getCanonicalizedResource';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
export default function constructStringToSign(
|
||||
request: any,
|
||||
data: { [key: string]: string },
|
||||
log: Logger,
|
||||
clientType?: any
|
||||
) {
|
||||
const utf8 = require('utf8');
|
||||
|
||||
const getCanonicalizedAmzHeaders = require('./getCanonicalizedAmzHeaders');
|
||||
const getCanonicalizedResource = require('./getCanonicalizedResource');
|
||||
|
||||
function constructStringToSign(request, data, log, clientType) {
|
||||
/*
|
||||
Build signature per AWS requirements:
|
||||
StringToSign = HTTP-Verb + '\n' +
|
||||
|
@ -45,3 +42,5 @@ export default function constructStringToSign(
|
|||
+ getCanonicalizedResource(request, clientType);
|
||||
return utf8.encode(stringToSign);
|
||||
}
|
||||
|
||||
module.exports = constructStringToSign;
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
export default function getCanonicalizedAmzHeaders(headers: Headers, clientType: string) {
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
function getCanonicalizedAmzHeaders(headers, clientType) {
|
||||
/*
|
||||
Iterate through headers and pull any headers that are x-amz headers.
|
||||
Need to include 'x-amz-date' here even though AWS docs
|
||||
ambiguous on this.
|
||||
*/
|
||||
const filterFn = clientType === 'GCP' ?
|
||||
(val: string) => val.substr(0, 7) === 'x-goog-' :
|
||||
(val: string) => val.substr(0, 6) === 'x-amz-';
|
||||
val => val.substr(0, 7) === 'x-goog-' :
|
||||
val => val.substr(0, 6) === 'x-amz-';
|
||||
const amzHeaders = Object.keys(headers)
|
||||
.filter(filterFn)
|
||||
.map(val => [val.trim(), headers[val].trim()]);
|
||||
|
@ -41,3 +43,5 @@ export default function getCanonicalizedAmzHeaders(headers: Headers, clientType:
|
|||
`${headerStr}${current[0]}:${current[1]}\n`,
|
||||
'');
|
||||
}
|
||||
|
||||
module.exports = getCanonicalizedAmzHeaders;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import * as url from 'url';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const url = require('url');
|
||||
|
||||
const gcpSubresources = [
|
||||
'acl',
|
||||
|
@ -39,7 +41,7 @@ const awsSubresources = [
|
|||
'website',
|
||||
];
|
||||
|
||||
export default function getCanonicalizedResource(request: any, clientType: string) {
|
||||
function getCanonicalizedResource(request, clientType) {
|
||||
/*
|
||||
This variable is used to determine whether to insert
|
||||
a '?' or '&'. Once a query parameter is added to the resourceString,
|
||||
|
@ -115,3 +117,5 @@ export default function getCanonicalizedResource(request: any, clientType: strin
|
|||
}
|
||||
return resourceString;
|
||||
}
|
||||
|
||||
module.exports = getCanonicalizedResource;
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { Logger } from 'werelogs';
|
||||
import errors from '../../errors';
|
||||
import * as constants from '../../constants';
|
||||
import constructStringToSign from './constructStringToSign';
|
||||
import checkRequestExpiry from './checkRequestExpiry';
|
||||
import algoCheck from './algoCheck';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
export function check(request: any, log: Logger, data: { [key: string]: string }) {
|
||||
const errors = require('../../errors');
|
||||
const constants = require('../../constants');
|
||||
const constructStringToSign = require('./constructStringToSign');
|
||||
const checkRequestExpiry = require('./checkRequestExpiry');
|
||||
const algoCheck = require('./algoCheck');
|
||||
|
||||
function check(request, log, data) {
|
||||
log.trace('running header auth check');
|
||||
const headers = request.headers;
|
||||
|
||||
|
@ -51,7 +52,6 @@ export function check(request: any, log: Logger, data: { [key: string]: string }
|
|||
log.trace('invalid authorization header', { authInfo });
|
||||
return { err: errors.MissingSecurityHeader };
|
||||
}
|
||||
// @ts-ignore
|
||||
log.addDefaultFields({ accessKey });
|
||||
|
||||
const signatureFromRequest = authInfo.substring(semicolonIndex + 1).trim();
|
||||
|
@ -80,3 +80,5 @@ export function check(request: any, log: Logger, data: { [key: string]: string }
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { check };
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { Logger } from 'werelogs';
|
||||
import errors from '../../errors';
|
||||
import * as constants from '../../constants';
|
||||
import algoCheck from './algoCheck';
|
||||
import constructStringToSign from './constructStringToSign';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
export function check(request: any, log: Logger, data: { [key: string]: string }) {
|
||||
const errors = require('../../errors');
|
||||
const constants = require('../../constants');
|
||||
const algoCheck = require('./algoCheck');
|
||||
const constructStringToSign = require('./constructStringToSign');
|
||||
|
||||
function check(request, log, data) {
|
||||
log.trace('running query auth check');
|
||||
if (request.method === 'POST') {
|
||||
log.debug('query string auth not supported for post requests');
|
||||
|
@ -50,7 +51,6 @@ export function check(request: any, log: Logger, data: { [key: string]: string }
|
|||
return { err: errors.RequestTimeTooSkewed };
|
||||
}
|
||||
const accessKey = data.AWSAccessKeyId;
|
||||
// @ts-ignore
|
||||
log.addDefaultFields({ accessKey });
|
||||
|
||||
const signatureFromRequest = decodeURIComponent(data.Signature);
|
||||
|
@ -82,3 +82,5 @@ export function check(request: any, log: Logger, data: { [key: string]: string }
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { check };
|
||||
|
|
|
@ -1,2 +1,11 @@
|
|||
export * as header from './headerAuthCheck';
|
||||
export * as query from './queryAuthCheck';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const headerAuthCheck = require('./headerAuthCheck');
|
||||
const queryAuthCheck = require('./queryAuthCheck');
|
||||
|
||||
const authV4 = {
|
||||
header: headerAuthCheck,
|
||||
query: queryAuthCheck,
|
||||
};
|
||||
|
||||
module.exports = authV4;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
/*
|
||||
AWS's URI encoding rules:
|
||||
URI encode every byte. Uri-Encode() must enforce the following rules:
|
||||
|
@ -17,7 +19,7 @@ See http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
|
|||
*/
|
||||
|
||||
// converts utf8 character to hex and pads "%" before every two hex digits
|
||||
function _toHexUTF8(char: string) {
|
||||
function _toHexUTF8(char) {
|
||||
const hexRep = Buffer.from(char, 'utf8').toString('hex').toUpperCase();
|
||||
let res = '';
|
||||
hexRep.split('').forEach((v, n) => {
|
||||
|
@ -30,11 +32,7 @@ function _toHexUTF8(char: string) {
|
|||
return res;
|
||||
}
|
||||
|
||||
export default function awsURIencode(
|
||||
input: string,
|
||||
encodeSlash?: boolean,
|
||||
noEncodeStar?: boolean
|
||||
) {
|
||||
function awsURIencode(input, encodeSlash, noEncodeStar) {
|
||||
const encSlash = encodeSlash === undefined ? true : encodeSlash;
|
||||
let encoded = '';
|
||||
/**
|
||||
|
@ -64,3 +62,5 @@ export default function awsURIencode(
|
|||
}
|
||||
return encoded;
|
||||
}
|
||||
|
||||
module.exports = awsURIencode;
|
||||
|
|
|
@ -1,33 +1,17 @@
|
|||
import * as crypto from 'crypto';
|
||||
import { Logger } from 'werelogs';
|
||||
import createCanonicalRequest from './createCanonicalRequest';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const crypto = require('crypto');
|
||||
|
||||
const createCanonicalRequest = require('./createCanonicalRequest');
|
||||
|
||||
/**
|
||||
* constructStringToSign - creates V4 stringToSign
|
||||
* @param {object} params - params object
|
||||
* @returns {string} - stringToSign
|
||||
*/
|
||||
export default function constructStringToSign(params: {
|
||||
request: any;
|
||||
signedHeaders: any;
|
||||
payloadChecksum: any;
|
||||
credentialScope: string;
|
||||
timestamp: string;
|
||||
query: { [key: string]: string };
|
||||
log?: Logger;
|
||||
proxyPath?: string;
|
||||
awsService: string;
|
||||
}): string | Error {
|
||||
const {
|
||||
request,
|
||||
signedHeaders,
|
||||
payloadChecksum,
|
||||
credentialScope,
|
||||
timestamp,
|
||||
query,
|
||||
log,
|
||||
proxyPath,
|
||||
} = params;
|
||||
function constructStringToSign(params) {
|
||||
const { request, signedHeaders, payloadChecksum, credentialScope, timestamp,
|
||||
query, log, proxyPath } = params;
|
||||
const path = proxyPath || request.path;
|
||||
|
||||
const canonicalReqResult = createCanonicalRequest({
|
||||
|
@ -40,8 +24,6 @@ export default function constructStringToSign(params: {
|
|||
service: params.awsService,
|
||||
});
|
||||
|
||||
// TODO Why that line?
|
||||
// @ts-ignore
|
||||
if (canonicalReqResult instanceof Error) {
|
||||
if (log) {
|
||||
log.error('error creating canonicalRequest');
|
||||
|
@ -58,3 +40,5 @@ export default function constructStringToSign(params: {
|
|||
`${credentialScope}\n${canonicalHex}`;
|
||||
return stringToSign;
|
||||
}
|
||||
|
||||
module.exports = constructStringToSign;
|
||||
|
|
|
@ -1,33 +1,27 @@
|
|||
import * as crypto from 'crypto';
|
||||
import * as queryString from 'querystring';
|
||||
import awsURIencode from './awsURIencode';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const awsURIencode = require('./awsURIencode');
|
||||
const crypto = require('crypto');
|
||||
const queryString = require('querystring');
|
||||
|
||||
/**
|
||||
* createCanonicalRequest - creates V4 canonical request
|
||||
* @param params - contains pHttpVerb (request type),
|
||||
* @param {object} params - contains pHttpVerb (request type),
|
||||
* pResource (parsed from URL), pQuery (request query),
|
||||
* pHeaders (request headers), pSignedHeaders (signed headers from request),
|
||||
* payloadChecksum (from request)
|
||||
* @returns - canonicalRequest
|
||||
* @returns {string} - canonicalRequest
|
||||
*/
|
||||
export default function createCanonicalRequest(
|
||||
params: {
|
||||
pHttpVerb: string;
|
||||
pResource: string;
|
||||
pQuery: { [key: string]: string };
|
||||
pHeaders: any;
|
||||
pSignedHeaders: any;
|
||||
service: string;
|
||||
payloadChecksum: string;
|
||||
}
|
||||
) {
|
||||
function createCanonicalRequest(params) {
|
||||
const pHttpVerb = params.pHttpVerb;
|
||||
const pResource = params.pResource;
|
||||
const pQuery = params.pQuery;
|
||||
const pHeaders = params.pHeaders;
|
||||
const pSignedHeaders = params.pSignedHeaders;
|
||||
const service = params.service;
|
||||
|
||||
let payloadChecksum = params.payloadChecksum;
|
||||
|
||||
if (!payloadChecksum) {
|
||||
if (pHttpVerb === 'GET') {
|
||||
payloadChecksum = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b' +
|
||||
|
@ -40,7 +34,7 @@ export default function createCanonicalRequest(
|
|||
if (/aws-sdk-java\/[0-9.]+/.test(pHeaders['user-agent'])) {
|
||||
notEncodeStar = true;
|
||||
}
|
||||
let payload = queryString.stringify(pQuery, undefined, undefined, {
|
||||
let payload = queryString.stringify(pQuery, null, null, {
|
||||
encodeURIComponent: input => awsURIencode(input, true,
|
||||
notEncodeStar),
|
||||
});
|
||||
|
@ -67,11 +61,11 @@ export default function createCanonicalRequest(
|
|||
|
||||
// signed headers
|
||||
const signedHeadersList = pSignedHeaders.split(';');
|
||||
signedHeadersList.sort((a: any, b: any) => a.localeCompare(b));
|
||||
signedHeadersList.sort((a, b) => a.localeCompare(b));
|
||||
const signedHeaders = signedHeadersList.join(';');
|
||||
|
||||
// canonical headers
|
||||
const canonicalHeadersList = signedHeadersList.map((signedHeader: any) => {
|
||||
const canonicalHeadersList = signedHeadersList.map(signedHeader => {
|
||||
if (pHeaders[signedHeader] !== undefined) {
|
||||
const trimmedHeader = pHeaders[signedHeader]
|
||||
.trim().replace(/\s+/g, ' ');
|
||||
|
@ -93,3 +87,5 @@ export default function createCanonicalRequest(
|
|||
`${signedHeaders}\n${payloadChecksum}`;
|
||||
return canonicalRequest;
|
||||
}
|
||||
|
||||
module.exports = createCanonicalRequest;
|
||||
|
|
|
@ -1,32 +1,27 @@
|
|||
import { Logger } from 'werelogs';
|
||||
import errors from '../../../lib/errors';
|
||||
import * as constants from '../../constants';
|
||||
import constructStringToSign from './constructStringToSign';
|
||||
import {
|
||||
checkTimeSkew,
|
||||
convertUTCtoISO8601,
|
||||
convertAmzTimeToMs,
|
||||
} from './timeUtils';
|
||||
import {
|
||||
extractAuthItems,
|
||||
validateCredentials,
|
||||
areSignedHeadersComplete,
|
||||
} from './validateInputs';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const errors = require('../../../lib/errors');
|
||||
const constants = require('../../constants');
|
||||
|
||||
const constructStringToSign = require('./constructStringToSign');
|
||||
const checkTimeSkew = require('./timeUtils').checkTimeSkew;
|
||||
const convertUTCtoISO8601 = require('./timeUtils').convertUTCtoISO8601;
|
||||
const convertAmzTimeToMs = require('./timeUtils').convertAmzTimeToMs;
|
||||
const extractAuthItems = require('./validateInputs').extractAuthItems;
|
||||
const validateCredentials = require('./validateInputs').validateCredentials;
|
||||
const areSignedHeadersComplete =
|
||||
require('./validateInputs').areSignedHeadersComplete;
|
||||
|
||||
/**
|
||||
* V4 header auth check
|
||||
* @param request - HTTP request object
|
||||
* @param log - logging object
|
||||
* @param data - Parameters from queryString parsing or body of
|
||||
* @param {object} request - HTTP request object
|
||||
* @param {object} log - logging object
|
||||
* @param {object} data - Parameters from queryString parsing or body of
|
||||
* POST request
|
||||
* @param awsService - Aws service ('iam' or 's3')
|
||||
* @param {string} awsService - Aws service ('iam' or 's3')
|
||||
* @return {callback} calls callback
|
||||
*/
|
||||
export function check(
|
||||
request: any,
|
||||
log: Logger,
|
||||
data: { [key: string]: string },
|
||||
awsService: string
|
||||
) {
|
||||
function check(request, log, data, awsService) {
|
||||
log.trace('running header auth check');
|
||||
|
||||
const token = request.headers['x-amz-security-token'];
|
||||
|
@ -67,16 +62,16 @@ export function check(
|
|||
|
||||
log.trace('authorization header from request', { authHeader });
|
||||
|
||||
const signatureFromRequest = authHeaderItems.signatureFromRequest!;
|
||||
const credentialsArr = authHeaderItems.credentialsArr!;
|
||||
const signedHeaders = authHeaderItems.signedHeaders!;
|
||||
const signatureFromRequest = authHeaderItems.signatureFromRequest;
|
||||
const credentialsArr = authHeaderItems.credentialsArr;
|
||||
const signedHeaders = authHeaderItems.signedHeaders;
|
||||
|
||||
if (!areSignedHeadersComplete(signedHeaders, request.headers)) {
|
||||
log.debug('signedHeaders are incomplete', { signedHeaders });
|
||||
return { err: errors.AccessDenied };
|
||||
}
|
||||
|
||||
let timestamp: string | undefined;
|
||||
let timestamp;
|
||||
// check request timestamp
|
||||
const xAmzDate = request.headers['x-amz-date'];
|
||||
if (xAmzDate) {
|
||||
|
@ -171,3 +166,5 @@ export function check(
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { check };
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
import { Logger } from 'werelogs';
|
||||
import * as constants from '../../constants';
|
||||
import errors from '../../errors';
|
||||
import constructStringToSign from './constructStringToSign';
|
||||
import { checkTimeSkew, convertAmzTimeToMs } from './timeUtils';
|
||||
import { validateCredentials, extractQueryParams } from './validateInputs';
|
||||
import { areSignedHeadersComplete } from './validateInputs';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const constants = require('../../constants');
|
||||
const errors = require('../../errors');
|
||||
|
||||
const constructStringToSign = require('./constructStringToSign');
|
||||
const checkTimeSkew = require('./timeUtils').checkTimeSkew;
|
||||
const convertAmzTimeToMs = require('./timeUtils').convertAmzTimeToMs;
|
||||
const validateCredentials = require('./validateInputs').validateCredentials;
|
||||
const extractQueryParams = require('./validateInputs').extractQueryParams;
|
||||
const areSignedHeadersComplete =
|
||||
require('./validateInputs').areSignedHeadersComplete;
|
||||
|
||||
/**
|
||||
* V4 query auth check
|
||||
* @param request - HTTP request object
|
||||
* @param log - logging object
|
||||
* @param data - Contain authentification params (GET or POST data)
|
||||
* @param {object} request - HTTP request object
|
||||
* @param {object} log - logging object
|
||||
* @param {object} data - Contain authentification params (GET or POST data)
|
||||
* @return {callback} calls callback
|
||||
*/
|
||||
export function check(request: any, log: Logger, data: { [key: string]: string }) {
|
||||
function check(request, log, data) {
|
||||
const authParams = extractQueryParams(data, log);
|
||||
|
||||
if (Object.keys(authParams).length !== 5) {
|
||||
|
@ -27,11 +33,11 @@ export function check(request: any, log: Logger, data: { [key: string]: string }
|
|||
return { err: errors.InvalidToken };
|
||||
}
|
||||
|
||||
const signedHeaders = authParams.signedHeaders!;
|
||||
const signatureFromRequest = authParams.signatureFromRequest!;
|
||||
const timestamp = authParams.timestamp!;
|
||||
const expiry = authParams.expiry!;
|
||||
const credential = authParams.credential!;
|
||||
const signedHeaders = authParams.signedHeaders;
|
||||
const signatureFromRequest = authParams.signatureFromRequest;
|
||||
const timestamp = authParams.timestamp;
|
||||
const expiry = authParams.expiry;
|
||||
const credential = authParams.credential;
|
||||
|
||||
if (!areSignedHeadersComplete(signedHeaders, request.headers)) {
|
||||
log.debug('signedHeaders are incomplete', { signedHeaders });
|
||||
|
@ -104,3 +110,5 @@ export function check(request: any, log: Logger, data: { [key: string]: string }
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { check };
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { Logger } from 'werelogs';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
/**
|
||||
* Convert timestamp to milliseconds since Unix Epoch
|
||||
* @param timestamp of ISO8601Timestamp format without
|
||||
* @param {string} timestamp of ISO8601Timestamp format without
|
||||
* dashes or colons, e.g. 20160202T220410Z
|
||||
* @return {number} number of milliseconds since Unix Epoch
|
||||
*/
|
||||
export function convertAmzTimeToMs(timestamp: string) {
|
||||
function convertAmzTimeToMs(timestamp) {
|
||||
const arr = timestamp.split('');
|
||||
// Convert to YYYY-MM-DDTHH:mm:ss.sssZ
|
||||
const ISO8601time = `${arr.slice(0, 4).join('')}-${arr[4]}${arr[5]}` +
|
||||
|
@ -14,12 +15,13 @@ export function convertAmzTimeToMs(timestamp: string) {
|
|||
return Date.parse(ISO8601time);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert UTC timestamp to ISO 8601 timestamp
|
||||
* @param timestamp of UTC form: Fri, 10 Feb 2012 21:34:55 GMT
|
||||
* @return ISO8601 timestamp of form: YYYYMMDDTHHMMSSZ
|
||||
* @param {string} timestamp of UTC form: Fri, 10 Feb 2012 21:34:55 GMT
|
||||
* @return {string} ISO8601 timestamp of form: YYYYMMDDTHHMMSSZ
|
||||
*/
|
||||
export function convertUTCtoISO8601(timestamp: string | number) {
|
||||
function convertUTCtoISO8601(timestamp) {
|
||||
// convert to ISO string: YYYY-MM-DDTHH:mm:ss.sssZ.
|
||||
const converted = new Date(timestamp).toISOString();
|
||||
// Remove "-"s and "."s and milliseconds
|
||||
|
@ -28,13 +30,13 @@ export function convertUTCtoISO8601(timestamp: string | number) {
|
|||
|
||||
/**
|
||||
* Check whether timestamp predates request or is too old
|
||||
* @param timestamp of ISO8601Timestamp format without
|
||||
* @param {string} timestamp of ISO8601Timestamp format without
|
||||
* dashes or colons, e.g. 20160202T220410Z
|
||||
* @param expiry - number of seconds signature should be valid
|
||||
* @param log - log for request
|
||||
* @return true if there is a time problem
|
||||
* @param {number} expiry - number of seconds signature should be valid
|
||||
* @param {object} log - log for request
|
||||
* @return {boolean} true if there is a time problem
|
||||
*/
|
||||
export function checkTimeSkew(timestamp: string, expiry: number, log: Logger) {
|
||||
function checkTimeSkew(timestamp, expiry, log) {
|
||||
const currentTime = Date.now();
|
||||
const fifteenMinutes = (15 * 60 * 1000);
|
||||
const parsedTimestamp = convertAmzTimeToMs(timestamp);
|
||||
|
@ -54,3 +56,5 @@ export function checkTimeSkew(timestamp: string, expiry: number, log: Logger) {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = { convertAmzTimeToMs, convertUTCtoISO8601, checkTimeSkew };
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
import { Logger } from 'werelogs';
|
||||
import errors from '../../../lib/errors';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const errors = require('../../../lib/errors');
|
||||
|
||||
/**
|
||||
* Validate Credentials
|
||||
* @param credentials - contains accessKey, scopeDate,
|
||||
* @param {array} credentials - contains accessKey, scopeDate,
|
||||
* region, service, requestType
|
||||
* @param timestamp - timestamp from request in
|
||||
* @param {string} timestamp - timestamp from request in
|
||||
* the format of ISO 8601: YYYYMMDDTHHMMSSZ
|
||||
* @param log - logging object
|
||||
* @param {object} log - logging object
|
||||
* @return {boolean} true if credentials are correct format, false if not
|
||||
*/
|
||||
export function validateCredentials(
|
||||
credentials: [string, string, string, string, string],
|
||||
timestamp: string,
|
||||
log: Logger
|
||||
): Error | {} {
|
||||
function validateCredentials(credentials, timestamp, log) {
|
||||
if (!Array.isArray(credentials) || credentials.length !== 5) {
|
||||
log.warn('credentials in improper format', { credentials });
|
||||
return errors.InvalidArgument;
|
||||
|
@ -60,21 +58,12 @@ export function validateCredentials(
|
|||
|
||||
/**
|
||||
* Extract and validate components from query object
|
||||
* @param queryObj - query object from request
|
||||
* @param log - logging object
|
||||
* @return object containing extracted query params for authV4
|
||||
* @param {object} queryObj - query object from request
|
||||
* @param {object} log - logging object
|
||||
* @return {object} object containing extracted query params for authV4
|
||||
*/
|
||||
export function extractQueryParams(
|
||||
queryObj: { [key: string]: string | undefined },
|
||||
log: Logger
|
||||
) {
|
||||
const authParams: {
|
||||
signedHeaders?: string;
|
||||
signatureFromRequest?: string;
|
||||
timestamp?: string;
|
||||
expiry?: number;
|
||||
credential?: [string, string, string, string, string];
|
||||
} = {};
|
||||
function extractQueryParams(queryObj, log) {
|
||||
const authParams = {};
|
||||
|
||||
// Do not need the algorithm sent back
|
||||
if (queryObj['X-Amz-Algorithm'] !== 'AWS4-HMAC-SHA256') {
|
||||
|
@ -110,7 +99,7 @@ export function extractQueryParams(
|
|||
return authParams;
|
||||
}
|
||||
|
||||
const expiry = Number.parseInt(queryObj['X-Amz-Expires'] ?? 'nope', 10);
|
||||
const expiry = Number.parseInt(queryObj['X-Amz-Expires'], 10);
|
||||
const sevenDays = 604800;
|
||||
if (expiry && (expiry > 0 && expiry <= sevenDays)) {
|
||||
authParams.expiry = expiry;
|
||||
|
@ -121,7 +110,6 @@ export function extractQueryParams(
|
|||
|
||||
const credential = queryObj['X-Amz-Credential'];
|
||||
if (credential && credential.length > 28 && credential.indexOf('/') > -1) {
|
||||
// @ts-ignore
|
||||
authParams.credential = credential.split('/');
|
||||
} else {
|
||||
log.warn('invalid credential param', { credential });
|
||||
|
@ -133,17 +121,14 @@ export function extractQueryParams(
|
|||
|
||||
/**
|
||||
* Extract and validate components from auth header
|
||||
* @param authHeader - authorization header from request
|
||||
* @param log - logging object
|
||||
* @return object containing extracted auth header items for authV4
|
||||
* @param {string} authHeader - authorization header from request
|
||||
* @param {object} log - logging object
|
||||
* @return {object} object containing extracted auth header items for authV4
|
||||
*/
|
||||
export function extractAuthItems(authHeader: string, log: Logger) {
|
||||
const authItems: {
|
||||
credentialsArr?: [string, string, string, string, string];
|
||||
signedHeaders?: string;
|
||||
signatureFromRequest?: string;
|
||||
} = {};
|
||||
const authArray = authHeader.replace('AWS4-HMAC-SHA256 ', '').split(',');
|
||||
function extractAuthItems(authHeader, log) {
|
||||
const authItems = {};
|
||||
const authArray = authHeader
|
||||
.replace('AWS4-HMAC-SHA256 ', '').split(',');
|
||||
|
||||
if (authArray.length < 3) {
|
||||
return authItems;
|
||||
|
@ -153,12 +138,8 @@ export function extractAuthItems(authHeader: string, log: Logger) {
|
|||
const signedHeadersStr = authArray[1];
|
||||
const signatureStr = authArray[2];
|
||||
log.trace('credentials from request', { credentialStr });
|
||||
if (
|
||||
credentialStr &&
|
||||
credentialStr.trim().startsWith('Credential=') &&
|
||||
credentialStr.indexOf('/') > -1
|
||||
) {
|
||||
// @ts-ignore
|
||||
if (credentialStr && credentialStr.trim().startsWith('Credential=')
|
||||
&& credentialStr.indexOf('/') > -1) {
|
||||
authItems.credentialsArr = credentialStr
|
||||
.trim().replace('Credential=', '').split('/');
|
||||
} else {
|
||||
|
@ -185,11 +166,11 @@ export function extractAuthItems(authHeader: string, log: Logger) {
|
|||
/**
|
||||
* Checks whether the signed headers include the host header
|
||||
* and all x-amz- and x-scal- headers in request
|
||||
* @param signedHeaders - signed headers sent with request
|
||||
* @param allHeaders - request.headers
|
||||
* @return true if all x-amz-headers included and false if not
|
||||
* @param {string} signedHeaders - signed headers sent with request
|
||||
* @param {object} allHeaders - request.headers
|
||||
* @return {boolean} true if all x-amz-headers included and false if not
|
||||
*/
|
||||
export function areSignedHeadersComplete(signedHeaders: string, allHeaders: Headers) {
|
||||
function areSignedHeadersComplete(signedHeaders, allHeaders) {
|
||||
const signedHeadersList = signedHeaders.split(';');
|
||||
if (signedHeadersList.indexOf('host') === -1) {
|
||||
return false;
|
||||
|
@ -204,3 +185,6 @@ export function areSignedHeadersComplete(signedHeaders: string, allHeaders: Head
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = { validateCredentials, extractQueryParams,
|
||||
areSignedHeadersComplete, extractAuthItems };
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
export const ciphers = [
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const ciphers = [
|
||||
'DHE-RSA-AES128-GCM-SHA256',
|
||||
'ECDHE-ECDSA-AES128-GCM-SHA256',
|
||||
'ECDHE-RSA-AES256-GCM-SHA384',
|
||||
|
@ -26,3 +28,7 @@ export const ciphers = [
|
|||
'!EDH-RSA-DES-CBC3-SHA',
|
||||
'!KRB5-DES-CBC3-SHA',
|
||||
].join(':');
|
||||
|
||||
module.exports = {
|
||||
ciphers,
|
||||
};
|
|
@ -29,11 +29,16 @@ c2CNfUEqyRbJF4pE9ZcdQReT5p/llmyhQdvq6cHH+cKJk63C6DHRVoStsnsUcvKe
|
|||
bLxKsygK77ttjr61cxLoDJeGd5L5h1CPmwIBAg==
|
||||
-----END DH PARAMETERS-----
|
||||
*/
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
export const dhparam =
|
||||
const dhparam =
|
||||
'MIIBCAKCAQEAh99T77KGNuiY9N6xrCJ3QNv4SFADTa3CD+1VMTAdRJLHUNpglB+i' +
|
||||
'AoTYiLDFZgtTCpx0ZZUD+JM3qiCZy0OK5/ZGlVD7sZmxjRtdpVK4qIPtwav8t0J7' +
|
||||
'c2CNfUEqyRbJF4pE9ZcdQReT5p/llmyhQdvq6cHH+cKJk63C6DHRVoStsnsUcvKe' +
|
||||
'23PLGZulKg8H3eRBxHamHkmyuEVDtoNhMIoJONsdXSpo5GgcD4EQMM8xb/qsnCxn' +
|
||||
'6QIGTBvcHskxtlTZOfUPk4XQ6Yb3tQi2TurzkQHLln4U7p/GZs+D+6D3SgSPqr6P' +
|
||||
'bLxKsygK77ttjr61cxLoDJeGd5L5h1CPmwIBAg==';
|
||||
|
||||
module.exports = {
|
||||
dhparam,
|
||||
};
|
|
@ -1,2 +0,0 @@
|
|||
export * as ciphers from './ciphers'
|
||||
export * as dhparam from './dh2048'
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
const assert = require('assert');
|
||||
|
||||
const AuthInfo = require('../../../lib/auth/AuthInfo').default;
|
||||
const AuthInfo = require('../../../lib/auth/AuthInfo');
|
||||
const constants = require('../../../lib/constants');
|
||||
|
||||
const arn = 'arn:aws:iam::123456789012:user/Fred';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const assert = require('assert');
|
||||
|
||||
const Indexer = require('../../../../lib/auth/in_memory/Indexer').default;
|
||||
const Indexer = require('../../../../lib/auth/in_memory/Indexer');
|
||||
const ref = require('./sample_authdata.json');
|
||||
const { should } = require('./AuthLoader.spec');
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
const assert = require('assert');
|
||||
|
||||
const getCanonicalizedAmzHeaders =
|
||||
require('../../../../lib/auth/v2/getCanonicalizedAmzHeaders').default;
|
||||
require('../../../../lib/auth/v2/getCanonicalizedAmzHeaders');
|
||||
const getCanonicalizedResource =
|
||||
require('../../../../lib/auth/v2/getCanonicalizedResource').default;
|
||||
require('../../../../lib/auth/v2/getCanonicalizedResource');
|
||||
|
||||
const getCanonicalizedGcpHeaders = headers =>
|
||||
getCanonicalizedAmzHeaders(headers, 'GCP');
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
const assert = require('assert');
|
||||
|
||||
const checkRequestExpiry =
|
||||
require('../../../../lib/auth/v2/checkRequestExpiry').default;
|
||||
require('../../../../lib/auth/v2/checkRequestExpiry');
|
||||
const DummyRequestLogger = require('../../helpers').DummyRequestLogger;
|
||||
const errors = require('../../../../index').errors;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
const assert = require('assert');
|
||||
|
||||
const constructStringToSign =
|
||||
require('../../../../lib/auth/v2/constructStringToSign').default;
|
||||
require('../../../../lib/auth/v2/constructStringToSign');
|
||||
const DummyRequestLogger = require('../../helpers').DummyRequestLogger;
|
||||
|
||||
const log = new DummyRequestLogger();
|
||||
|
|
|
@ -4,7 +4,7 @@ const assert = require('assert');
|
|||
|
||||
const errors = require('../../../../lib/errors');
|
||||
const auth = require('../../../../lib/auth/auth').server.doAuth;
|
||||
const AuthInfo = require('../../../../lib/auth/AuthInfo').default;
|
||||
const AuthInfo = require('../../../../lib/auth/AuthInfo');
|
||||
const constants = require('../../../../lib/constants');
|
||||
const DummyRequestLogger = require('../../helpers.js').DummyRequestLogger;
|
||||
const RequestContext =
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
const assert = require('assert');
|
||||
|
||||
const constructStringToSign =
|
||||
require('../../../../lib/auth/v2/constructStringToSign').default;
|
||||
require('../../../../lib/auth/v2/constructStringToSign');
|
||||
const hashSignature =
|
||||
require('../../../../lib/auth/in_memory/vaultUtilities').hashSignature;
|
||||
const DummyRequestLogger = require('../../helpers').DummyRequestLogger;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
const assert = require('assert');
|
||||
|
||||
const awsURIencode = require('../../../../lib/auth/v4/awsURIencode').default;
|
||||
const awsURIencode = require('../../../../lib/auth/v4/awsURIencode');
|
||||
|
||||
// Note that expected outputs came from running node aws-sdk's
|
||||
// AWS.util.uriEscapePath and AWS.util.uriEscape functions
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
const assert = require('assert');
|
||||
|
||||
const constructStringToSign =
|
||||
require('../../../../lib/auth/v4/constructStringToSign').default;
|
||||
require('../../../../lib/auth/v4/constructStringToSign');
|
||||
const DummyRequestLogger = require('../../helpers').DummyRequestLogger;
|
||||
|
||||
const log = new DummyRequestLogger();
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
const assert = require('assert');
|
||||
const awsURIencode =
|
||||
require('../../../../lib/auth/v4/awsURIencode').default;
|
||||
require('../../../../lib/auth/v4/awsURIencode');
|
||||
const createCanonicalRequest =
|
||||
require('../../../../lib/auth/v4/createCanonicalRequest').default;
|
||||
require('../../../../lib/auth/v4/createCanonicalRequest');
|
||||
|
||||
describe('createCanonicalRequest function', () => {
|
||||
// Example taken from: http://docs.aws.amazon.com/AmazonS3/
|
||||
|
|
Loading…
Reference in New Issue