diff --git a/lib/api/api.js b/lib/api/api.js index 23039807da..6b133251c1 100644 --- a/lib/api/api.js +++ b/lib/api/api.js @@ -152,8 +152,6 @@ const api = { log.trace('get object authorization denial from Vault'); return errors.AccessDenied; } - // TODO add support for returnTagCount in the bucket policy - // checks isImplicitDeny[authResults[0].action] = authResults[0].isImplicit; // second item checks s3:GetObject(Version)Tagging action if (!authResults[1].isAllowed) { @@ -281,6 +279,9 @@ const api = { sourceObject, sourceVersionId, log, callback); } if (apiMethod === 'objectGet') { + // remove objectGetTagging/objectGetTaggingVersion from apiMethods, these were added by + // prepareRequestContexts to determine the value of returnTagCount. + request.apiMethods = request.apiMethods.filter(methodName => !methodName.includes('Tagging')); return this[apiMethod](userInfo, request, returnTagCount, log, callback); } return this[apiMethod](userInfo, request, log, callback); diff --git a/lib/api/apiUtils/authorization/permissionChecks.js b/lib/api/apiUtils/authorization/permissionChecks.js index ad6bdee50d..4ebed1f326 100644 --- a/lib/api/apiUtils/authorization/permissionChecks.js +++ b/lib/api/apiUtils/authorization/permissionChecks.js @@ -14,6 +14,20 @@ const { const publicReadBuckets = process.env.ALLOW_PUBLIC_READ_BUCKETS ? process.env.ALLOW_PUBLIC_READ_BUCKETS.split(',') : []; +// WARNING: enum order matters DO NOT change. +const checkPrincipalResult = Object.freeze({ + KO: 0, + CROSS_ACCOUNT_OK: 1, + OK: 2, +}); + +const checkBucketPolicyResult = Object.freeze({ + DEFAULT_DENY: 0, + EXPLICIT_DENY: 1, + ALLOW: 2, + CROSS_ACCOUNT_ALLOW: 3, +}); + /** * Checks the access control for a given bucket based on the request type and user's canonical ID. * @@ -117,7 +131,7 @@ function checkBucketAcls(bucket, requestType, canonicalID, mainApiCall) { // authorization check should just return true so can move on to check // rights at the object level. return (requestTypeParsed === 'objectPutACL' || requestTypeParsed === 'objectGetACL' - || requestTypeParsed === 'objectGet' || requestTypeParsed === 'objectHead'); + || requestTypeParsed === 'objectGet' || requestTypeParsed === 'objectHead'); } function checkObjectAcls(bucket, objectMD, requestType, canonicalID, requesterIsNotUser, @@ -265,67 +279,206 @@ function _isAccountId(principal) { return (principal.length === 12 && /^\d+$/.test(principal)); } -function _checkPrincipal(requester, principal) { - if (principal === '*') { - return true; +/** + * Checks if the ARN represents a root user account + * @param {string} arn - The ARN to check + * @returns {boolean} True if root user, false otherwise + */ +function _isRootUser(arn) { + if (!arn) { + return false; } - // User in unauthenticated (anonymous request) - if (requester === undefined) { + + // Vault returns the following arn when the account makes requests 'arn:aws:iam::123456789012:/accountName/', + // with an empty resource type ('user/' prefix missing). + const arns = arn.split(':'); + if (arns.length < 6) { return false; } - if (principal === requester) { + + const resource = arns[arns.length - 1]; + + // If we start with '/' is because we have a empty resource type so we know it is a root account. + if (resource.startsWith('/')) { return true; } - if (_isAccountId(principal)) { - return _getAccountId(requester) === principal; + + return false; +} + +/** _evaluateCrossAccount - checks if it is a cross-account request. + * @param {string} requesterARN - requester ARN + * @param {string} requesterCanonicalID - requester canonical ID + * @param {string} bucketOwnerCanonicalID - bucket owner canonical ID + * @return {checkPrincipalResult} OK if it is not cross-account, CROSS_ACCOUNT_OK otherwise. + */ +function _checkCrossAccount(requesterARN, requesterCanonicalID, bucketOwnerCanonicalID) { + // Vault returns ARNs like 'arn:aws:iam::123456789012:/accountName/' for root accounts + // with an empty resource type (missing 'user/' prefix) + if (!_isRootUser(requesterARN)) { + return bucketOwnerCanonicalID === requesterCanonicalID ? + checkPrincipalResult.OK : checkPrincipalResult.CROSS_ACCOUNT_OK; } - if (principal.endsWith('root')) { - return _getAccountId(requester) === _getAccountId(principal); + + return checkPrincipalResult.OK; +} + +function _checkPrincipalWildcard(requestARN, requesterCanonicalID, bucketOwnerCanonicalID) { + if (requestARN === undefined) { // User in unauthenticated (anonymous request) + return checkPrincipalResult.OK; } - return false; + + return _checkCrossAccount(requestARN, requesterCanonicalID, bucketOwnerCanonicalID); } -function _checkPrincipals(canonicalID, arn, principal) { +function _checkPrincipalAWS(principal, requesterARN, requesterCanonicalID, bucketOwnerCanonicalID) { if (principal === '*') { - return true; + return _checkPrincipalWildcard(requesterARN, requesterCanonicalID, bucketOwnerCanonicalID); } - if (principal.CanonicalUser) { - if (Array.isArray(principal.CanonicalUser)) { - return principal.CanonicalUser.some(p => _checkPrincipal(canonicalID, p)); + + if (requesterARN === undefined) { // User in unauthenticated (anonymous request) + return checkPrincipalResult.KO; + } + + if (principal === requesterARN) { + return _checkCrossAccount(requesterARN, requesterCanonicalID, bucketOwnerCanonicalID); + } + + if (_isAccountId(principal) && principal === _getAccountId(requesterARN)) { + return _checkCrossAccount(requesterARN, requesterCanonicalID, bucketOwnerCanonicalID); + } + + if (principal.endsWith(':root') && _getAccountId(principal) === _getAccountId(requesterARN)) { + return _checkCrossAccount(requesterARN, requesterCanonicalID, bucketOwnerCanonicalID); + } + + return checkPrincipalResult.KO; +} + +function _checkPrincipalCanonicalUser(principal, requesterARN, requesterCanonicalID, bucketOwnerCanonicalID) { + if (principal === '*') { + return _checkPrincipalWildcard(requesterARN, requesterCanonicalID, bucketOwnerCanonicalID); + } + + if (requesterARN === undefined) { // User in unauthenticated (anonymous request) + return checkPrincipalResult.KO; + } + + if (principal === requesterCanonicalID) { + return _checkCrossAccount(requesterARN, requesterCanonicalID, bucketOwnerCanonicalID); + } + + return checkPrincipalResult.KO; +} + +function _findBestPrincipalMatch(principalArray, checkFunc) { + let bestMatch = checkPrincipalResult.KO; + if (!principalArray) { + return bestMatch; + } + + const principals = Array.isArray(principalArray) ? principalArray : [principalArray]; + + // eslint-disable-next-line no-restricted-syntax + for (const p of principals) { + const result = checkFunc(p); + if (result === checkPrincipalResult.OK) { + return checkPrincipalResult.OK; // Highest permission, can exit early + } + if (result > bestMatch) { + bestMatch = result; } - return _checkPrincipal(canonicalID, principal.CanonicalUser); } + + return bestMatch; +} + +function _checkPrincipals(canonicalID, arn, principal, bucketOwnerCanonicalID) { + if (principal === '*') { + return _checkPrincipalWildcard(arn, canonicalID, bucketOwnerCanonicalID); + } + + if (principal.CanonicalUser) { + return _findBestPrincipalMatch(principal.CanonicalUser, + p => _checkPrincipalCanonicalUser(p, arn, canonicalID, bucketOwnerCanonicalID)); + } + if (principal.AWS) { - if (Array.isArray(principal.AWS)) { - return principal.AWS.some(p => _checkPrincipal(arn, p)); - } - return _checkPrincipal(arn, principal.AWS); + return _findBestPrincipalMatch(principal.AWS, + p => _checkPrincipalAWS(p, arn, canonicalID, bucketOwnerCanonicalID)); } - return false; + + return checkPrincipalResult.KO; } +// checkBucketPolicy Finite State Machine. +// ┌───────────────────────────┐ +// │ ▼ +// │ ┌───────┐ +// │ ┌────────►│ ALLOW ├──────────────┐ +// │ ┌─────┐ │ └──┬────┘ │ +// │ │START│ │ │ │ +// │ └──┬──┘ │ │ │ +// │ │ │ │ │ +// │ ▼ │ ▼ ▼ +// │┌──────────────┤ ┌────┐ ┌─────┐ +// ││ DEFAULT_DENY ├─────────►│DENY├────────────►│ END │ +// │└──────┬───────┤ └────┘ └─────┘ +// │ │ │ ▲ ▲ ▲ +// │ │ │ │ │ │ +// │ │ │ │ │ │ +// │ │ │ ┌───┴───────────┐ │ │ +// │ │ └────────►│ CROSS_ACCOUNT ├──────┘ │ +// │ │ └┬──────────────┘ │ +// └───────┼──────────────────┘ │ +// └──────────────────────────────────────────┘ +// function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, log, request, actionImplicitDenies) { - let permission = 'defaultDeny'; + let permission = checkBucketPolicyResult.DEFAULT_DENY; // if requester is user within bucket owner account, actions should be // allowed unless explicitly denied (assumes allowed by IAM policy) if (bucketOwner === canonicalID && actionImplicitDenies[requestType] === false) { - permission = 'allow'; + permission = checkBucketPolicyResult.ALLOW; } let copiedStatement = JSON.parse(JSON.stringify(policy.Statement)); while (copiedStatement.length > 0) { const s = copiedStatement[0]; - const principalMatch = _checkPrincipals(canonicalID, arn, s.Principal); + const principalMatch = _checkPrincipals(canonicalID, arn, s.Principal, bucketOwner); const actionMatch = _checkBucketPolicyActions(requestType, s.Action, log); const resourceMatch = _checkBucketPolicyResources(request, s.Resource, log); const conditionsMatch = _checkBucketPolicyConditions(request, s.Condition, log); - if (principalMatch && actionMatch && resourceMatch && conditionsMatch && s.Effect === 'Deny') { - // explicit deny trumps any allows, so return immediately - return 'explicitDeny'; - } - if (principalMatch && actionMatch && resourceMatch && conditionsMatch && s.Effect === 'Allow') { - permission = 'allow'; + const ok = principalMatch === checkPrincipalResult.OK && actionMatch && resourceMatch && conditionsMatch; + const okCross = principalMatch === checkPrincipalResult.CROSS_ACCOUNT_OK + && actionMatch && resourceMatch && conditionsMatch; + switch (permission) { + case checkBucketPolicyResult.DEFAULT_DENY: + if ((ok || okCross) && s.Effect === 'Deny') { + return checkBucketPolicyResult.EXPLICIT_DENY; + } else if (ok && s.Effect === 'Allow') { + permission = checkBucketPolicyResult.ALLOW; + } else if (okCross && s.Effect === 'Allow') { + permission = checkBucketPolicyResult.CROSS_ACCOUNT_ALLOW; + } + break; + case checkBucketPolicyResult.EXPLICIT_DENY: + return checkBucketPolicyResult.EXPLICIT_DENY; + case checkBucketPolicyResult.ALLOW: + if ((ok || okCross) && s.Effect === 'Deny') { + return checkBucketPolicyResult.EXPLICIT_DENY; + } + break; + case checkBucketPolicyResult.CROSS_ACCOUNT_ALLOW: + if ((ok || okCross) && s.Effect === 'Deny') { + return checkBucketPolicyResult.EXPLICIT_DENY; + } else if (ok && s.Effect === 'Allow') { + permission = checkBucketPolicyResult.ALLOW; + } + break; + default: // Needed for the linter, should be unreachable. + break; } + copiedStatement = copiedStatement.splice(1); } return permission; @@ -341,9 +494,13 @@ function processBucketPolicy(requestType, bucket, canonicalID, arn, bucketOwner, const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType, canonicalID, arn, bucketOwner, log, request, actionImplicitDenies); - if (bucketPolicyPermission === 'explicitDeny') { + if (bucketPolicyPermission === checkBucketPolicyResult.EXPLICIT_DENY) { processedResult = false; - } else if (bucketPolicyPermission === 'allow') { + } else if (bucketPolicyPermission === checkBucketPolicyResult.ALLOW) { + processedResult = true; + } else if (bucketPolicyPermission === checkBucketPolicyResult.CROSS_ACCOUNT_ALLOW + && actionImplicitDenies[requestType] === false) { + // If the bucket policy is cross account, only return true if Vault also returned an explicit allow. processedResult = true; } else { processedResult = actionImplicitDenies[requestType] === false && aclPermission; @@ -405,7 +562,7 @@ function evaluateBucketPolicyWithIAM(bucket, requestTypesInput, canonicalID, aut arn = authInfo.getArn(); } return processBucketPolicy(_requestType, bucket, canonicalID, arn, bucket.getOwner(), log, - request, true, results, actionImplicitDenies); + request, true, results, actionImplicitDenies); }); } @@ -456,8 +613,8 @@ function isObjAuthorized(bucket, objectMD, requestTypesInput, canonicalID, authI // - account is the bucket owner // - requester is account, not user if (bucketOwnerActions.includes(parsedMethodName) - && (bucketOwner === canonicalID) - && requesterIsNotUser) { + && (bucketOwner === canonicalID) + && requesterIsNotUser) { results[_requestType] = actionImplicitDenies[_requestType] === false; return results[_requestType]; } @@ -613,4 +770,6 @@ module.exports = { validatePolicyConditions, isLifecycleSession, evaluateBucketPolicyWithIAM, + checkBucketPolicy, + checkBucketPolicyResult, }; diff --git a/lib/api/objectGet.js b/lib/api/objectGet.js index afd68f11dd..c7860b1f5a 100644 --- a/lib/api/objectGet.js +++ b/lib/api/objectGet.js @@ -51,6 +51,7 @@ function objectGet(authInfo, request, returnTagCount, log, callback) { getDeleteMarker: true, requestType: request.apiMethods || 'objectGet', request, + returnTagCount, }; return standardMetadataValidateBucketAndObj(mdValParams, request.actionImplicitDenies, log, @@ -97,7 +98,8 @@ function objectGet(authInfo, request, returnTagCount, log, callback) { return callback(headerValResult.error, null, corsHeaders); } const responseMetaHeaders = collectResponseHeaders(objMD, - corsHeaders, verCfg, returnTagCount); + corsHeaders, verCfg, + returnTagCount && objMD.returnTagCount); // IAM and Bucket policy should both authorize tagging. setExpirationHeaders(responseMetaHeaders, { lifecycleConfig: bucket.getLifecycleConfiguration(), diff --git a/lib/metadata/metadataUtils.js b/lib/metadata/metadataUtils.js index 16c1a1f91f..953ff97689 100644 --- a/lib/metadata/metadataUtils.js +++ b/lib/metadata/metadataUtils.js @@ -230,13 +230,33 @@ function standardMetadataValidateBucketAndObj(params, actionImplicitDenies, log, return next(null, bucket, objMD); }, (bucket, objMD, next) => { + const objMetadata = objMD; const canonicalID = authInfo.getCanonicalID(); - if (!isObjAuthorized(bucket, objMD, requestType, canonicalID, authInfo, log, request, + if (!isObjAuthorized(bucket, objMetadata, requestType, canonicalID, authInfo, log, request, actionImplicitDenies)) { log.debug('access denied for user on object', { requestType }); return next(errors.AccessDenied, bucket); } - return next(null, bucket, objMD); + + if (!objMetadata) { + return next(null, bucket, objMetadata); + } + + let returnTagCount = false; + if (params.returnTagCount) { + // If returnTagCount is true we know that Vault authorized the request so it is not an implicitDeny. + const implicitDeny = false; + if (requestType.some(r => r === 'objectGet')) { + returnTagCount = isObjAuthorized(bucket, objMetadata, ['objectGetTagging'], canonicalID, authInfo, + log, request, implicitDeny); + } else if (requestType.some(r => r === 'objectGetVersion')) { + returnTagCount = isObjAuthorized(bucket, objMetadata, ['objectGetTaggingVersion'], + canonicalID, authInfo, log, request, implicitDeny); + } + + objMetadata.returnTagCount = returnTagCount; + } + return next(null, bucket, objMetadata); }, ], (err, bucket, objMD) => { if (err) { diff --git a/tests/unit/api/apiUtils/permissionChecks.js b/tests/unit/api/apiUtils/permissionChecks.js index 5a99f677d1..8d059b9206 100644 --- a/tests/unit/api/apiUtils/permissionChecks.js +++ b/tests/unit/api/apiUtils/permissionChecks.js @@ -1,37 +1,40 @@ const assert = require('assert'); -const { isLifecycleSession } = - require('../../../../lib/api/apiUtils/authorization/permissionChecks.js'); - -const tests = [ - { - arn: 'arn:aws:sts::257038443293:assumed-role/rolename/backbeat-lifecycle', - description: 'a role assumed by lifecycle service', - expectedResult: true, - }, - { - arn: undefined, - description: 'undefined', - expectedResult: false, - }, - { - arn: '', - description: 'empty', - expectedResult: false, - }, - { - arn: 'arn:aws:iam::257038443293:user/bart', - description: 'a user', - expectedResult: false, - }, - { - arn: 'arn:aws:sts::257038443293:assumed-role/rolename/other-service', - description: 'a role assumed by another service', - expectedResult: false, - }, -]; +const { isLifecycleSession, checkBucketPolicyResult, checkBucketPolicy } = + require('../../../../lib/api/apiUtils/authorization/permissionChecks.js'); +const { DummyRequestLogger } = require('../../helpers'); + +const stubLog = new DummyRequestLogger(); describe('authInfoHelper', () => { + const tests = [ + { + arn: 'arn:aws:sts::257038443293:assumed-role/rolename/backbeat-lifecycle', + description: 'a role assumed by lifecycle service', + expectedResult: true, + }, + { + arn: undefined, + description: 'undefined', + expectedResult: false, + }, + { + arn: '', + description: 'empty', + expectedResult: false, + }, + { + arn: 'arn:aws:iam::257038443293:user/bart', + description: 'a user', + expectedResult: false, + }, + { + arn: 'arn:aws:sts::257038443293:assumed-role/rolename/other-service', + description: 'a role assumed by another service', + expectedResult: false, + }, + ]; + tests.forEach(t => { it(`should return ${t.expectedResult} if arn is ${t.description}`, () => { const result = isLifecycleSession(t.arn); @@ -39,3 +42,669 @@ describe('authInfoHelper', () => { }); }); }); + +describe('checkBucketPolicy Principal logic', () => { + /* eslint-disable max-len */ + const tests = [ + { + description: 'bucket owner with same canonicalID as requesters should return ALLOW', + policy: { Statement: [] }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: '', + bucketOwner: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + log: null, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.ALLOW, + }, + { + description: 'bucket owner with different canonicalID as requesters should return DEFAULT_DENY', + policy: { Statement: [] }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: '', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: null, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.DEFAULT_DENY, + }, + { + description: 'bucket owner and requester share the same account, Allow policy should return ALLOW', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { + AWS: 'arn:aws:iam::123456789012:root', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.ALLOW, + }, + { + description: 'bucket owner and requester share the same account, principal account ID, Allow policy should return ALLOW', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { + AWS: '123456789012', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.ALLOW, + }, + { + description: 'bucket owner and requester share the same account, Deny policy should return DENY', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Deny', + Principal: { + AWS: 'arn:aws:iam::123456789012:root', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.EXPLICIT_DENY, + }, + { + description: 'bucket owner and requester don\'t share the same account, Allow policy should return CROSS ACCOUNT', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { + AWS: 'arn:aws:iam::123456789012:root', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.CROSS_ACCOUNT_ALLOW, + }, + { + description: 'bucket owner and requester don\'t share the same account, principal account ID, Allow policy should return CROSS ACCOUNT', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { + AWS: '123456789012', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.CROSS_ACCOUNT_ALLOW, + }, + { + description: 'bucket owner and requester don\'t share the same account, Deny policy should return DENY', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Deny', + Principal: { + AWS: 'arn:aws:iam::123456789012:root', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.EXPLICIT_DENY, + }, + { + description: 'bucket owner and requester don\'t share the same account, requester is root, Allow policy should return ALLOW', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { + AWS: 'arn:aws:iam::123456789012:root', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:/accountName/', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.ALLOW, + }, + { + description: 'bucket owner and requester don\'t share the same account, requester is root, Deny policy should return DENY', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Deny', + Principal: { + AWS: 'arn:aws:iam::123456789012:root', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:/accountName/', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.EXPLICIT_DENY, + }, + { + description: 'bucket owner and requester don\'t share the same account, requester and principal are users, Allow policy should return CROSS_ACCOUNT', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { + AWS: 'arn:aws:iam::123456789012:user/testuser', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.CROSS_ACCOUNT_ALLOW, + }, + { + description: 'bucket owner and requester don\'t share the same account, requester and principal are users, Deny policy should return EXPLICIT_DENY', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Deny', + Principal: { + AWS: 'arn:aws:iam::123456789012:user/testuser', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.EXPLICIT_DENY, + }, + { + description: 'bucket owner and requester don\'t share the same account, wildcard "*" principal, Allow policy should return CROSS_ACCOUNT_ALLOW', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { + AWS: '*', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.CROSS_ACCOUNT_ALLOW, + }, + { + description: 'bucket owner and requester share the same account, wildcard "*" principal, Allow policy should return ALLOW', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { + AWS: '*', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.ALLOW, + }, + { + description: 'bucket owner and requester share the same account, wildcard "*" principal, string typeof principal , Allow policy should return ALLOW', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: '*', + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.ALLOW, + }, + { + description: 'bucket owner and requester don\'t share the same account, wildcard "*" principal, string typeof principal, Allow policy should return CROSS_ACCOUNT_ALLOW', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: '*', + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.CROSS_ACCOUNT_ALLOW, + }, + { + description: 'bucket owner and requester don\'t share the same account, no bucket policy for user, canonical user principal, Allow policy should return DEFAULT_DENY', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { + CanonicalUser: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.DEFAULT_DENY, + }, + { + description: 'bucket owner and requester don\'t share the same account, canonical user principal, Allow policy should return CROSS_ACCOUNT_ALLOW', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { + CanonicalUser: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.CROSS_ACCOUNT_ALLOW, + }, + { + description: 'first policy allows second denies, should return EXPLICIT_DENY', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { + CanonicalUser: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + { + Sid: 'Example permissions', + Effect: 'Deny', + Principal: { + CanonicalUser: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.EXPLICIT_DENY, + }, + { + description: 'first policy denies second allows, should return EXPLICIT_DENY', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Deny', + Principal: { + CanonicalUser: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { + CanonicalUser: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be', + arn: 'arn:aws:iam::123456789012:user/testuser', + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.EXPLICIT_DENY, + }, + { + description: 'anonymous user and wildcard policy should return Allow', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: '*', + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: undefined, + arn: undefined, + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.ALLOW, + }, + { + description: 'anonymous user and wildcard AWS policy should return Allow', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { AWS: '*' }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: undefined, + arn: undefined, + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.ALLOW, + }, + { + description: 'anonymous user and wildcard CanonicalUser policy should return Allow', + policy: { + Statement: [ + { + Sid: 'Example permissions', + Effect: 'Allow', + Principal: { CanonicalUser: '*' }, + Action: [ + 's3:*', + ], + Resource: [ + 'arn:aws:s3:::amzn-s3-demo-bucket', + ], + }, + ], + }, + requestType: 'bucketGet', + canonicalID: undefined, + arn: undefined, + bucketOwner: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + log: stubLog, + request: null, + actionImplicitDenies: { bucketGet: false }, + + expectedResult: checkBucketPolicyResult.ALLOW, + }, + ]; + /* eslint-enable max-len */ + + tests.forEach(t => { + it(t.description, () => { + const res = checkBucketPolicy(t.policy, t.requestType, t.canonicalID, t.arn, t.bucketOwner, + t.log, t.request, t.actionImplicitDenies); + assert.equal(res, t.expectedResult); + }); + }); +}); diff --git a/tests/unit/api/bucketPolicyAuth.js b/tests/unit/api/bucketPolicyAuth.js index 428e9fb954..258bd1f540 100644 --- a/tests/unit/api/bucketPolicyAuth.js +++ b/tests/unit/api/bucketPolicyAuth.js @@ -270,12 +270,12 @@ describe('bucket policy authorization', () => { }); it('should deny access to non-bucket owner', - done => { - const allowed = isBucketAuthorized(bucket, 'bucketPut', - altAcctCanonicalId, null, log); - assert.equal(allowed, false); - done(); - }); + done => { + const allowed = isBucketAuthorized(bucket, 'bucketPut', + altAcctCanonicalId, null, log); + assert.equal(allowed, false); + done(); + }); }); describe('isBucketAuthorized with bucket policy set', () => { @@ -286,20 +286,20 @@ describe('bucket policy authorization', () => { }); it('should allow access to non-bucket owner if principal is set to "*"', - done => { - const allowed = isBucketAuthorized(bucket, bucAction, - altAcctCanonicalId, null, log); - assert.equal(allowed, true); - done(); - }); + done => { + const allowed = isBucketAuthorized(bucket, bucAction, + altAcctCanonicalId, null, log); + assert.equal(allowed, true); + done(); + }); it('should allow access to public user if principal is set to "*"', - done => { - const allowed = isBucketAuthorized(bucket, bucAction, - constants.publicId, publicUserAuthInfo, log); - assert.equal(allowed, true); - done(); - }); + done => { + const allowed = isBucketAuthorized(bucket, bucAction, + constants.publicId, publicUserAuthInfo, log); + assert.equal(allowed, true); + done(); + }); it('should deny access to public user if principal is not set to "*"', function itFn(done) { const newPolicy = this.test.basePolicy; @@ -324,7 +324,7 @@ describe('bucket policy authorization', () => { }); it('should deny access to non-bucket owner if two statements apply ' + - 'to principal but one denies access', function itFn(done) { + 'to principal but one denies access', function itFn(done) { const newPolicy = this.test.basePolicy; newPolicy.Statement[1] = { Effect: 'Deny', @@ -334,18 +334,18 @@ describe('bucket policy authorization', () => { }; bucket.setBucketPolicy(newPolicy); const allowed = isBucketAuthorized(bucket, bucAction, - altAcctCanonicalId, null, log); + altAcctCanonicalId, null, log); assert.equal(allowed, false); done(); }); it('should deny access to non-bucket owner with an unsupported action type', - done => { - const allowed = isBucketAuthorized(bucket, 'unsupportedAction', - altAcctCanonicalId, null, log); - assert.equal(allowed, false); - done(); - }); + done => { + const allowed = isBucketAuthorized(bucket, 'unsupportedAction', + altAcctCanonicalId, null, log); + assert.equal(allowed, false); + done(); + }); }); describe('isObjAuthorized with no policy set', () => { @@ -361,12 +361,12 @@ describe('bucket policy authorization', () => { }); it('should deny access to non-object owner', - done => { - const allowed = isObjAuthorized(bucket, object, objAction, - altAcctCanonicalId, null, log); - assert.equal(allowed, false); - done(); - }); + done => { + const allowed = isObjAuthorized(bucket, object, objAction, + altAcctCanonicalId, null, log); + assert.equal(allowed, false); + done(); + }); }); describe('isObjAuthorized with bucket policy set', () => { @@ -380,20 +380,20 @@ describe('bucket policy authorization', () => { }); it('should allow access to non-object owner if principal is set to "*"', - done => { - const allowed = isObjAuthorized(bucket, object, objAction, - altAcctCanonicalId, null, log); - assert.equal(allowed, true); - done(); - }); + done => { + const allowed = isObjAuthorized(bucket, object, objAction, + altAcctCanonicalId, null, log); + assert.equal(allowed, true); + done(); + }); it('should allow access to public user if principal is set to "*"', - done => { - const allowed = isObjAuthorized(bucket, object, objAction, - constants.publicId, publicUserAuthInfo, log); - assert.equal(allowed, true); - done(); - }); + done => { + const allowed = isObjAuthorized(bucket, object, objAction, + constants.publicId, publicUserAuthInfo, log); + assert.equal(allowed, true); + done(); + }); authTests.forEach(t => { it(`${t.name}object owner`, function itFn(done) { @@ -408,27 +408,27 @@ describe('bucket policy authorization', () => { }); it('should allow access to non-object owner for objectHead action with s3:GetObject permission', - function itFn(done) { - const newPolicy = this.test.basePolicy; - newPolicy.Statement[0].Action = ['s3:GetObject']; - bucket.setBucketPolicy(newPolicy); - const allowed = isObjAuthorized(bucket, object, 'objectHead', - altAcctCanonicalId, altAcctAuthInfo, log); - assert.equal(allowed, true); - done(); - }); + function itFn(done) { + const newPolicy = this.test.basePolicy; + newPolicy.Statement[0].Action = ['s3:GetObject']; + bucket.setBucketPolicy(newPolicy); + const allowed = isObjAuthorized(bucket, object, 'objectHead', + altAcctCanonicalId, altAcctAuthInfo, log); + assert.equal(allowed, true); + done(); + }); it('should deny access to non-object owner for objectHead action without s3:GetObject permission', - function itFn(done) { - const newPolicy = this.test.basePolicy; - newPolicy.Statement[0].Action = ['s3:PutObject']; - bucket.setBucketPolicy(newPolicy); - const allowed = isObjAuthorized(bucket, object, 'objectHead', - altAcctCanonicalId, altAcctAuthInfo, log); - assert.equal(allowed, false); - done(); - }); + function itFn(done) { + const newPolicy = this.test.basePolicy; + newPolicy.Statement[0].Action = ['s3:PutObject']; + bucket.setBucketPolicy(newPolicy); + const allowed = isObjAuthorized(bucket, object, 'objectHead', + altAcctCanonicalId, altAcctAuthInfo, log); + assert.equal(allowed, false); + done(); + }); it('should deny access to non-object owner if two statements apply ' + - 'to principal but one denies access', function itFn(done) { + 'to principal but one denies access', function itFn(done) { const newPolicy = this.test.basePolicy; newPolicy.Statement[1] = { Effect: 'Deny', @@ -438,18 +438,18 @@ describe('bucket policy authorization', () => { }; bucket.setBucketPolicy(newPolicy); const allowed = isObjAuthorized(bucket, object, objAction, - altAcctCanonicalId, null, log); + altAcctCanonicalId, null, log); assert.equal(allowed, false); done(); }); it('should deny access to non-object owner with an unsupported action type', - done => { - const allowed = isObjAuthorized(bucket, object, 'unsupportedAction', - altAcctCanonicalId, null, log); - assert.equal(allowed, false); - done(); - }); + done => { + const allowed = isObjAuthorized(bucket, object, 'unsupportedAction', + altAcctCanonicalId, null, log); + assert.equal(allowed, false); + done(); + }); it('should allow access when implicitDeny true with Allow bucket policy', function itFn() { const requestTypes = ['objectPut', 'objectDelete']; @@ -461,12 +461,19 @@ describe('bucket policy authorization', () => { newPolicy.Statement[0].Action = ['s3:PutObject', 's3:DeleteObject']; bucket.setBucketPolicy(newPolicy); - const results = requestTypes.map(type => { - const allowed = isObjAuthorized(bucket, object, type, - altAcctCanonicalId, altAcctAuthInfo, log, null, impDenies); - return allowed; - }); - assert.deepStrictEqual(results, [true, true]); + const oldOwner = bucket.getOwner(); + bucket.setOwner(altAcctCanonicalId); // The bucket owner needs to have the same account as the user. + + try { + const results = requestTypes.map(type => { + const allowed = isObjAuthorized(bucket, object, type, + altAcctCanonicalId, altAcctAuthInfo, log, null, impDenies); + return allowed; + }); + assert.deepStrictEqual(results, [true, true]); + } finally { + bucket.setOwner(oldOwner); + } }); it('should deny access when implicitDeny true with Deny bucket policy', function itFn() { @@ -487,7 +494,7 @@ describe('bucket policy authorization', () => { const results = requestTypes.map(type => { const allowed = isObjAuthorized(bucket, object, type, - altAcctCanonicalId, altAcctAuthInfo, log, null, impDenies); + altAcctCanonicalId, altAcctAuthInfo, log, null, impDenies); return allowed; }); assert.deepStrictEqual(results, [false, false]); @@ -507,7 +514,7 @@ describe('bucket policy authorization', () => { }); it('should return false if any statement resource does not match ' + - 'bucket arn', done => { + 'bucket arn', done => { const newPolicy = basePolicyObj; newPolicy.Statement = [newPolicy.Statement]; newPolicy.Statement[1] = basePolicyObj.Statement; @@ -632,7 +639,7 @@ describe('bucket policy authorization', () => { altAcctCanonicalId, altAcctAuthInfo, log, request); assert.strictEqual(results, t.expectedVerdict); } - ); + ); }); }); }); diff --git a/tests/unit/api/objectACLauth.js b/tests/unit/api/objectACLauth.js index f4ef2de207..df0ffad322 100644 --- a/tests/unit/api/objectACLauth.js +++ b/tests/unit/api/objectACLauth.js @@ -95,7 +95,7 @@ describe('object acl authorization for objectGet and objectHead', () => { assert.deepStrictEqual(results, [true, true]); }); - it('should allow access to bucker owner when object owner is alt account if ' + + it('should allow access to bucket owner when object owner is alt account if ' + 'bucket-owner-read ACL', () => { const altAcctObj = { 'owner-id': accountToVet, @@ -118,7 +118,7 @@ describe('object acl authorization for objectGet and objectHead', () => { assert.deepStrictEqual(authResults, [true, true]); }); - it('should allow access to bucker owner when object owner is alt account if ' + + it('should allow access to bucket owner when object owner is alt account if ' + 'bucket-owner-full-control ACL', () => { const altAcctObj = { 'owner-id': accountToVet,