From 62682c8cd11f59267299cfa6a5278777dabb7003 Mon Sep 17 00:00:00 2001 From: David Pineau Date: Thu, 2 Jun 2016 20:10:32 +0200 Subject: [PATCH 1/7] [Auth](ft): Adding a new Auth config file format --- conf/authdata.json | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 conf/authdata.json diff --git a/conf/authdata.json b/conf/authdata.json new file mode 100644 index 0000000000..075d7cb77d --- /dev/null +++ b/conf/authdata.json @@ -0,0 +1,32 @@ +{ + "accounts": [{ + "name": "Bart", + "email": "sampleaccount1@sampling.com", + "arn": "aws::iam:123456789012:root", + "canonicalID": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be", + "shortid": "123456789012", + "keys": [{ + "access": "accessKey1", + "secret": "verySecretKey1" + }], + "users": [{ + "name": "Bart Jr", + "email": "user1.sampleaccount2@sampling.com", + "arn": "aws::iam:123456789013:bart", + "keys": [{ + "access": "accessKey1/1", + "secret": "verySecretKey1" + }] + }] + }, { + "name": "Lisa", + "email": "sampleaccount2@sampling.com", + "arn": "aws::iam:accessKey2:user/Lisa", + "canonicalID": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2bf", + "shortid": "123456789012", + "keys": [{ + "access": "accessKey2", + "secret": "verySecretKey2" + }] + }] +} From dd59606332989a82869783e35e92997f98e6147a Mon Sep 17 00:00:00 2001 From: David Pineau Date: Thu, 2 Jun 2016 20:10:50 +0200 Subject: [PATCH 2/7] [Auth](ft): Adding a format checker for the config --- lib/auth/in_memory/checker.js | 182 ++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 lib/auth/in_memory/checker.js diff --git a/lib/auth/in_memory/checker.js b/lib/auth/in_memory/checker.js new file mode 100644 index 0000000000..db96b94443 --- /dev/null +++ b/lib/auth/in_memory/checker.js @@ -0,0 +1,182 @@ +import Logger from 'werelogs'; + +// Here, we expect the logger to have already been configured in S3 +const log = new Logger('S3'); + +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) { + 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 (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) { + 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) { + let nerr = dumpCountError('CanonicalID', checkData.canonicalIds); + nerr += dumpCountError('Email', checkData.emails); + nerr += dumpCountError('ARN', checkData.arns); + nerr += dumpCountError('AccessKey', checkData.keys); + + 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 + * @return {boolean} true on erroneous data + * false on success + */ +export default function check(authdata) { + const checkData = { + errors: [], + emails: [], + arns: [], + canonicalIds: [], + keys: [], + }; + + 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); + }); + + return dumpErrors(checkData); +} From da0e8a1b3bfcae10caa4544fbddea3b5087d34a4 Mon Sep 17 00:00:00 2001 From: alexandre-merle Date: Thu, 9 Jun 2016 17:00:49 +0200 Subject: [PATCH 3/7] [Auth](ft/tests): Add unit tests for checker - Add unit tests for in-memory checker --- tests/unit/auth/checker.js | 134 +++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 tests/unit/auth/checker.js diff --git a/tests/unit/auth/checker.js b/tests/unit/auth/checker.js new file mode 100644 index 0000000000..7ad89e72a4 --- /dev/null +++ b/tests/unit/auth/checker.js @@ -0,0 +1,134 @@ +import assert from 'assert'; + +import checker from '../../../lib/auth/in_memory/checker'; +import ref from '../../../conf/authdata.json'; + +function getParentField(obj, field) { + const fields = field.split('.'); + let parent = obj; + for (let i = 0; i < fields.length - 1; ++i) { + const cur = fields[i]; + const n = Number(cur, 10); + if (isNaN(n)) { + parent = parent[cur]; + } else { + parent = parent[n]; + } + } + return parent; +} + +function getFieldName(field) { + return field.split('.').pop(); +} + +function shouldFail(obj, done) { + const res = checker(obj); + assert.strictEqual(res, true); + done(); +} + +function shouldSuccess(obj, done) { + const res = checker(obj); + assert.strictEqual(res, false); + done(); +} + +export const should = { + _exec: undefined, + missingField: (obj, field, done) => { + delete getParentField(obj, field)[getFieldName(field)]; + should._exec(obj, done); + }, + modifiedField: (obj, field, value, done) => { + getParentField(obj, field)[getFieldName(field)] = value; + should._exec(obj, done); + }, +}; + +describe('auth/in_memory/checker.js', () => { + let obj = {}; + + beforeEach(done => { + obj = JSON.parse(JSON.stringify(ref)); + done(); + }); + + // Each item will run a test who need to result in a failure + // format: + // - key: field to modify + // - value: if undefined, the field is removed + [ + ['accounts', undefined], + ['accounts.0.email', undefined], + ['accounts.0.email', 64], + ['accounts.0.arn', undefined], + ['accounts.0.arn', 64], + ['accounts.0.canonicalID', undefined], + ['accounts.0.canonicalID', 64], + ['accounts.0.users', 'not an object'], + ['accounts.0.users.0.arn', undefined], + ['accounts.0.users.0.arn', 64], + ['accounts.0.users.0.email', undefined], + ['accounts.0.users.0.email', 64], + ['accounts.0.users.0.keys', undefined], + ['accounts.0.users.0.keys', 'not an Array'], + ['accounts.0.keys', 'not an Array'], + ].forEach(test => { + if (test[1] === undefined) { + // Check a failure when deleting required fields + it(`should fail when missing field ${test[0]}`, done => { + should._exec = shouldFail; + should.missingField(obj, test[0], done); + }); + } else { + // Check a failure when the type of field is different than + // expected + it(`should fail when modified field ${test[0]}${test[1]}`, done => { + should._exec = shouldFail; + should.modifiedField(obj, test[0], test[1], done); + }); + } + }); + + // Each item will run a test who need to result in a success when missing + // optionals fields + // format: + // - key: field to modify + [ + 'accounts.0.keys', + 'accounts.0.users', + ].forEach(test => { + // Check a success when deleting optional fields + it(`should success when missing field ${test[0]}`, done => { + should._exec = shouldSuccess; + should.missingField(obj, test[0], done); + }); + }); + + it('Should return error on two same canonicalID', done => { + obj.accounts[0].canonicalID = obj.accounts[1].canonicalID; + shouldFail(obj, done); + }); + + it('Should return error on two same emails, account-account', done => { + obj.accounts[0].email = obj.accounts[1].email; + shouldFail(obj, done); + }); + + it('Should return error on two same emails account-user', done => { + obj.accounts[0].users[0].email = obj.accounts[1].email; + shouldFail(obj, done); + }); + + it('Should return error on two same arn', done => { + obj.accounts[0].arn = obj.accounts[0].users[0].arn; + shouldFail(obj, done); + }); + + it('Should return error on two same access key', done => { + obj.accounts[0].keys[0].access = + obj.accounts[0].users[0].keys[0].access; + shouldFail(obj, done); + }); +}); From f35e0ffa88f4cb5db2cfa0b38b7e6be37f6b35a7 Mon Sep 17 00:00:00 2001 From: David Pineau Date: Thu, 2 Jun 2016 20:11:09 +0200 Subject: [PATCH 4/7] [Auth](ft): Adding a simple indexer for the in-memory auth data --- lib/auth/in_memory/indexer.js | 145 ++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 lib/auth/in_memory/indexer.js diff --git a/lib/auth/in_memory/indexer.js b/lib/auth/in_memory/indexer.js new file mode 100644 index 0000000000..cad8524b0b --- /dev/null +++ b/lib/auth/in_memory/indexer.js @@ -0,0 +1,145 @@ +/** + * 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 { + /** + * @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.name, + 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 + */ + getByCanId(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 + */ + getByKey(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 + */ + getByEmail(email) { + const lowerCasedEmail = email.toLowerCase(); + if (this.usersBy.email.hasOwnProperty(lowerCasedEmail)) { + return this.usersBy.email[lowerCasedEmail]; + } + return this.accountsBy.email[lowerCasedEmail]; + } +} From a7ac54daede5d7289c7e6e9f779ffbf6bf7b5b0e Mon Sep 17 00:00:00 2001 From: alexandre-merle Date: Fri, 10 Jun 2016 03:22:19 +0200 Subject: [PATCH 5/7] [Auth](ft/tests): Add unit test for indexer - Add unit test for lib/auth/in_memory/index.js --- tests/unit/auth/indexer.js | 77 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 tests/unit/auth/indexer.js diff --git a/tests/unit/auth/indexer.js b/tests/unit/auth/indexer.js new file mode 100644 index 0000000000..3381bfd4df --- /dev/null +++ b/tests/unit/auth/indexer.js @@ -0,0 +1,77 @@ +import assert from 'assert'; + +import Indexer from '../../../lib/auth/in_memory/indexer'; +import ref from '../../../conf/authdata.json'; +import { should } from './checker'; + +describe('lib/auth/in_memory/index.js', () => { + let obj = {}; + let index = undefined; + + beforeEach(done => { + obj = JSON.parse(JSON.stringify(ref)); + index = new Indexer(obj); + done(); + }); + + it('Should return account from canonicalID', done => { + const res = index.getByCanId(obj.accounts[0].canonicalID); + assert.strictEqual(typeof res, 'object'); + assert.strictEqual(res.arn, obj.accounts[0].arn); + done(); + }); + + it('Should return account from email', done => { + const res = index.getByEmail(obj.accounts[1].email); + assert.strictEqual(typeof res, 'object'); + assert.strictEqual(res.canonicalID, obj.accounts[1].canonicalID); + done(); + }); + + it('Should return user from email', done => { + const res = index.getByEmail(obj.accounts[0].users[0].email); + assert.strictEqual(typeof res, 'object'); + assert.strictEqual(res.arn, obj.accounts[0].arn); + assert.strictEqual(res.IAMdisplayName, + obj.accounts[0].users[0].name); + done(); + }); + + it('Should return account from key', done => { + const res = index.getByKey(obj.accounts[0].keys[0].access); + assert.strictEqual(typeof res, 'object'); + assert.strictEqual(res.arn, obj.accounts[0].arn); + done(); + }); + + it('Should return user from key', done => { + const res = index.getByKey(obj.accounts[0].users[0].keys[0].access); + assert.strictEqual(typeof res, 'object'); + assert.strictEqual(res.arn, obj.accounts[0].arn); + assert.strictEqual(res.IAMdisplayName, + obj.accounts[0].users[0].name); + done(); + }); + + it('should index account without keys', done => { + should._exec = () => { + index = new Indexer(obj); + const res = index.getByEmail(obj.accounts[0].email); + assert.strictEqual(typeof res, 'object'); + assert.strictEqual(res.arn, obj.accounts[0].arn); + done(); + }; + should.missingField(obj, 'accounts.0.keys'); + }); + + it('should index account without users', done => { + should._exec = () => { + index = new Indexer(obj); + const res = index.getByEmail(obj.accounts[0].email); + assert.strictEqual(typeof res, 'object'); + assert.strictEqual(res.arn, obj.accounts[0].arn); + done(); + }; + should.missingField(obj, 'accounts.0.users'); + }); +}); From 3637fc75615ae28648be8cd31a8ca75117daf7e4 Mon Sep 17 00:00:00 2001 From: David Pineau Date: Fri, 3 Jun 2016 19:41:22 +0200 Subject: [PATCH 6/7] [Auth](ft): File backend: use new auth config file - Use new auth config file in Config.js - Define a new env variable to change the path of the authentication config file - Update backend.js to use the new authentication data API - Update tests to remove references to the old, inconsistent auth config file format. --- lib/Config.js | 10 +++- lib/auth/in_memory/backend.js | 43 ++++++++--------- lib/auth/in_memory/vault.json | 79 -------------------------------- tests/functional/s3curl/tests.js | 3 +- tests/unit/api/bucketDelete.js | 3 +- tests/unit/helpers.js | 15 ++++-- 6 files changed, 46 insertions(+), 107 deletions(-) delete mode 100644 lib/auth/in_memory/vault.json diff --git a/lib/Config.js b/lib/Config.js index 7bd2b5ff32..794084bde1 100644 --- a/lib/Config.js +++ b/lib/Config.js @@ -2,6 +2,8 @@ import assert from 'assert'; import fs from 'fs'; import path from 'path'; +import authDataChecker from './auth/in_memory/checker'; + /** * Reads from a config file and returns the content as a config object */ @@ -175,11 +177,15 @@ class Config { if (auth === 'file' || auth === 'mem') { // Auth only checks for 'mem' since mem === file auth = 'mem'; - let authfile = `${__dirname}/auth/in_memory/vault.json`; + let authfile = `${__dirname}/../conf/authdata.json`; if (process.env.S3AUTH_CONFIG) { authfile = process.env.S3AUTH_CONFIG; } - this.authData = require(authfile); + const authData = require(authfile); + if (authDataChecker(authData)) { + throw new Error('bad config: invalid auth config file.'); + } + this.authData = authData; } if (process.env.S3SPROXYD) { data = process.env.S3SPROXYD; diff --git a/lib/auth/in_memory/backend.js b/lib/auth/in_memory/backend.js index 1a6f67f420..bce72d5d1a 100644 --- a/lib/auth/in_memory/backend.js +++ b/lib/auth/in_memory/backend.js @@ -2,9 +2,12 @@ import { errors } from 'arsenal'; import crypto from 'crypto'; import config from '../../Config'; +import Indexer from './indexer'; import { calculateSigningKey, hashSignature } from './vaultUtilities'; +const authIndex = new Indexer(config.authData); + const backend = { /** verifySignatureV2 * @param {string} stringToSign - string to sign built per AWS rules @@ -16,21 +19,22 @@ const backend = { */ verifySignatureV2: (stringToSign, signatureFromRequest, accessKey, options, callback) => { - const account = config.authData.accountsKeyedbyAccessKey[accessKey]; - if (!account) { + const entity = authIndex.getByKey(accessKey); + if (!entity) { return callback(errors.InvalidAccessKeyId); } - const secretKey = account.secretKey; + const secretKey = entity.keys + .filter(kv => kv.access === accessKey)[0].secret; const reconstructedSig = hashSignature(stringToSign, secretKey, options.algo); if (signatureFromRequest !== reconstructedSig) { return callback(errors.SignatureDoesNotMatch); } const userInfoToSend = { - accountDisplayName: account.displayName, - canonicalID: account.canonicalID, - arn: account.arn, - IAMdisplayName: account.IAMdisplayName, + accountDisplayName: entity.accountDisplayName, + canonicalID: entity.canonicalID, + arn: entity.arn, + IAMdisplayName: entity.IAMdisplayName, }; const vaultReturnObject = { message: { @@ -54,11 +58,12 @@ const backend = { */ verifySignatureV4: (stringToSign, signatureFromRequest, accessKey, region, scopeDate, options, callback) => { - const account = config.authData.accountsKeyedbyAccessKey[accessKey]; - if (!account) { + const entity = authIndex.getByKey(accessKey); + if (!entity) { return callback(errors.InvalidAccessKeyId); } - const secretKey = account.secretKey; + const secretKey = entity.keys + .filter(kv => kv.access === accessKey)[0].secret; const signingKey = calculateSigningKey(secretKey, region, scopeDate); const reconstructedSig = crypto.createHmac('sha256', signingKey) .update(stringToSign).digest('hex'); @@ -66,10 +71,10 @@ const backend = { return callback(errors.SignatureDoesNotMatch); } const userInfoToSend = { - accountDisplayName: account.displayName, - canonicalID: account.canonicalID, - arn: account.arn, - IAMdisplayName: account.IAMdisplayName, + accountDisplayName: entity.accountDisplayName, + canonicalID: entity.canonicalID, + arn: entity.arn, + IAMdisplayName: entity.IAMdisplayName, }; const vaultReturnObject = { message: { @@ -92,13 +97,10 @@ const backend = { getCanonicalIds: (emails, log, cb) => { const results = {}; emails.forEach(email => { - const lowercasedEmail = email.toLowerCase(); - if (!config.authData.accountsKeyedbyEmail[lowercasedEmail]) { + if (!authIndex.getByEmail(email)) { results[email] = 'NotFound'; } else { - results[email] = - config.authData.accountsKeyedbyEmail[lowercasedEmail] - .canonicalID; + results[email] = authIndex.getByEmail(email).canonicalID; } }); const vaultReturnObject = { @@ -123,8 +125,7 @@ const backend = { getEmailAddresses: (canonicalIDs, options, cb) => { const results = {}; canonicalIDs.forEach(canonicalId => { - const foundAccount = config.authData - .accountsKeyedbyCanID[canonicalId]; + const foundAccount = authIndex.getByCanId(canonicalId); if (!foundAccount || !foundAccount.email) { results[canonicalId] = 'NotFound'; } else { diff --git a/lib/auth/in_memory/vault.json b/lib/auth/in_memory/vault.json deleted file mode 100644 index 7b27316901..0000000000 --- a/lib/auth/in_memory/vault.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "accountsKeyedbyAccessKey": { - "accessKey1": { - "arn": "aws::iam:accessKey1:user/Bart", - "IAMdisplayName": "Bart", - "secretKey": "verySecretKey1", - "canonicalID": "accessKey1canonicalID", - "displayName": "accessKey1displayName" - }, - "accessKey2": { - "arn": "aws::iam:accessKey2:user/Lisa", - "IAMdisplayName": "Lisa", - "secretKey": "verySecretKey2", - "canonicalID": "accessKey2canonicalID", - "displayName": "accessKey2displayName" - } - }, - - "accountsKeyedbyEmail": { - "sampleaccount1@sampling.com": { - "arn": "aws::iam:123456789012:root", - "createDate": "", - "saltedPwd": "", - "pwdlastUsed": "", - "pwdCreated": "", - "name": "", - "shortid": "123456789012", - "email": "sampleaccount1@sampling.com", - "canonicalID": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be", - "secretKeyIdList": [], - "aliasList": [], - "oidcpdList": [] - }, - "sampleaccount2@sampling.com": { - "arn": "aws::iam:321456789012:root", - "createDate": "", - "saltedPwd": "", - "pwdlastUsed": "", - "pwdCreated": "", - "name": "", - "shortid": "321456789012", - "email": "sampleaccount2@sampling.com", - "canonicalID": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2bf", - "secretKeyIdList": [], - "aliasList": [], - "oidcpdList": [] - } - }, - "accountsKeyedbyCanID": { - "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be": { - "arn": "aws::iam:123456789012:root", - "createDate": "", - "saltedPwd": "", - "pwdlastUsed": "", - "pwdCreated": "", - "name": "", - "shortid": "123456789012", - "email": "sampleaccount1@sampling.com", - "canonicalID": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be", - "secretKeyIdList": [], - "aliasList": [], - "oidcpdList": [] - }, - "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2bf": { - "arn": "aws::iam:321456789012:root", - "createDate": "", - "saltedPwd": "", - "pwdlastUsed": "", - "pwdCreated": "", - "name": "", - "shortid": "321456789012", - "email": "sampleaccount2@sampling.com", - "canonicalID": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2bf", - "secretKeyIdList": [], - "aliasList": [], - "oidcpdList": [] - } - } -} diff --git a/tests/functional/s3curl/tests.js b/tests/functional/s3curl/tests.js index d4aebd7024..2229ad584d 100644 --- a/tests/functional/s3curl/tests.js +++ b/tests/functional/s3curl/tests.js @@ -23,7 +23,8 @@ const aclBucket = 'acluniverse'; const nonexist = 'nonexist'; const prefix = 'topLevel'; const delimiter = '/'; -const ownerCanonicalId = 'accessKey1canonicalID'; +const ownerCanonicalId = '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d52' + + '18e7cd47ef2be'; const endpoint = `${transport}://${ipAddress}:8000`; function diff(putFile, receivedFile, done) { diff --git a/tests/unit/api/bucketDelete.js b/tests/unit/api/bucketDelete.js index 103a2adc96..07b89a35d2 100644 --- a/tests/unit/api/bucketDelete.js +++ b/tests/unit/api/bucketDelete.js @@ -47,7 +47,8 @@ describe('bucketDelete API', () => { assert.deepStrictEqual(err, errors.BucketNotEmpty); metadata.getBucket(bucketName, log, (err, md) => { assert.strictEqual(md.getName(), bucketName); - metadata.listObject(usersBucket, canonicalID, + metadata.listObject(usersBucket, + authInfo.getCanonicalID(), null, null, null, log, (err, listResponse) => { assert.strictEqual(listResponse.Contents.length, 1); diff --git a/tests/unit/helpers.js b/tests/unit/helpers.js index cf875357e8..bd18e72114 100644 --- a/tests/unit/helpers.js +++ b/tests/unit/helpers.js @@ -21,7 +21,9 @@ export function shuffle(array) { array.forEach((item, currentIndex, array) => { randomIndex = Math.floor(Math.random() * length); temporaryValue = array[currentIndex]; + // eslint-disable-next-line no-param-reassign array[currentIndex] = array[randomIndex]; + // eslint-disable-next-line no-param-reassign array[randomIndex] = temporaryValue; }); return array; @@ -36,10 +38,17 @@ export function timeDiff(startTime) { } export function makeAuthInfo(accessKey) { - const canonicalID = accessKey === constants.publicId ? - constants.publicId : `${accessKey}canonicalID`; + const canIdMap = { + accessKey1: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7' + + 'cd47ef2be', + accessKey2: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7' + + 'cd47ef2bf', + default: `${accessKey}canonicalID`, + }; + canIdMap[constants.publicId] = constants.publicId; + return new AuthInfo({ - canonicalID, + canonicalID: canIdMap[accessKey] || canIdMap.default, shortid: 'shortid', email: `${accessKey}@l.com`, accountDisplayName: `${accessKey}displayName`, From c4b70e9cfbb71c3938c38894d75e29518990da79 Mon Sep 17 00:00:00 2001 From: David Pineau Date: Sun, 26 Jun 2016 20:21:48 +0200 Subject: [PATCH 7/7] Allow overriding the ownerCanonicalId in s3curl tests through env --- tests/functional/s3curl/tests.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/functional/s3curl/tests.js b/tests/functional/s3curl/tests.js index 2229ad584d..dc4b20acb3 100644 --- a/tests/functional/s3curl/tests.js +++ b/tests/functional/s3curl/tests.js @@ -23,10 +23,22 @@ const aclBucket = 'acluniverse'; const nonexist = 'nonexist'; const prefix = 'topLevel'; const delimiter = '/'; -const ownerCanonicalId = '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d52' +let ownerCanonicalId = '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d52' + '18e7cd47ef2be'; const endpoint = `${transport}://${ipAddress}:8000`; +/* + * XXX TODO FIXME TODO XXX + * The following codeblock aims at improving flexibility of this tests by + * overriding some specific test values from the environment. This is partly + * aimed at re-using this test suite in a different context (end-to-end + * testing rather than functional testing) + * XXX TODO FIXME TODO XXX + */ +if (process.env.S3_TESTVAL_OWNERCANONICALID) { + ownerCanonicalId = process.env.S3_TESTVAL_OWNERCANONICALID; +} + function diff(putFile, receivedFile, done) { process.stdout.write(`diff ${putFile} ${receivedFile}\n`); proc.spawn('diff', [putFile, receivedFile]).on('exit', code => {