Compare commits
3 Commits
developmen
...
doc-adddoc
Author | SHA1 | Date |
---|---|---|
Electra Chong | d09b25877c | |
Electra Chong | ed15e244af | |
Electra Chong | a3b5127141 |
|
@ -10,6 +10,11 @@ const constructStringToSignV4 = require('./v4/constructStringToSign');
|
||||||
const convertUTCtoISO8601 = require('./v4/timeUtils').convertUTCtoISO8601;
|
const convertUTCtoISO8601 = require('./v4/timeUtils').convertUTCtoISO8601;
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const vaultUtilities = require('./in_memory/vaultUtilities');
|
const vaultUtilities = require('./in_memory/vaultUtilities');
|
||||||
|
const backend = require('./in_memory/s3/backend');
|
||||||
|
const indexer = require('./in_memory/s3/indexer');
|
||||||
|
const checker = require('./in_memory/s3/checker');
|
||||||
|
|
||||||
|
|
||||||
let vault = null;
|
let vault = null;
|
||||||
const auth = {};
|
const auth = {};
|
||||||
const checkFunctions = {
|
const checkFunctions = {
|
||||||
|
@ -207,4 +212,9 @@ module.exports = {
|
||||||
client: {
|
client: {
|
||||||
generateV4Headers,
|
generateV4Headers,
|
||||||
},
|
},
|
||||||
|
inMemory: {
|
||||||
|
backend,
|
||||||
|
indexer,
|
||||||
|
checker,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,39 @@ const accountsKeyedbyEmail =
|
||||||
const calculateSigningKey = require('./vaultUtilities').calculateSigningKey;
|
const calculateSigningKey = require('./vaultUtilities').calculateSigningKey;
|
||||||
const hashSignature = require('./vaultUtilities').hashSignature;
|
const hashSignature = require('./vaultUtilities').hashSignature;
|
||||||
|
|
||||||
const backend = {
|
|
||||||
|
const _indexer = {
|
||||||
|
getEntityByKey(accessKey) {
|
||||||
|
return accountsKeyedbyAccessKey[accessKey];
|
||||||
|
},
|
||||||
|
getEntityByEmail(email) {
|
||||||
|
return accountsKeyedbyEmail[email];
|
||||||
|
},
|
||||||
|
getEntityByCanId(canonicalId) {
|
||||||
|
return accountsKeyedbyCanID[canonicalId];
|
||||||
|
},
|
||||||
|
getSecretKey(account) {
|
||||||
|
return account.secretKey;
|
||||||
|
},
|
||||||
|
getAcctDisplayName(account) {
|
||||||
|
return account.displayName;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function _formatResponse(userInfoToSend) {
|
||||||
|
return {
|
||||||
|
message: {
|
||||||
|
body: userInfoToSend,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class BackendTemplate {
|
||||||
|
constructor(indexer, formatter) {
|
||||||
|
this.indexer = indexer;
|
||||||
|
this.formatResponse = formatter;
|
||||||
|
}
|
||||||
|
|
||||||
/** verifySignatureV2
|
/** verifySignatureV2
|
||||||
* @param {string} stringToSign - string to sign built per AWS rules
|
* @param {string} stringToSign - string to sign built per AWS rules
|
||||||
* @param {string} signatureFromRequest - signature sent with request
|
* @param {string} signatureFromRequest - signature sent with request
|
||||||
|
@ -21,31 +53,27 @@ const backend = {
|
||||||
* @param {function} callback - callback with either error or user info
|
* @param {function} callback - callback with either error or user info
|
||||||
* @return {function} calls callback
|
* @return {function} calls callback
|
||||||
*/
|
*/
|
||||||
verifySignatureV2: (stringToSign, signatureFromRequest,
|
verifySignatureV2(stringToSign, signatureFromRequest,
|
||||||
accessKey, options, callback) => {
|
accessKey, options, callback) {
|
||||||
const account = accountsKeyedbyAccessKey[accessKey];
|
const entity = this.indexer.getEntityByKey(accessKey);
|
||||||
if (!account) {
|
if (!entity) {
|
||||||
return callback(errors.InvalidAccessKeyId);
|
return callback(errors.InvalidAccessKeyId);
|
||||||
}
|
}
|
||||||
const secretKey = account.secretKey;
|
const secretKey = this.indexer.getSecretKey(entity, accessKey);
|
||||||
const reconstructedSig =
|
const reconstructedSig =
|
||||||
hashSignature(stringToSign, secretKey, options.algo);
|
hashSignature(stringToSign, secretKey, options.algo);
|
||||||
if (signatureFromRequest !== reconstructedSig) {
|
if (signatureFromRequest !== reconstructedSig) {
|
||||||
return callback(errors.SignatureDoesNotMatch);
|
return callback(errors.SignatureDoesNotMatch);
|
||||||
}
|
}
|
||||||
const userInfoToSend = {
|
const userInfoToSend = {
|
||||||
accountDisplayName: account.displayName,
|
accountDisplayName: this.indexer.getAcctDisplayName(entity),
|
||||||
canonicalID: account.canonicalID,
|
canonicalID: entity.canonicalID,
|
||||||
arn: account.arn,
|
arn: entity.arn,
|
||||||
IAMdisplayName: account.IAMdisplayName,
|
IAMdisplayName: entity.IAMdisplayName,
|
||||||
};
|
|
||||||
const vaultReturnObject = {
|
|
||||||
message: {
|
|
||||||
body: userInfoToSend,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
const vaultReturnObject = this.formatResponse(userInfoToSend);
|
||||||
return callback(null, vaultReturnObject);
|
return callback(null, vaultReturnObject);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
|
||||||
/** verifySignatureV4
|
/** verifySignatureV4
|
||||||
|
@ -59,13 +87,13 @@ const backend = {
|
||||||
* @param {function} callback - callback with either error or user info
|
* @param {function} callback - callback with either error or user info
|
||||||
* @return {function} calls callback
|
* @return {function} calls callback
|
||||||
*/
|
*/
|
||||||
verifySignatureV4: (stringToSign, signatureFromRequest, accessKey,
|
verifySignatureV4(stringToSign, signatureFromRequest, accessKey,
|
||||||
region, scopeDate, options, callback) => {
|
region, scopeDate, options, callback) {
|
||||||
const account = accountsKeyedbyAccessKey[accessKey];
|
const entity = this.indexer.getEntityByKey(accessKey);
|
||||||
if (!account) {
|
if (!entity) {
|
||||||
return callback(errors.InvalidAccessKeyId);
|
return callback(errors.InvalidAccessKeyId);
|
||||||
}
|
}
|
||||||
const secretKey = account.secretKey;
|
const secretKey = this.indexer.getSecretKey(entity, accessKey);
|
||||||
const signingKey = calculateSigningKey(secretKey, region, scopeDate);
|
const signingKey = calculateSigningKey(secretKey, region, scopeDate);
|
||||||
const reconstructedSig = crypto.createHmac('sha256', signingKey)
|
const reconstructedSig = crypto.createHmac('sha256', signingKey)
|
||||||
.update(stringToSign, 'binary').digest('hex');
|
.update(stringToSign, 'binary').digest('hex');
|
||||||
|
@ -73,18 +101,14 @@ const backend = {
|
||||||
return callback(errors.SignatureDoesNotMatch);
|
return callback(errors.SignatureDoesNotMatch);
|
||||||
}
|
}
|
||||||
const userInfoToSend = {
|
const userInfoToSend = {
|
||||||
accountDisplayName: account.displayName,
|
accountDisplayName: this.indexer.getAcctDisplayName(entity),
|
||||||
canonicalID: account.canonicalID,
|
canonicalID: entity.canonicalID,
|
||||||
arn: account.arn,
|
arn: entity.arn,
|
||||||
IAMdisplayName: account.IAMdisplayName,
|
IAMdisplayName: entity.IAMdisplayName,
|
||||||
};
|
|
||||||
const vaultReturnObject = {
|
|
||||||
message: {
|
|
||||||
body: userInfoToSend,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
const vaultReturnObject = this.formatResponse(userInfoToSend);
|
||||||
return callback(null, vaultReturnObject);
|
return callback(null, vaultReturnObject);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets canonical ID's for a list of accounts
|
* Gets canonical ID's for a list of accounts
|
||||||
|
@ -96,15 +120,16 @@ const backend = {
|
||||||
* object with email addresses as keys and canonical IDs
|
* object with email addresses as keys and canonical IDs
|
||||||
* as values
|
* as values
|
||||||
*/
|
*/
|
||||||
getCanonicalIds: (emails, log, cb) => {
|
getCanonicalIds(emails, log, cb) {
|
||||||
const results = {};
|
const results = {};
|
||||||
emails.forEach(email => {
|
emails.forEach(email => {
|
||||||
const lowercasedEmail = email.toLowerCase();
|
const lowercasedEmail = email.toLowerCase();
|
||||||
if (!accountsKeyedbyEmail[lowercasedEmail]) {
|
const entity = this.indexer.getEntityByEmail[lowercasedEmail];
|
||||||
|
if (!entity) {
|
||||||
results[email] = 'NotFound';
|
results[email] = 'NotFound';
|
||||||
} else {
|
} else {
|
||||||
results[email] =
|
results[email] =
|
||||||
accountsKeyedbyEmail[lowercasedEmail].canonicalID;
|
entity.canonicalID;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const vaultReturnObject = {
|
const vaultReturnObject = {
|
||||||
|
@ -113,7 +138,7 @@ const backend = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return cb(null, vaultReturnObject);
|
return cb(null, vaultReturnObject);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets email addresses (referred to as diplay names for getACL's)
|
* Gets email addresses (referred to as diplay names for getACL's)
|
||||||
|
@ -126,14 +151,14 @@ const backend = {
|
||||||
* an object from Vault containing account canonicalID
|
* an object from Vault containing account canonicalID
|
||||||
* as each object key and an email address as the value (or "NotFound")
|
* as each object key and an email address as the value (or "NotFound")
|
||||||
*/
|
*/
|
||||||
getEmailAddresses: (canonicalIDs, options, cb) => {
|
getEmailAddresses(canonicalIDs, options, cb) {
|
||||||
const results = {};
|
const results = {};
|
||||||
canonicalIDs.forEach(canonicalId => {
|
canonicalIDs.forEach(canonicalId => {
|
||||||
const foundAccount = accountsKeyedbyCanID[canonicalId];
|
const foundEntity = this.indexer.getEntityByCanId(canonicalId);
|
||||||
if (!foundAccount || !foundAccount.email) {
|
if (!foundEntity || !foundEntity.email) {
|
||||||
results[canonicalId] = 'NotFound';
|
results[canonicalId] = 'NotFound';
|
||||||
} else {
|
} else {
|
||||||
results[canonicalId] = foundAccount.email;
|
results[canonicalId] = foundEntity.email;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const vaultReturnObject = {
|
const vaultReturnObject = {
|
||||||
|
@ -142,7 +167,12 @@ const backend = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return cb(null, vaultReturnObject);
|
return cb(null, vaultReturnObject);
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = backend;
|
const backend = new BackendTemplate(_indexer, _formatResponse);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
backend,
|
||||||
|
BackendTemplate,
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
const BackendTemplate = require('../backend').BackendTemplate;
|
||||||
|
|
||||||
|
function _buildArn(generalResource, specificResource) {
|
||||||
|
return `arn:aws:s3:::${generalResource}/${specificResource}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Backend extends BackendTemplate {
|
||||||
|
/**
|
||||||
|
* Mocks Vault's response to a policy evaluation request
|
||||||
|
* Since policies not actually implemented in memory backend,
|
||||||
|
* we allow users to proceed with request.
|
||||||
|
* @param {object} requestContextParams - parameters needed to construct
|
||||||
|
* requestContext in Vault
|
||||||
|
* @param {object} requestContextParams.constantParams -
|
||||||
|
* params that have the
|
||||||
|
* same value for each requestContext to be constructed in Vault
|
||||||
|
* @param {object} requestContextParams.paramaterize - params that have
|
||||||
|
* arrays as values since a requestContext needs to be constructed with
|
||||||
|
* each option in Vault
|
||||||
|
* @param {object[]} requestContextParams.paramaterize.specificResource -
|
||||||
|
* specific resources paramaterized as an array of objects containing
|
||||||
|
* properties `key` and optional `versionId`
|
||||||
|
* @param {string} userArn - arn of requesting user
|
||||||
|
* @param {object} log - log object
|
||||||
|
* @param {function} cb - callback with either error or an array
|
||||||
|
* of authorization results
|
||||||
|
* @returns {undefined}
|
||||||
|
* @callback called with (err, vaultReturnObject)
|
||||||
|
*/
|
||||||
|
checkPolicies(requestContextParams, userArn, log, cb) {
|
||||||
|
let results;
|
||||||
|
const parameterizeParams = requestContextParams.parameterize;
|
||||||
|
if (parameterizeParams && parameterizeParams.specificResource) {
|
||||||
|
// object is parameterized
|
||||||
|
results = parameterizeParams.specificResource.map(obj => ({
|
||||||
|
isAllowed: true,
|
||||||
|
arn: _buildArn(requestContextParams
|
||||||
|
.constantParams.generalResource, obj.key),
|
||||||
|
versionId: obj.versionId,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
results = [{
|
||||||
|
isAllowed: true,
|
||||||
|
arn: _buildArn(requestContextParams
|
||||||
|
.constantParams.generalResource, requestContextParams
|
||||||
|
.constantParams.specificResource),
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
const vaultReturnObject = {
|
||||||
|
message: {
|
||||||
|
body: results,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return cb(null, vaultReturnObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Backend;
|
|
@ -0,0 +1,194 @@
|
||||||
|
const werelogs = require('werelogs');
|
||||||
|
|
||||||
|
function incr(count) {
|
||||||
|
if (count !== undefined) {
|
||||||
|
return count + 1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function ensures that the field `name` inside `container` is of the
|
||||||
|
* expected `type` inside `obj`. If any error is found, an entry is added into
|
||||||
|
* the error collector object.
|
||||||
|
*
|
||||||
|
* @param {object} data - the error collector object
|
||||||
|
* @param {string} container - the name of the entity that contains
|
||||||
|
* what we're checking
|
||||||
|
* @param {string} name - the name of the entity we're checking for
|
||||||
|
* @param {string} type - expected typename of the entity we're checking
|
||||||
|
* @param {object} obj - the object we're checking the fields of
|
||||||
|
* @return {boolean} true if the type is Ok and no error found
|
||||||
|
* false if an error was found and reported
|
||||||
|
*/
|
||||||
|
function checkType(data, container, name, type, obj) {
|
||||||
|
if ((type === 'array' && !Array.isArray(obj[name]))
|
||||||
|
|| (type !== 'array' && typeof obj[name] !== type)) {
|
||||||
|
data.errors.push({
|
||||||
|
txt: 'property is not of the expected type',
|
||||||
|
obj: {
|
||||||
|
entity: container,
|
||||||
|
property: name,
|
||||||
|
type: typeof obj[name],
|
||||||
|
expectedType: type,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function ensures that the field `name` inside `obj` which is a
|
||||||
|
* `container`. If any error is found, an entry is added into the error
|
||||||
|
* collector object.
|
||||||
|
*
|
||||||
|
* @param {object} data - the error collector object
|
||||||
|
* @param {string} container - the name of the entity that contains
|
||||||
|
* what we're checking
|
||||||
|
* @param {string} name - the name of the entity we're checking for
|
||||||
|
* @param {string} type - expected typename of the entity we're checking
|
||||||
|
* @param {object} obj - the object we're checking the fields of
|
||||||
|
* @return {boolean} true if the field exists and type is Ok
|
||||||
|
* false if an error was found and reported
|
||||||
|
*/
|
||||||
|
function checkExists(data, container, name, type, obj) {
|
||||||
|
if (obj[name] === undefined) {
|
||||||
|
data.errors.push({
|
||||||
|
txt: 'missing property in auth entity',
|
||||||
|
obj: {
|
||||||
|
entity: container,
|
||||||
|
property: name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return checkType(data, container, name, type, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkUser(data, userObj) {
|
||||||
|
if (checkExists(data, 'User', 'arn', 'string', userObj)) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
data.arns[userObj.arn] = incr(data.arns[userObj.arn]);
|
||||||
|
}
|
||||||
|
if (checkExists(data, 'User', 'email', 'string', userObj)) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
data.emails[userObj.email] = incr(data.emails[userObj.email]);
|
||||||
|
}
|
||||||
|
if (checkExists(data, 'User', 'keys', 'array', userObj)) {
|
||||||
|
userObj.keys.forEach(keyObj => {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
data.keys[keyObj.access] = incr(data.keys[keyObj.access]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAccount(data, accountObj, checkSas) {
|
||||||
|
if (checkExists(data, 'Account', 'email', 'string', accountObj)) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
data.emails[accountObj.email] = incr(data.emails[accountObj.email]);
|
||||||
|
}
|
||||||
|
if (checkExists(data, 'Account', 'arn', 'string', accountObj)) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
data.arns[accountObj.arn] = incr(data.arns[accountObj.arn]);
|
||||||
|
}
|
||||||
|
if (checkExists(data, 'Account', 'canonicalID', 'string', accountObj)) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
data.canonicalIds[accountObj.canonicalID] =
|
||||||
|
incr(data.canonicalIds[accountObj.canonicalID]);
|
||||||
|
}
|
||||||
|
if (checkSas &&
|
||||||
|
checkExists(data, 'Account', 'sasBlob', 'string', accountObj)) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
data.sasBlobs[accountObj.sasBlob] =
|
||||||
|
incr(data.sasBlobs[accountObj.sasBlob]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountObj.users) {
|
||||||
|
if (checkType(data, 'Account', 'users', 'array', accountObj)) {
|
||||||
|
accountObj.users.forEach(userObj => checkUser(data, userObj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountObj.keys) {
|
||||||
|
if (checkType(data, 'Account', 'keys', 'array', accountObj)) {
|
||||||
|
accountObj.keys.forEach(keyObj => {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
data.keys[keyObj.access] = incr(data.keys[keyObj.access]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dumpCountError(property, obj, log) {
|
||||||
|
let count = 0;
|
||||||
|
Object.keys(obj).forEach(key => {
|
||||||
|
if (obj[key] > 1) {
|
||||||
|
log.error('property should be unique', {
|
||||||
|
property,
|
||||||
|
value: key,
|
||||||
|
count: obj[key],
|
||||||
|
});
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dumpErrors(checkData, log) {
|
||||||
|
let nerr = dumpCountError('CanonicalID', checkData.canonicalIds, log);
|
||||||
|
nerr += dumpCountError('Email', checkData.emails, log);
|
||||||
|
nerr += dumpCountError('ARN', checkData.arns, log);
|
||||||
|
nerr += dumpCountError('AccessKey', checkData.keys, log);
|
||||||
|
nerr += dumpCountError('SAS Blob', checkData.sasBlobs, log);
|
||||||
|
|
||||||
|
if (checkData.errors.length > 0) {
|
||||||
|
checkData.errors.forEach(msg => {
|
||||||
|
log.error(msg.txt, msg.obj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkData.errors.length === 0 && nerr === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.fatal('invalid authentication config file (cannot start)');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} authdata - the authentication config file's data
|
||||||
|
* @param {object} loggerConfig - config for logger if needed to dump errors
|
||||||
|
* @param {string} loggerConfig.level - logger level
|
||||||
|
* @param {string} loggerConfig.dump - logger dump level
|
||||||
|
* @param {(boolean|null)} checkSas - whether to check Azure SAS for ea. account
|
||||||
|
* @return {boolean} true on erroneous data
|
||||||
|
* false on success
|
||||||
|
*/
|
||||||
|
function check(authdata, loggerConfig, checkSas) {
|
||||||
|
const checkData = {
|
||||||
|
errors: [],
|
||||||
|
emails: [],
|
||||||
|
arns: [],
|
||||||
|
canonicalIds: [],
|
||||||
|
keys: [],
|
||||||
|
sasBlobs: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (authdata.accounts === undefined) {
|
||||||
|
checkData.errors.push({
|
||||||
|
txt: 'no "accounts" array defined in Auth config',
|
||||||
|
});
|
||||||
|
return dumpErrors(checkData);
|
||||||
|
}
|
||||||
|
|
||||||
|
authdata.accounts.forEach(account => {
|
||||||
|
checkAccount(checkData, account, checkSas);
|
||||||
|
});
|
||||||
|
|
||||||
|
const log = new werelogs.Logger('S3', loggerConfig);
|
||||||
|
return dumpErrors(checkData, log);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = check;
|
|
@ -0,0 +1,165 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
module.exports = class Indexer {
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @param {object} authdata - the authentication config file's data
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
constructor(authdata) {
|
||||||
|
this.accountsBy = {
|
||||||
|
canId: {},
|
||||||
|
accessKey: {},
|
||||||
|
email: {},
|
||||||
|
};
|
||||||
|
this.usersBy = {
|
||||||
|
accessKey: {},
|
||||||
|
email: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This may happen if the file backend is not configured for S3.
|
||||||
|
* As such, we're managing the error here to avoid screwing up there.
|
||||||
|
*/
|
||||||
|
if (!authdata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._build(authdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
_indexUser(account, user) {
|
||||||
|
const userData = {
|
||||||
|
arn: account.arn,
|
||||||
|
canonicalID: account.canonicalID,
|
||||||
|
shortid: account.shortid,
|
||||||
|
accountDisplayName: account.accountDisplayName,
|
||||||
|
IAMdisplayName: user.name,
|
||||||
|
email: user.email.toLowerCase(),
|
||||||
|
keys: [],
|
||||||
|
};
|
||||||
|
this.usersBy.email[userData.email] = userData;
|
||||||
|
user.keys.forEach(key => {
|
||||||
|
userData.keys.push(key);
|
||||||
|
this.usersBy.accessKey[key.access] = userData;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_indexAccount(account) {
|
||||||
|
const accountData = {
|
||||||
|
arn: account.arn,
|
||||||
|
canonicalID: account.canonicalID,
|
||||||
|
shortid: account.shortid,
|
||||||
|
accountDisplayName: account.name,
|
||||||
|
email: account.email.toLowerCase(),
|
||||||
|
keys: [],
|
||||||
|
};
|
||||||
|
this.accountsBy.canId[accountData.canonicalID] = accountData;
|
||||||
|
this.accountsBy.email[accountData.email] = accountData;
|
||||||
|
if (account.keys !== undefined) {
|
||||||
|
account.keys.forEach(key => {
|
||||||
|
accountData.keys.push(key);
|
||||||
|
this.accountsBy.accessKey[key.access] = accountData;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (account.users !== undefined) {
|
||||||
|
account.users.forEach(user => {
|
||||||
|
this._indexUser(accountData, user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_build(authdata) {
|
||||||
|
authdata.accounts.forEach(account => {
|
||||||
|
this._indexAccount(account);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
if (this.accountsBy.accessKey.hasOwnProperty(key)) {
|
||||||
|
return this.accountsBy.accessKey[key];
|
||||||
|
}
|
||||||
|
return this.usersBy.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) {
|
||||||
|
const lowerCasedEmail = email.toLowerCase();
|
||||||
|
if (this.usersBy.email.hasOwnProperty(lowerCasedEmail)) {
|
||||||
|
return this.usersBy.email[lowerCasedEmail];
|
||||||
|
}
|
||||||
|
return this.accountsBy.email[lowerCasedEmail];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* @param {Object} entity - the entity object
|
||||||
|
* @returns {string} account display name
|
||||||
|
*/
|
||||||
|
getAcctDisplayName(entity) {
|
||||||
|
return entity.accountDisplayName;
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue