Compare commits

...

21 Commits

Author SHA1 Message Date
williamlardier b7db1e26f8
S3C-7845: support authz in special APIs 2023-07-28 12:30:43 +02:00
williamlardier 7bd3ce7c7d
checkpoint 2023-07-20 18:26:08 +02:00
williamlardier 41b36504f1
wip 2023-07-20 16:32:18 +02:00
williamlardier 36a4d8083d
checkpoint 2023-07-20 16:07:06 +02:00
williamlardier 631241953f
wip 2023-07-20 15:01:34 +02:00
williamlardier 7bc6ab0b21
wip 2023-07-20 11:42:50 +02:00
williamlardier 25c729d533
wip 2023-07-20 09:36:12 +02:00
williamlardier 060f940ff2
wip 2023-07-20 09:16:24 +02:00
williamlardier cae50fe0d6
wip 2023-07-19 22:31:52 +02:00
williamlardier a4f6efa994
wip 2023-07-19 21:50:40 +02:00
williamlardier c5f3c4e8e6
checkpoint 2023-07-19 19:36:42 +02:00
williamlardier 9a5835b6a2
checkpoint 2023-07-19 18:42:51 +02:00
williamlardier 400bfda121
wip 2023-07-19 15:10:48 +02:00
williamlardier 7ff60af5a8
wip 2023-07-19 14:02:32 +02:00
williamlardier 052d619f1d
testing 2023-07-19 13:38:23 +02:00
williamlardier 6f9466bfeb
testing 2023-07-19 12:43:38 +02:00
williamlardier 31bb84788c
checkpoint 2023-07-18 18:08:03 +02:00
williamlardier cf230e7c07
test bb 2023-07-11 11:44:35 +02:00
williamlardier 7ccf386da5
wip 2023-07-11 11:05:45 +02:00
williamlardier d596264f01
wip 2023-07-11 10:11:07 +02:00
williamlardier 9a29d19a5a
Handle implcit deny results from IAM.
The API is started if IAM returns Allow or an implicit Deny.
In these cases, we add a new boolean to the request that
serves as a context when checking the Bucket/Object ACL or
the Bucket policies. Then, we implement the same authorization
logic as AWS, where an implicit deny from IAM and an Allow from
the Bucket Policy should allow the request.
2023-07-10 10:24:24 +02:00
127 changed files with 1018 additions and 382 deletions

View File

@ -49,7 +49,7 @@
}, },
"clusters": 10, "clusters": 10,
"log": { "log": {
"logLevel": "info", "logLevel": "trace",
"dumpLevel": "error" "dumpLevel": "error"
}, },
"healthChecks": { "healthChecks": {

View File

@ -152,6 +152,8 @@ const constants = {
'objectDeleteTagging', 'objectDeleteTagging',
'objectGetTagging', 'objectGetTagging',
'objectPutTagging', 'objectPutTagging',
'objectPutLegalHold',
'objectPutRetention',
], ],
// response header to be sent when there are invalid // response header to be sent when there are invalid
// user metadata in the object's metadata // user metadata in the object's metadata

View File

@ -114,6 +114,7 @@ const api = {
// no need to check auth on website or cors preflight requests // no need to check auth on website or cors preflight requests
if (apiMethod === 'websiteGet' || apiMethod === 'websiteHead' || if (apiMethod === 'websiteGet' || apiMethod === 'websiteHead' ||
apiMethod === 'corsPreflight') { apiMethod === 'corsPreflight') {
request.iamAuthzResults = false;
return this[apiMethod](request, log, callback); return this[apiMethod](request, log, callback);
} }
@ -136,15 +137,25 @@ const api = {
const requestContexts = prepareRequestContexts(apiMethod, request, const requestContexts = prepareRequestContexts(apiMethod, request,
sourceBucket, sourceObject, sourceVersionId); sourceBucket, sourceObject, sourceVersionId);
// Extract all the _apiMethods and store them in an array
const apiMethods = requestContexts ? requestContexts.map(context => context._apiMethod) : [];
// Attach the names to the current request
// eslint-disable-next-line no-param-reassign
request.apiMethods = apiMethods;
function checkAuthResults(authResults) { function checkAuthResults(authResults) {
let returnTagCount = true; let returnTagCount = true;
const isImplicitDeny = {};
let isOnlyImplicitDeny = true;
if (apiMethod === 'objectGet') { if (apiMethod === 'objectGet') {
// first item checks s3:GetObject(Version) action // first item checks s3:GetObject(Version) action
if (!authResults[0].isAllowed) { if (!authResults[0].isAllowed && !authResults[0].isImplicit) {
log.trace('get object authorization denial from Vault'); log.trace('get object authorization denial from Vault');
return errors.AccessDenied; 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 // second item checks s3:GetObject(Version)Tagging action
if (!authResults[1].isAllowed) { if (!authResults[1].isAllowed) {
log.trace('get tagging authorization denial ' + log.trace('get tagging authorization denial ' +
@ -153,13 +164,25 @@ const api = {
} }
} else { } else {
for (let i = 0; i < authResults.length; i++) { for (let i = 0; i < authResults.length; i++) {
if (!authResults[i].isAllowed) { isImplicitDeny[authResults[i].action] = true;
if (!authResults[i].isAllowed && !authResults[i].isImplicit) {
// Any explicit deny rejects the current API call
log.trace('authorization denial from Vault'); log.trace('authorization denial from Vault');
return errors.AccessDenied; return errors.AccessDenied;
} else if (authResults[i].isAllowed) {
// If the action is allowed, the result is not implicit
// Deny.
isImplicitDeny[authResults[i].action] = false;
isOnlyImplicitDeny = false;
} }
} }
} }
return returnTagCount; // These two APIs cannot use ACLs or Bucket Policies, hence, any
// implicit deny from vault must be treated as an explicit deny.
if ((apiMethod === 'bucketPut' || apiMethod === 'serviceGet') && isOnlyImplicitDeny) {
return errors.AccessDenied;
}
return { returnTagCount, isImplicitDeny };
} }
return async.waterfall([ return async.waterfall([
@ -237,7 +260,14 @@ const api = {
if (checkedResults instanceof Error) { if (checkedResults instanceof Error) {
return callback(checkedResults); return callback(checkedResults);
} }
returnTagCount = checkedResults; returnTagCount = checkedResults.returnTagCount;
request.iamAuthzResults = checkedResults.isImplicitDeny;
} else {
// create an object of keys apiMethods with all values to false
request.iamAuthzResults = apiMethods.reduce((acc, curr) => {
acc[curr] = false;
return acc;
}, {});
} }
if (apiMethod === 'objectPut' || apiMethod === 'objectPutPart') { if (apiMethod === 'objectPut' || apiMethod === 'objectPutPart') {
request._response = response; request._response = response;

View File

@ -1,5 +1,6 @@
const { evaluators, actionMaps, RequestContext } = require('arsenal').policies; const { evaluators, actionMaps, RequestContext, requestUtils } = require('arsenal').policies;
const constants = require('../../../../constants'); const constants = require('../../../../constants');
const { config } = require('../../../Config');
const { allAuthedUsersId, bucketOwnerActions, logId, publicId, const { allAuthedUsersId, bucketOwnerActions, logId, publicId,
assumedRoleArnResourceType, backbeatLifecycleSessionName } = constants; assumedRoleArnResourceType, backbeatLifecycleSessionName } = constants;
@ -8,13 +9,33 @@ const { allAuthedUsersId, bucketOwnerActions, logId, publicId,
const publicReadBuckets = process.env.ALLOW_PUBLIC_READ_BUCKETS ? const publicReadBuckets = process.env.ALLOW_PUBLIC_READ_BUCKETS ?
process.env.ALLOW_PUBLIC_READ_BUCKETS.split(',') : []; process.env.ALLOW_PUBLIC_READ_BUCKETS.split(',') : [];
function checkBucketAcls(bucket, requestType, canonicalID) { function checkBucketAcls(bucket, requestType, canonicalID, mainApiCall) {
// Same logic applies on the Versioned APIs, so let's simplify it.
const requestTypeParsed = requestType.endsWith('Version') ?
requestType.slice(0, -7) : requestType;
if (bucket.getOwner() === canonicalID) { if (bucket.getOwner() === canonicalID) {
return true; return true;
} }
// Backward compatibility
const arrayOfAllowed = [
'objectGetTagging',
'objectPutTagging',
'objectPutLegalHold',
'objectPutRetention',
];
if (mainApiCall === 'objectGet') {
if (arrayOfAllowed.includes(requestTypeParsed)) {
return true;
}
}
if (mainApiCall === 'objectPut') {
if (arrayOfAllowed.includes(requestTypeParsed)) {
return true;
}
}
const bucketAcl = bucket.getAcl(); const bucketAcl = bucket.getAcl();
if (requestType === 'bucketGet' || requestType === 'bucketHead') { if (requestTypeParsed === 'bucketGet' || requestTypeParsed === 'bucketHead') {
if (bucketAcl.Canned === 'public-read' if (bucketAcl.Canned === 'public-read'
|| bucketAcl.Canned === 'public-read-write' || bucketAcl.Canned === 'public-read-write'
|| (bucketAcl.Canned === 'authenticated-read' || (bucketAcl.Canned === 'authenticated-read'
@ -32,7 +53,7 @@ function checkBucketAcls(bucket, requestType, canonicalID) {
return true; return true;
} }
} }
if (requestType === 'bucketGetACL') { if (requestTypeParsed === 'bucketGetACL') {
if ((bucketAcl.Canned === 'log-delivery-write' if ((bucketAcl.Canned === 'log-delivery-write'
&& canonicalID === logId) && canonicalID === logId)
|| bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1 || bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
@ -48,7 +69,7 @@ function checkBucketAcls(bucket, requestType, canonicalID) {
} }
} }
if (requestType === 'bucketPutACL') { if (requestTypeParsed === 'bucketPutACL') {
if (bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1 if (bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
|| bucketAcl.WRITE_ACP.indexOf(canonicalID) > -1) { || bucketAcl.WRITE_ACP.indexOf(canonicalID) > -1) {
return true; return true;
@ -62,11 +83,7 @@ function checkBucketAcls(bucket, requestType, canonicalID) {
} }
} }
if (requestType === 'bucketDelete' && bucket.getOwner() === canonicalID) { if (requestTypeParsed === 'objectDelete' || requestTypeParsed === 'objectPut') {
return true;
}
if (requestType === 'objectDelete' || requestType === 'objectPut') {
if (bucketAcl.Canned === 'public-read-write' if (bucketAcl.Canned === 'public-read-write'
|| bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1 || bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
|| bucketAcl.WRITE.indexOf(canonicalID) > -1) { || bucketAcl.WRITE.indexOf(canonicalID) > -1) {
@ -86,11 +103,12 @@ function checkBucketAcls(bucket, requestType, canonicalID) {
// objectPutACL, objectGetACL, objectHead or objectGet, the bucket // objectPutACL, objectGetACL, objectHead or objectGet, the bucket
// authorization check should just return true so can move on to check // authorization check should just return true so can move on to check
// rights at the object level. // rights at the object level.
return (requestType === 'objectPutACL' || requestType === 'objectGetACL' || return (requestTypeParsed === 'objectPutACL' || requestTypeParsed === 'objectGetACL' ||
requestType === 'objectGet' || requestType === 'objectHead'); requestTypeParsed === 'objectGet' || requestTypeParsed === 'objectHead');
} }
function checkObjectAcls(bucket, objectMD, requestType, canonicalID) { function checkObjectAcls(bucket, objectMD, requestType, canonicalID, requesterIsNotUser,
isUserUnauthenticated, mainApiCall) {
const bucketOwner = bucket.getOwner(); const bucketOwner = bucket.getOwner();
// acls don't distinguish between users and accounts, so both should be allowed // acls don't distinguish between users and accounts, so both should be allowed
if (bucketOwnerActions.includes(requestType) if (bucketOwnerActions.includes(requestType)
@ -100,6 +118,15 @@ function checkObjectAcls(bucket, objectMD, requestType, canonicalID) {
if (objectMD['owner-id'] === canonicalID) { if (objectMD['owner-id'] === canonicalID) {
return true; return true;
} }
// Backward compatibility
if (mainApiCall === 'objectGet') {
if ((isUserUnauthenticated || (requesterIsNotUser && bucketOwner === objectMD['owner-id']))
&& requestType === 'objectGetTagging') {
return true;
}
}
if (!objectMD.acl) { if (!objectMD.acl) {
return false; return false;
} }
@ -198,6 +225,20 @@ function _checkBucketPolicyResources(request, resource, log) {
return evaluators.isResourceApplicable(requestContext, resource, log); return evaluators.isResourceApplicable(requestContext, resource, log);
} }
function _checkBucketPolicyConditions(request, conditions, log) {
const ip = request ? requestUtils.getClientIp(request, config) : undefined;
if (!conditions) {
return true;
}
// build request context from the request!
const requestContext = new RequestContext(request.headers, request.query,
request.bucketName, request.objectKey, ip,
request.connection.encrypted, request.resourceType, 's3', null, null,
null, null, null, null, null, null, null, null, null,
request.objectLockRetentionDays);
return evaluators.meetConditions(requestContext, conditions, log);
}
function _getAccountId(arn) { function _getAccountId(arn) {
// account or user arn is of format 'arn:aws:iam::<12-digit-acct-id>:etc... // account or user arn is of format 'arn:aws:iam::<12-digit-acct-id>:etc...
return arn.substr(13, 12); return arn.substr(13, 12);
@ -255,12 +296,13 @@ function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, l
const principalMatch = _checkPrincipals(canonicalID, arn, s.Principal); const principalMatch = _checkPrincipals(canonicalID, arn, s.Principal);
const actionMatch = _checkBucketPolicyActions(requestType, s.Action, log); const actionMatch = _checkBucketPolicyActions(requestType, s.Action, log);
const resourceMatch = _checkBucketPolicyResources(request, s.Resource, log); const resourceMatch = _checkBucketPolicyResources(request, s.Resource, log);
const conditionsMatch = _checkBucketPolicyConditions(request, s.Condition, log);
if (principalMatch && actionMatch && resourceMatch && s.Effect === 'Deny') { if (principalMatch && actionMatch && resourceMatch && conditionsMatch && s.Effect === 'Deny') {
// explicit deny trumps any allows, so return immediately // explicit deny trumps any allows, so return immediately
return 'explicitDeny'; return 'explicitDeny';
} }
if (principalMatch && actionMatch && resourceMatch && s.Effect === 'Allow') { if (principalMatch && actionMatch && resourceMatch && conditionsMatch && s.Effect === 'Allow') {
permission = 'allow'; permission = 'allow';
} }
copiedStatement = copiedStatement.splice(1); copiedStatement = copiedStatement.splice(1);
@ -268,75 +310,194 @@ function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, l
return permission; return permission;
} }
function isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request) { function isBucketAuthorized(bucket, requestTypes, canonicalID, authInfo, iamAuthzResults, log, request) {
// Check to see if user is authorized to perform a if (!Array.isArray(requestTypes)) {
// particular action on bucket based on ACLs. // eslint-disable-next-line no-param-reassign
// TODO: Add IAM checks requestTypes = [requestTypes];
let requesterIsNotUser = true;
let arn = null;
if (authInfo) {
requesterIsNotUser = !authInfo.isRequesterAnIAMUser();
arn = authInfo.getArn();
} }
// if the bucket owner is an account, users should not have default access if (iamAuthzResults === false) {
if ((bucket.getOwner() === canonicalID) && requesterIsNotUser) { // eslint-disable-next-line no-param-reassign
return true; iamAuthzResults = {};
} }
const aclPermission = checkBucketAcls(bucket, requestType, canonicalID); // By default, all missing actions are defined as allowed from IAM, to be
const bucketPolicy = bucket.getBucketPolicy(); // backward compatible
if (!bucketPolicy) { requestTypes.forEach(requestType => {
return aclPermission; if (iamAuthzResults[requestType] === undefined) {
} // eslint-disable-next-line no-param-reassign
const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType, iamAuthzResults[requestType] = false;
canonicalID, arn, bucket.getOwner(), log, request); }
if (bucketPolicyPermission === 'explicitDeny') { });
return false; const mainApiCall = requestTypes[0];
} const results = {};
return (aclPermission || (bucketPolicyPermission === 'allow')); requestTypes.forEach(_requestType => {
// Check to see if user is authorized to perform a
// particular action on bucket based on ACLs.
// TODO: Add IAM checks
let requesterIsNotUser = true;
let arn = null;
if (authInfo) {
requesterIsNotUser = !authInfo.isRequesterAnIAMUser();
arn = authInfo.getArn();
}
// if the bucket owner is an account, users should not have default access
if ((bucket.getOwner() === canonicalID) && requesterIsNotUser) {
results[_requestType] = iamAuthzResults[_requestType] === false;
return;
}
const aclPermission = checkBucketAcls(bucket, _requestType, canonicalID, mainApiCall);
const bucketPolicy = bucket.getBucketPolicy();
if (!bucketPolicy) {
results[_requestType] = iamAuthzResults[_requestType] === false && aclPermission;
return;
}
const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, _requestType,
canonicalID, arn, bucket.getOwner(), log, request);
if (bucketPolicyPermission === 'explicitDeny') {
results[_requestType] = false;
return;
}
// If the bucket policy returns an allow, we accept the request, as the
// IAM response here is either Allow or implicit deny.
if (bucketPolicyPermission === 'allow') {
results[_requestType] = true;
return;
}
results[_requestType] = iamAuthzResults[_requestType] === false && aclPermission;
});
// final result is true if all the results are true
return Object.keys(results).every(key => results[key] === true);
} }
function isObjAuthorized(bucket, objectMD, requestType, canonicalID, authInfo, log, request) { function evaluateBucketPolicyWithIAM(bucket, requestTypes, canonicalID, authInfo, iamAuthzResults, log, request) {
const bucketOwner = bucket.getOwner(); if (!Array.isArray(requestTypes)) {
if (!objectMD) { // eslint-disable-next-line no-param-reassign
// User is already authorized on the bucket for FULL_CONTROL or WRITE or requestTypes = [requestTypes];
// bucket has canned ACL public-read-write }
if (requestType === 'objectPut' || requestType === 'objectDelete') { if (iamAuthzResults === false) {
return true; // eslint-disable-next-line no-param-reassign
iamAuthzResults = {};
}
// By default, all missing actions are defined as allowed from IAM, to be
// backward compatible
requestTypes.forEach(requestType => {
if (iamAuthzResults[requestType] === undefined) {
// eslint-disable-next-line no-param-reassign
iamAuthzResults[requestType] = false;
} }
// check bucket has read access });
// 'bucketGet' covers listObjects and listMultipartUploads, bucket read actions
return isBucketAuthorized(bucket, 'bucketGet', canonicalID, authInfo, log, request); const results = {};
requestTypes.forEach(_requestType => {
let arn = null;
if (authInfo) {
arn = authInfo.getArn();
}
const bucketPolicy = bucket.getBucketPolicy();
if (!bucketPolicy) {
results[_requestType] = iamAuthzResults[_requestType] === false;
return;
}
const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, _requestType,
canonicalID, arn, bucket.getOwner(), log, request);
if (bucketPolicyPermission === 'explicitDeny') {
results[_requestType] = false;
return;
}
// If the bucket policy returns an allow, we accept the request, as the
// IAM response here is either Allow or implicit deny.
if (bucketPolicyPermission === 'allow') {
results[_requestType] = true;
return;
}
results[_requestType] = iamAuthzResults[_requestType] === false;
});
// final result is true if all the results are true
return Object.keys(results).every(key => results[key] === true);
}
function isObjAuthorized(bucket, objectMD, requestTypes, canonicalID, authInfo, iamAuthzResults, log, request) {
if (!Array.isArray(requestTypes)) {
// eslint-disable-next-line no-param-reassign
requestTypes = [requestTypes];
} }
let requesterIsNotUser = true; // By default, all missing actions are defined as allowed from IAM, to be
let arn = null; // backward compatible
if (authInfo) { if (iamAuthzResults === false) {
requesterIsNotUser = !authInfo.isRequesterAnIAMUser(); // eslint-disable-next-line no-param-reassign
arn = authInfo.getArn(); iamAuthzResults = {};
} }
if (objectMD['owner-id'] === canonicalID && requesterIsNotUser) { requestTypes.forEach(requestType => {
return true; if (iamAuthzResults[requestType] === undefined) {
} // eslint-disable-next-line no-param-reassign
// account is authorized if: iamAuthzResults[requestType] = false;
// - requesttype is included in bucketOwnerActions and }
// - account is the bucket owner });
// - requester is account, not user const results = {};
if (bucketOwnerActions.includes(requestType) const mainApiCall = requestTypes[0];
&& (bucketOwner === canonicalID) requestTypes.forEach(_requestType => {
&& requesterIsNotUser) { const parsedMethodName = _requestType.endsWith('Version') ?
return true; _requestType.slice(0, -7) : _requestType;
} const bucketOwner = bucket.getOwner();
const aclPermission = checkObjectAcls(bucket, objectMD, requestType, if (!objectMD) {
canonicalID); // User is already authorized on the bucket for FULL_CONTROL or WRITE or
const bucketPolicy = bucket.getBucketPolicy(); // bucket has canned ACL public-read-write
if (!bucketPolicy) { if (parsedMethodName === 'objectPut' || parsedMethodName === 'objectDelete') {
return aclPermission; results[_requestType] = iamAuthzResults[_requestType] === false;
} return;
const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType, }
canonicalID, arn, bucket.getOwner(), log, request); // check bucket has read access
if (bucketPolicyPermission === 'explicitDeny') { // 'bucketGet' covers listObjects and listMultipartUploads, bucket read actions
return false; results[_requestType] = isBucketAuthorized(bucket, 'bucketGet', canonicalID, authInfo, false, log, request);
} return;
return (aclPermission || (bucketPolicyPermission === 'allow')); }
let requesterIsNotUser = true;
let arn = null;
let isUserUnauthenticated = false;
if (authInfo) {
requesterIsNotUser = !authInfo.isRequesterAnIAMUser();
arn = authInfo.getArn();
isUserUnauthenticated = arn === undefined;
}
if (objectMD['owner-id'] === canonicalID && requesterIsNotUser) {
results[_requestType] = iamAuthzResults[_requestType] === false;
return;
}
// account is authorized if:
// - requesttype is included in bucketOwnerActions and
// - account is the bucket owner
// - requester is account, not user
if (bucketOwnerActions.includes(parsedMethodName)
&& (bucketOwner === canonicalID)
&& requesterIsNotUser) {
results[_requestType] = iamAuthzResults[_requestType] === false;
return;
}
const aclPermission = checkObjectAcls(bucket, objectMD, parsedMethodName,
canonicalID, requesterIsNotUser, isUserUnauthenticated, mainApiCall);
const bucketPolicy = bucket.getBucketPolicy();
if (!bucketPolicy) {
results[_requestType] = iamAuthzResults[_requestType] === false && aclPermission;
return;
}
const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, _requestType,
canonicalID, arn, bucket.getOwner(), log, request);
if (bucketPolicyPermission === 'explicitDeny') {
results[_requestType] = false;
return;
}
// If the bucket policy returns an allow, we accept the request, as the
// IAM response here is either Allow or implicit deny.
if (bucketPolicyPermission === 'allow') {
results[_requestType] = true;
return;
}
results[_requestType] = iamAuthzResults[_requestType] === false && aclPermission;
});
// final result is true if all the results are true
return Object.keys(results).every(key => results[key] === true);
} }
function _checkResource(resource, bucketArn) { function _checkResource(resource, bucketArn) {
@ -395,4 +556,5 @@ module.exports = {
checkObjectAcls, checkObjectAcls,
validatePolicyResource, validatePolicyResource,
isLifecycleSession, isLifecycleSession,
evaluateBucketPolicyWithIAM,
}; };

View File

@ -24,7 +24,7 @@ function _deleteMPUbucket(destinationBucketName, log, cb) {
}); });
} }
function _deleteOngoingMPUs(authInfo, bucketName, bucketMD, mpus, log, cb) { function _deleteOngoingMPUs(authInfo, bucketName, bucketMD, mpus, request, log, cb) {
async.mapLimit(mpus, 1, (mpu, next) => { async.mapLimit(mpus, 1, (mpu, next) => {
const splitterChar = mpu.key.includes(oldSplitter) ? const splitterChar = mpu.key.includes(oldSplitter) ?
oldSplitter : splitter; oldSplitter : splitter;
@ -40,7 +40,7 @@ function _deleteOngoingMPUs(authInfo, bucketName, bucketMD, mpus, log, cb) {
byteLength: partSizeSum, byteLength: partSizeSum,
}); });
next(err); next(err);
}); }, request);
}, cb); }, cb);
} }
/** /**
@ -49,11 +49,13 @@ function _deleteOngoingMPUs(authInfo, bucketName, bucketMD, mpus, log, cb) {
* @param {object} bucketMD - bucket attributes/metadata * @param {object} bucketMD - bucket attributes/metadata
* @param {string} bucketName - bucket in which objectMetadata is stored * @param {string} bucketName - bucket in which objectMetadata is stored
* @param {string} canonicalID - account canonicalID of requester * @param {string} canonicalID - account canonicalID of requester
* @param {object} request - request object given by router
* including normalized headers
* @param {object} log - Werelogs logger * @param {object} log - Werelogs logger
* @param {function} cb - callback from async.waterfall in bucketDelete * @param {function} cb - callback from async.waterfall in bucketDelete
* @return {undefined} * @return {undefined}
*/ */
function deleteBucket(authInfo, bucketMD, bucketName, canonicalID, log, cb) { function deleteBucket(authInfo, bucketMD, bucketName, canonicalID, request, log, cb) {
log.trace('deleting bucket from metadata'); log.trace('deleting bucket from metadata');
assert.strictEqual(typeof bucketName, 'string'); assert.strictEqual(typeof bucketName, 'string');
assert.strictEqual(typeof canonicalID, 'string'); assert.strictEqual(typeof canonicalID, 'string');
@ -100,7 +102,7 @@ function deleteBucket(authInfo, bucketMD, bucketName, canonicalID, log, cb) {
} }
if (objectsListRes.Contents.length) { if (objectsListRes.Contents.length) {
return _deleteOngoingMPUs(authInfo, bucketName, return _deleteOngoingMPUs(authInfo, bucketName,
bucketMD, objectsListRes.Contents, log, err => { bucketMD, objectsListRes.Contents, request, log, err => {
if (err) { if (err) {
return next(err); return next(err);
} }

View File

@ -14,7 +14,7 @@ function abortMultipartUpload(authInfo, bucketName, objectKey, uploadId, log,
bucketName, bucketName,
objectKey, objectKey,
uploadId, uploadId,
preciseRequestType: 'multipartDelete', preciseRequestType: request.apiMethods || 'multipartDelete',
request, request,
}; };
// For validating the request at the destinationBucket level // For validating the request at the destinationBucket level
@ -22,10 +22,11 @@ function abortMultipartUpload(authInfo, bucketName, objectKey, uploadId, log,
// but the requestType is the more general 'objectDelete' // but the requestType is the more general 'objectDelete'
const metadataValParams = Object.assign({}, metadataValMPUparams); const metadataValParams = Object.assign({}, metadataValMPUparams);
metadataValParams.requestType = 'objectPut'; metadataValParams.requestType = 'objectPut';
const authzIdentityResult = request ? request.iamAuthzResults : true;
async.waterfall([ async.waterfall([
function checkDestBucketVal(next) { function checkDestBucketVal(next) {
metadataValidateBucketAndObj(metadataValParams, log, metadataValidateBucketAndObj(metadataValParams, authzIdentityResult, log,
(err, destinationBucket) => { (err, destinationBucket) => {
if (err) { if (err) {
return next(err, destinationBucket); return next(err, destinationBucket);
@ -56,9 +57,15 @@ function abortMultipartUpload(authInfo, bucketName, objectKey, uploadId, log,
function abortExternalMpu(mpuBucket, mpuOverviewObj, destBucket, function abortExternalMpu(mpuBucket, mpuOverviewObj, destBucket,
next) { next) {
const location = mpuOverviewObj.controllingLocationConstraint; const location = mpuOverviewObj.controllingLocationConstraint;
const originalIdentityAuthzResults = request.iamAuthzResults;
// eslint-disable-next-line no-param-reassign
// eslint-disable-next-line no-param-reassign
delete request.iamAuthzResults;
return data.abortMPU(objectKey, uploadId, location, bucketName, return data.abortMPU(objectKey, uploadId, location, bucketName,
request, destBucket, locationConstraintCheck, log, request, destBucket, locationConstraintCheck, log,
(err, skipDataDelete) => { (err, skipDataDelete) => {
// eslint-disable-next-line no-param-reassign
request.iamAuthzResults = originalIdentityAuthzResults;
if (err) { if (err) {
return next(err, destBucket); return next(err, destBucket);
} }

View File

@ -3,6 +3,7 @@ const moment = require('moment');
const { config } = require('../../../Config'); const { config } = require('../../../Config');
const vault = require('../../../auth/vault'); const vault = require('../../../auth/vault');
const { evaluateBucketPolicyWithIAM } = require('../authorization/permissionChecks');
/** /**
* Calculates retain until date for the locked object version * Calculates retain until date for the locked object version
@ -302,7 +303,11 @@ function checkUserGovernanceBypass(request, authInfo, bucketMD, objectKey, log,
if (err) { if (err) {
return cb(err); return cb(err);
} }
if (authorizationResults[0].isAllowed !== true) {
// Deny immediately if there is any explicit deny
const explicitDenyExists = authorizationResults.some(
authzResult => authzResult.isAllowed === false && authzResult.isImplicit === false);
if (explicitDenyExists) {
log.trace('authorization check failed for user', log.trace('authorization check failed for user',
{ {
'method': 'checkUserPolicyGovernanceBypass', 'method': 'checkUserPolicyGovernanceBypass',
@ -310,7 +315,25 @@ function checkUserGovernanceBypass(request, authInfo, bucketMD, objectKey, log,
}); });
return cb(errors.AccessDenied); return cb(errors.AccessDenied);
} }
return cb(null);
// Convert authorization results into an easier to handle format
const iamAuthzResults = authorizationResults.reduce((acc, curr, idx) => {
// eslint-disable-next-line no-param-reassign
acc[requestContextParams[idx].apiMethod] = curr.isImplicit;
return acc;
}, {});
// Evaluate against the bucket policies
const areAllActionsAllowed = evaluateBucketPolicyWithIAM(
bucketMD,
Object.keys(iamAuthzResults),
authInfo.getCanonicalID(),
authInfo,
iamAuthzResults,
log,
request);
return cb(areAllActionsAllowed ? null : errors.AccessDenied);
}); });
} }

View File

@ -30,11 +30,11 @@ function bucketDelete(authInfo, request, log, cb) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketDelete', requestType: request.apiMethods || 'bucketDelete',
request, request,
}; };
return metadataValidateBucket(metadataValParams, log, return metadataValidateBucket(metadataValParams, request.iamAuthzResults, log,
(err, bucketMD) => { (err, bucketMD) => {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucketMD); request.method, bucketMD);
@ -48,7 +48,7 @@ function bucketDelete(authInfo, request, log, cb) {
log.trace('passed checks', log.trace('passed checks',
{ method: 'metadataValidateBucket' }); { method: 'metadataValidateBucket' });
return deleteBucket(authInfo, bucketMD, bucketName, return deleteBucket(authInfo, bucketMD, bucketName,
authInfo.getCanonicalID(), log, err => { authInfo.getCanonicalID(), request, log, err => {
if (err) { if (err) {
monitoring.promMetrics( monitoring.promMetrics(
'DELETE', bucketName, err.code, 'deleteBucket'); 'DELETE', bucketName, err.code, 'deleteBucket');

View File

@ -38,7 +38,8 @@ function bucketDeleteCors(authInfo, request, log, callback) {
} }
log.trace('found bucket in metadata'); log.trace('found bucket in metadata');
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) { if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
request.iamAuthzResults, log, request)) {
log.debug('access denied for user on bucket', { log.debug('access denied for user on bucket', {
requestType, requestType,
method: 'bucketDeleteCors', method: 'bucketDeleteCors',

View File

@ -21,12 +21,12 @@ function bucketDeleteEncryption(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketDeleteEncryption', requestType: request.apiMethods || 'bucketDeleteEncryption',
request, request,
}; };
return async.waterfall([ return async.waterfall([
next => metadataValidateBucket(metadataValParams, log, next), next => metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, next),
(bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => next(err, bucket)), (bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => next(err, bucket)),
(bucket, next) => { (bucket, next) => {
const sseConfig = bucket.getServerSideEncryption(); const sseConfig = bucket.getServerSideEncryption();

View File

@ -18,10 +18,10 @@ function bucketDeleteLifecycle(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketDeleteLifecycle', requestType: request.apiMethods || 'bucketDeleteLifecycle',
request, request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
if (err) { if (err) {
log.debug('error processing request', { log.debug('error processing request', {

View File

@ -16,10 +16,10 @@ function bucketDeletePolicy(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketDeletePolicy', requestType: request.apiMethods || 'bucketDeletePolicy',
request, request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
if (err) { if (err) {
log.debug('error processing request', { log.debug('error processing request', {

View File

@ -18,10 +18,10 @@ function bucketDeleteReplication(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketDeleteReplication', requestType: request.apiMethods || 'bucketDeleteReplication',
request, request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
if (err) { if (err) {
log.debug('error processing request', { log.debug('error processing request', {

View File

@ -26,11 +26,11 @@ async function bucketDeleteTagging(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketDeleteTagging', requestType: request.apiMethods || 'bucketDeleteTagging',
}; };
try { try {
bucket = await metadataValidateBucketPromise(metadataValParams, log); bucket = await metadataValidateBucketPromise(metadataValParams, request.iamAuthzResults, log);
bucket.setTags([]); bucket.setTags([]);
// eslint-disable-next-line no-unused-expressions // eslint-disable-next-line no-unused-expressions
await updateBucketPromise(bucket.getName(), bucket, log); await updateBucketPromise(bucket.getName(), bucket, log);

View File

@ -30,7 +30,8 @@ function bucketDeleteWebsite(authInfo, request, log, callback) {
} }
log.trace('found bucket in metadata'); log.trace('found bucket in metadata');
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) { if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
request.iamAuthzResults, log, request)) {
log.debug('access denied for user on bucket', { log.debug('access denied for user on bucket', {
requestType, requestType,
method: 'bucketDeleteWebsite', method: 'bucketDeleteWebsite',

View File

@ -328,7 +328,7 @@ function bucketGet(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGet', requestType: request.apiMethods || 'bucketGet',
request, request,
}; };
const listParams = { const listParams = {
@ -351,7 +351,7 @@ function bucketGet(authInfo, request, log, callback) {
listParams.marker = params.marker; listParams.marker = params.marker;
} }
metadataValidateBucket(metadataValParams, log, (err, bucket) => { metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket); request.method, bucket);
if (err) { if (err) {

View File

@ -44,7 +44,7 @@ function bucketGetACL(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetACL', requestType: request.apiMethods || 'bucketGetACL',
request, request,
}; };
const grantInfo = { const grantInfo = {
@ -55,7 +55,7 @@ function bucketGetACL(authInfo, request, log, callback) {
}, },
}; };
metadataValidateBucket(metadataValParams, log, (err, bucket) => { metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket); request.method, bucket);
if (err) { if (err) {

View File

@ -39,7 +39,8 @@ function bucketGetCors(authInfo, request, log, callback) {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket); request.method, bucket);
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) { if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
request.iamAuthzResults, log, request)) {
log.debug('access denied for user on bucket', { log.debug('access denied for user on bucket', {
requestType, requestType,
method: 'bucketGetCors', method: 'bucketGetCors',

View File

@ -22,12 +22,12 @@ function bucketGetEncryption(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetEncryption', requestType: request.apiMethods || 'bucketGetEncryption',
request, request,
}; };
return async.waterfall([ return async.waterfall([
next => metadataValidateBucket(metadataValParams, log, next), next => metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, next),
(bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => next(err, bucket)), (bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => next(err, bucket)),
(bucket, next) => { (bucket, next) => {
// If sseInfo is present but the `mandatory` flag is not set // If sseInfo is present but the `mandatory` flag is not set

View File

@ -21,10 +21,10 @@ function bucketGetLifecycle(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetLifecycle', requestType: request.apiMethods || 'bucketGetLifecycle',
request, request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
if (err) { if (err) {
log.debug('error processing request', { log.debug('error processing request', {

View File

@ -41,7 +41,8 @@ function bucketGetLocation(authInfo, request, log, callback) {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket); request.method, bucket);
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) { if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
request.iamAuthzResults, log, request)) {
log.debug('access denied for account on bucket', { log.debug('access denied for account on bucket', {
requestType, requestType,
method: 'bucketGetLocation', method: 'bucketGetLocation',

View File

@ -37,11 +37,11 @@ function bucketGetNotification(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetNotification', requestType: request.apiMethods || 'bucketGetNotification',
request, request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
if (err) { if (err) {
log.debug('error processing request', { log.debug('error processing request', {

View File

@ -33,10 +33,10 @@ function bucketGetObjectLock(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetObjectLock', requestType: request.apiMethods || 'bucketGetObjectLock',
request, request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
if (err) { if (err) {
log.debug('error processing request', { log.debug('error processing request', {

View File

@ -17,11 +17,11 @@ function bucketGetPolicy(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetPolicy', requestType: request.apiMethods || 'bucketGetPolicy',
request, request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
if (err) { if (err) {
log.debug('error processing request', { log.debug('error processing request', {

View File

@ -21,10 +21,10 @@ function bucketGetReplication(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetReplication', requestType: request.apiMethods || 'bucketGetReplication',
request, request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
if (err) { if (err) {
log.debug('error processing request', { log.debug('error processing request', {

View File

@ -70,7 +70,7 @@ async function bucketGetTagging(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetTagging', requestType: request.apiMethods || 'bucketGetTagging',
request, request,
}; };
@ -78,7 +78,7 @@ async function bucketGetTagging(authInfo, request, log, callback) {
let xml = null; let xml = null;
try { try {
bucket = await metadataValidateBucketPromise(metadataValParams, log); bucket = await metadataValidateBucketPromise(metadataValParams, request.iamAuthzResults, log);
// eslint-disable-next-line no-unused-expressions // eslint-disable-next-line no-unused-expressions
await checkExpectedBucketOwnerPromise(headers, bucket, log); await checkExpectedBucketOwnerPromise(headers, bucket, log);
const tags = bucket.getTags(); const tags = bucket.getTags();

View File

@ -54,11 +54,11 @@ function bucketGetVersioning(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetVersioning', requestType: request.apiMethods || 'bucketGetVersioning',
request, request,
}; };
metadataValidateBucket(metadataValParams, log, (err, bucket) => { metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket); request.method, bucket);
if (err) { if (err) {

View File

@ -39,7 +39,8 @@ function bucketGetWebsite(authInfo, request, log, callback) {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket); request.method, bucket);
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) { if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
request.iamAuthzResults, log, request)) {
log.debug('access denied for user on bucket', { log.debug('access denied for user on bucket', {
requestType, requestType,
method: 'bucketGetWebsite', method: 'bucketGetWebsite',

View File

@ -19,10 +19,10 @@ function bucketHead(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketHead', requestType: request.apiMethods || 'bucketHead',
request, request,
}; };
metadataValidateBucket(metadataValParams, log, (err, bucket) => { metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket); request.method, bucket);
if (err) { if (err) {

View File

@ -54,19 +54,6 @@ function bucketPutACL(authInfo, request, log, callback) {
'authenticated-read', 'authenticated-read',
'log-delivery-write', 'log-delivery-write',
]; ];
if (newCannedACL && possibleCannedACL.indexOf(newCannedACL) === -1) {
log.trace('invalid canned acl argument', {
acl: newCannedACL,
method: 'bucketPutACL',
});
monitoring.promMetrics('PUT', bucketName, 400, 'bucketPutACL');
return callback(errors.InvalidArgument);
}
if (!aclUtils.checkGrantHeaderValidity(request.headers)) {
log.trace('invalid acl header');
monitoring.promMetrics('PUT', bucketName, 400, 'bucketPutACL');
return callback(errors.InvalidArgument);
}
const possibleGroups = [constants.allAuthedUsersId, const possibleGroups = [constants.allAuthedUsersId,
constants.publicId, constants.publicId,
constants.logId, constants.logId,
@ -74,7 +61,7 @@ function bucketPutACL(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutACL', requestType: request.apiMethods || 'bucketPutACL',
request, request,
}; };
const possibleGrants = ['FULL_CONTROL', 'WRITE', const possibleGrants = ['FULL_CONTROL', 'WRITE',
@ -105,7 +92,7 @@ function bucketPutACL(authInfo, request, log, callback) {
return async.waterfall([ return async.waterfall([
function waterfall1(next) { function waterfall1(next) {
metadataValidateBucket(metadataValParams, log, metadataValidateBucket(metadataValParams, request.iamAuthzResults, log,
(err, bucket) => { (err, bucket) => {
if (err) { if (err) {
log.trace('request authorization failed', { log.trace('request authorization failed', {
@ -114,6 +101,20 @@ function bucketPutACL(authInfo, request, log, callback) {
}); });
return next(err, bucket); return next(err, bucket);
} }
// if the API call is allowed, ensure that the parameters are valid
if (newCannedACL && possibleCannedACL.indexOf(newCannedACL) === -1) {
log.trace('invalid canned acl argument', {
acl: newCannedACL,
method: 'bucketPutACL',
});
monitoring.promMetrics('PUT', bucketName, 400, 'bucketPutACL');
return next(errors.InvalidArgument);
}
if (!aclUtils.checkGrantHeaderValidity(request.headers)) {
log.trace('invalid acl header');
monitoring.promMetrics('PUT', bucketName, 400, 'bucketPutACL');
return next(errors.InvalidArgument);
}
return next(null, bucket); return next(null, bucket);
}); });
}, },

View File

@ -70,7 +70,8 @@ function bucketPutCors(authInfo, request, log, callback) {
}); });
}, },
function validateBucketAuthorization(bucket, rules, corsHeaders, next) { function validateBucketAuthorization(bucket, rules, corsHeaders, next) {
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) { if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
request.iamAuthzResults, log, request)) {
log.debug('access denied for account on bucket', { log.debug('access denied for account on bucket', {
requestType, requestType,
}); });

View File

@ -23,12 +23,12 @@ function bucketPutEncryption(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutEncryption', requestType: request.apiMethods || 'bucketPutEncryption',
request, request,
}; };
return async.waterfall([ return async.waterfall([
next => metadataValidateBucket(metadataValParams, log, next), next => metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, next),
(bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => next(err, bucket)), (bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => next(err, bucket)),
(bucket, next) => { (bucket, next) => {
log.trace('parsing encryption config', { method: 'bucketPutEncryption' }); log.trace('parsing encryption config', { method: 'bucketPutEncryption' });

View File

@ -26,7 +26,7 @@ function bucketPutLifecycle(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutLifecycle', requestType: request.apiMethods || 'bucketPutLifecycle',
request, request,
}; };
return waterfall([ return waterfall([
@ -43,7 +43,7 @@ function bucketPutLifecycle(authInfo, request, log, callback) {
return next(null, configObj); return next(null, configObj);
}); });
}, },
(lcConfig, next) => metadataValidateBucket(metadataValParams, log, (lcConfig, next) => metadataValidateBucket(metadataValParams, request.iamAuthzResults, log,
(err, bucket) => { (err, bucket) => {
if (err) { if (err) {
return next(err, bucket); return next(err, bucket);

View File

@ -23,7 +23,7 @@ function bucketPutNotification(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutNotification', requestType: request.apiMethods || 'bucketPutNotification',
request, request,
}; };
@ -34,7 +34,7 @@ function bucketPutNotification(authInfo, request, log, callback) {
const notifConfig = notificationConfig.error ? undefined : notificationConfig; const notifConfig = notificationConfig.error ? undefined : notificationConfig;
process.nextTick(() => next(notificationConfig.error, notifConfig)); process.nextTick(() => next(notificationConfig.error, notifConfig));
}, },
(notifConfig, next) => metadataValidateBucket(metadataValParams, log, (notifConfig, next) => metadataValidateBucket(metadataValParams, request.iamAuthzResults, log,
(err, bucket) => next(err, bucket, notifConfig)), (err, bucket) => next(err, bucket, notifConfig)),
(bucket, notifConfig, next) => { (bucket, notifConfig, next) => {
bucket.setNotificationConfiguration(notifConfig); bucket.setNotificationConfiguration(notifConfig);

View File

@ -26,7 +26,7 @@ function bucketPutObjectLock(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutObjectLock', requestType: request.apiMethods || 'bucketPutObjectLock',
request, request,
}; };
return waterfall([ return waterfall([
@ -41,7 +41,7 @@ function bucketPutObjectLock(authInfo, request, log, callback) {
return next(configObj.error || null, configObj); return next(configObj.error || null, configObj);
}); });
}, },
(objectLockConfig, next) => metadataValidateBucket(metadataValParams, (objectLockConfig, next) => metadataValidateBucket(metadataValParams, request.iamAuthzResults,
log, (err, bucket) => { log, (err, bucket) => {
if (err) { if (err) {
return next(err, bucket); return next(err, bucket);

View File

@ -17,8 +17,7 @@ const { BucketPolicy } = models;
function _checkNotImplementedPolicy(policyString) { function _checkNotImplementedPolicy(policyString) {
// bucket names and key names cannot include "", so including those // bucket names and key names cannot include "", so including those
// isolates not implemented keys // isolates not implemented keys
return policyString.includes('"Condition"') return policyString.includes('"Service"')
|| policyString.includes('"Service"')
|| policyString.includes('"Federated"'); || policyString.includes('"Federated"');
} }
@ -37,7 +36,7 @@ function bucketPutPolicy(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutPolicy', requestType: request.apiMethods || 'bucketPutPolicy',
request, request,
}; };
@ -70,7 +69,7 @@ function bucketPutPolicy(authInfo, request, log, callback) {
return next(null, bucketPolicy); return next(null, bucketPolicy);
}); });
}, },
(bucketPolicy, next) => metadataValidateBucket(metadataValParams, log, (bucketPolicy, next) => metadataValidateBucket(metadataValParams, request.iamAuthzResults, log,
(err, bucket) => { (err, bucket) => {
if (err) { if (err) {
return next(err, bucket); return next(err, bucket);

View File

@ -28,7 +28,7 @@ function bucketPutReplication(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutReplication', requestType: request.apiMethods || 'bucketPutReplication',
request, request,
}; };
return waterfall([ return waterfall([
@ -37,7 +37,7 @@ function bucketPutReplication(authInfo, request, log, callback) {
// Check bucket user privileges and ensure versioning is 'Enabled'. // Check bucket user privileges and ensure versioning is 'Enabled'.
(config, next) => (config, next) =>
// TODO: Validate that destination bucket exists and has versioning. // TODO: Validate that destination bucket exists and has versioning.
metadataValidateBucket(metadataValParams, log, (err, bucket) => { metadataValidateBucket(metadataValParams, request.iamAuthzResults, log, (err, bucket) => {
if (err) { if (err) {
return next(err); return next(err);
} }

View File

@ -38,11 +38,11 @@ function bucketPutTagging(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutTagging', requestType: request.apiMethods || 'bucketPutTagging',
}; };
let bucket = null; let bucket = null;
return waterfall([ return waterfall([
next => metadataValidateBucket(metadataValParams, log, next => metadataValidateBucket(metadataValParams, request.iamAuthzResults, log,
(err, b) => { (err, b) => {
bucket = b; bucket = b;
return next(err); return next(err);

View File

@ -88,13 +88,13 @@ function bucketPutVersioning(authInfo, request, log, callback) {
const metadataValParams = { const metadataValParams = {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutVersioning', requestType: request.apiMethods || 'bucketPutVersioning',
request, request,
}; };
return waterfall([ return waterfall([
next => _parseXML(request, log, next), next => _parseXML(request, log, next),
next => metadataValidateBucket(metadataValParams, log, next => metadataValidateBucket(metadataValParams, request.iamAuthzResults, log,
(err, bucket) => next(err, bucket)), // ignore extra null object, (err, bucket) => next(err, bucket)), // ignore extra null object,
(bucket, next) => parseString(request.post, (err, result) => { (bucket, next) => parseString(request.post, (err, result) => {
// just for linting; there should not be any parsing error here // just for linting; there should not be any parsing error here

View File

@ -49,7 +49,8 @@ function bucketPutWebsite(authInfo, request, log, callback) {
}); });
}, },
function validateBucketAuthorization(bucket, config, next) { function validateBucketAuthorization(bucket, config, next) {
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) { if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
request.iamAuthzResults, log, request)) {
log.debug('access denied for user on bucket', { log.debug('access denied for user on bucket', {
requestType, requestType,
method: 'bucketPutWebsite', method: 'bucketPutWebsite',

View File

@ -118,9 +118,9 @@ function completeMultipartUpload(authInfo, request, log, callback) {
bucketName, bucketName,
// Required permissions for this action // Required permissions for this action
// at the destinationBucket level are same as objectPut // at the destinationBucket level are same as objectPut
requestType: 'objectPut', requestType: request.apiMethods || 'objectPut',
}; };
metadataValidateBucketAndObj(metadataValParams, log, next); metadataValidateBucketAndObj(metadataValParams, request.iamAuthzResults, log, next);
}, },
function validateMultipart(destBucket, objMD, next) { function validateMultipart(destBucket, objMD, next) {
if (objMD) { if (objMD) {
@ -190,9 +190,14 @@ function completeMultipartUpload(authInfo, request, log, callback) {
const mdInfo = { storedParts, mpuOverviewKey, splitter }; const mdInfo = { storedParts, mpuOverviewKey, splitter };
const mpuInfo = const mpuInfo =
{ objectKey, uploadId, jsonList, bucketName, destBucket }; { objectKey, uploadId, jsonList, bucketName, destBucket };
const originalIdentityAuthzResults = request.iamAuthzResults;
// eslint-disable-next-line no-param-reassign
delete request.iamAuthzResults;
return data.completeMPU(request, mpuInfo, mdInfo, location, return data.completeMPU(request, mpuInfo, mdInfo, location,
null, null, null, locationConstraintCheck, log, null, null, null, locationConstraintCheck, log,
(err, completeObjData) => { (err, completeObjData) => {
// eslint-disable-next-line no-param-reassign
request.iamAuthzResults = originalIdentityAuthzResults;
if (err) { if (err) {
return next(err, destBucket); return next(err, destBucket);
} }

View File

@ -93,7 +93,7 @@ function initiateMultipartUpload(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
// Required permissions for this action are same as objectPut // Required permissions for this action are same as objectPut
requestType: 'objectPut', requestType: request.apiMethods || 'objectPut',
request, request,
}; };
const accountCanonicalID = authInfo.getCanonicalID(); const accountCanonicalID = authInfo.getCanonicalID();
@ -262,7 +262,7 @@ function initiateMultipartUpload(authInfo, request, log, callback) {
} }
async.waterfall([ async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log, next => metadataValidateBucketAndObj(metadataValParams, request.iamAuthzResults, log,
(error, destinationBucket) => { (error, destinationBucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, destinationBucket); request.method, destinationBucket);

View File

@ -96,8 +96,8 @@ function listMultipartUploads(authInfo, request, log, callback) {
// to list the multipart uploads so we have provided here that // to list the multipart uploads so we have provided here that
// the authorization to list multipart uploads is the same // the authorization to list multipart uploads is the same
// as listing objects in a bucket. // as listing objects in a bucket.
requestType: 'bucketGet', requestType: request.apiMethods || 'bucketGet',
preciseRequestType: 'listMultipartUploads', preciseRequestType: request.apiMethods || 'listMultipartUploads',
request, request,
}; };
@ -105,7 +105,7 @@ function listMultipartUploads(authInfo, request, log, callback) {
function waterfall1(next) { function waterfall1(next) {
// Check final destination bucket for authorization rather // Check final destination bucket for authorization rather
// than multipart upload bucket // than multipart upload bucket
metadataValidateBucket(metadataValParams, log, metadataValidateBucket(metadataValParams, request.iamAuthzResults, log,
(err, bucket) => next(err, bucket)); (err, bucket) => next(err, bucket));
}, },
function getMPUBucket(bucket, next) { function getMPUBucket(bucket, next) {

View File

@ -98,7 +98,7 @@ function listParts(authInfo, request, log, callback) {
bucketName, bucketName,
objectKey, objectKey,
uploadId, uploadId,
preciseRequestType: 'listParts', preciseRequestType: request.apiMethods || 'listParts',
request, request,
}; };
// For validating the request at the destinationBucket level // For validating the request at the destinationBucket level
@ -115,7 +115,7 @@ function listParts(authInfo, request, log, callback) {
async.waterfall([ async.waterfall([
function checkDestBucketVal(next) { function checkDestBucketVal(next) {
metadataValidateBucketAndObj(metadataValParams, log, metadataValidateBucketAndObj(metadataValParams, request.iamAuthzResults, log,
(err, destinationBucket) => { (err, destinationBucket) => {
if (err) { if (err) {
return next(err, destinationBucket, null); return next(err, destinationBucket, null);
@ -153,8 +153,13 @@ function listParts(authInfo, request, log, callback) {
mpuOverviewObj, mpuOverviewObj,
destBucket, destBucket,
}; };
const originalIdentityAuthzResults = request.iamAuthzResults;
// eslint-disable-next-line no-param-reassign
delete request.iamAuthzResults;
return data.listParts(mpuInfo, request, locationConstraintCheck, return data.listParts(mpuInfo, request, locationConstraintCheck,
log, (err, backendPartList) => { log, (err, backendPartList) => {
// eslint-disable-next-line no-param-reassign
request.iamAuthzResults = originalIdentityAuthzResults;
if (err) { if (err) {
return next(err, destBucket); return next(err, destBucket);
} }

View File

@ -11,7 +11,7 @@ const collectCorsHeaders = require('../utilities/collectCorsHeaders');
const metadata = require('../metadata/wrapper'); const metadata = require('../metadata/wrapper');
const services = require('../services'); const services = require('../services');
const vault = require('../auth/vault'); const vault = require('../auth/vault');
const { isBucketAuthorized } = const { isBucketAuthorized, evaluateBucketPolicyWithIAM } =
require('./apiUtils/authorization/permissionChecks'); require('./apiUtils/authorization/permissionChecks');
const { preprocessingVersioningDelete } const { preprocessingVersioningDelete }
= require('./apiUtils/object/versioning'); = require('./apiUtils/object/versioning');
@ -396,15 +396,37 @@ function multiObjectDelete(authInfo, request, log, callback) {
return next(null, quietSetting, objects); return next(null, quietSetting, objects);
}); });
}, },
function checkPolicies(quietSetting, objects, next) { function checkBucketMetadata(quietSetting, objects, next) {
const errorResults = [];
return metadata.getBucket(bucketName, log, (err, bucketMD) => {
if (err) {
log.trace('error retrieving bucket metadata', { error: err });
return next(err);
}
if (bucketShield(bucketMD, 'objectDelete')) {
return next(errors.NoSuchBucket);
}
if (!isBucketAuthorized(bucketMD, 'objectDelete', canonicalID, authInfo,
request.iamAuthzResults, authInfo)) {
log.trace("access denied due to bucket acl's");
objects.forEach(entry => {
errorResults.push({
entry,
error: errors.AccessDenied,
});
});
return next(null, quietSetting, errorResults, [], bucketMD);
}
return next(null, quietSetting, errorResults, objects, bucketMD);
});
},
function checkPolicies(quietSetting, errorResults, objects, bucketMD, next) {
// track keys that are still on track to be deleted // track keys that are still on track to be deleted
const inPlay = []; const inPlay = [];
const errorResults = [];
// if request from account, no need to check policies // if request from account, no need to check policies
// all objects are inPlay so send array of object keys
// as inPlay argument
if (!authInfo.isRequesterAnIAMUser()) { if (!authInfo.isRequesterAnIAMUser()) {
return next(null, quietSetting, errorResults, objects); return next(null, quietSetting, errorResults, objects, bucketMD);
} }
// TODO: once arsenal's extractParams is separated from doAuth // TODO: once arsenal's extractParams is separated from doAuth
@ -440,7 +462,6 @@ function multiObjectDelete(authInfo, request, log, callback) {
}; };
return vault.checkPolicies(requestContextParams, authInfo.getArn(), return vault.checkPolicies(requestContextParams, authInfo.getArn(),
log, (err, authorizationResults) => { log, (err, authorizationResults) => {
// there were no policies so received a blanket AccessDenied
if (err && err.is.AccessDenied) { if (err && err.is.AccessDenied) {
objects.forEach(entry => { objects.forEach(entry => {
errorResults.push({ errorResults.push({
@ -448,7 +469,7 @@ function multiObjectDelete(authInfo, request, log, callback) {
error: errors.AccessDenied }); error: errors.AccessDenied });
}); });
// send empty array for inPlay // send empty array for inPlay
return next(null, quietSetting, errorResults, []); return next(null, quietSetting, errorResults, [], bucketMD);
} }
if (err) { if (err) {
log.trace('error checking policies', { log.trace('error checking policies', {
@ -466,6 +487,16 @@ function multiObjectDelete(authInfo, request, log, callback) {
}); });
return next(errors.InternalError); return next(errors.InternalError);
} }
// Convert authorization results into an easier to handle format
const iamAuthzResults = authorizationResults.reduce((acc, curr, idx) => {
const apiMethod = requestContextParams[idx].apiMethod;
// eslint-disable-next-line no-param-reassign
acc[apiMethod] = curr.isImplicit;
return acc;
}, {});
for (let i = 0; i < authorizationResults.length; i++) { for (let i = 0; i < authorizationResults.length; i++) {
const result = authorizationResults[i]; const result = authorizationResults[i];
// result is { isAllowed: true, // result is { isAllowed: true,
@ -481,7 +512,27 @@ function multiObjectDelete(authInfo, request, log, callback) {
key: result.arn.slice(slashIndex + 1), key: result.arn.slice(slashIndex + 1),
versionId: result.versionId, versionId: result.versionId,
}; };
if (result.isAllowed) {
// Deny immediately if there is an explicit deny
if (!result.isImplicit && !result.isAllowed) {
errorResults.push({
entry,
error: errors.AccessDenied,
});
continue;
}
// Evaluate against the bucket policies
const areAllActionsAllowed = evaluateBucketPolicyWithIAM(
bucketMD,
Object.keys(iamAuthzResults),
canonicalID,
authInfo,
iamAuthzResults,
log,
request);
if (areAllActionsAllowed) {
inPlay.push(entry); inPlay.push(entry);
} else { } else {
errorResults.push({ errorResults.push({
@ -490,50 +541,9 @@ function multiObjectDelete(authInfo, request, log, callback) {
}); });
} }
} }
return next(null, quietSetting, errorResults, inPlay); return next(null, quietSetting, errorResults, inPlay, bucketMD);
}); });
}, },
function checkBucketMetadata(quietSetting, errorResults, inPlay, next) {
// if no objects in play, no need to check ACLs / get metadata,
// just move on if there is no Origin header
if (inPlay.length === 0 && !request.headers.origin) {
return next(null, quietSetting, errorResults, inPlay,
undefined);
}
return metadata.getBucket(bucketName, log, (err, bucketMD) => {
if (err) {
log.trace('error retrieving bucket metadata',
{ error: err });
return next(err);
}
// check whether bucket has transient or deleted flag
if (bucketShield(bucketMD, 'objectDelete')) {
return next(errors.NoSuchBucket);
}
// if no objects in play, no need to check ACLs
if (inPlay.length === 0) {
return next(null, quietSetting, errorResults, inPlay,
bucketMD);
}
if (!isBucketAuthorized(bucketMD, 'objectDelete', canonicalID, authInfo, log, request)) {
log.trace("access denied due to bucket acl's");
// if access denied at the bucket level, no access for
// any of the objects so all results will be error results
inPlay.forEach(entry => {
errorResults.push({
entry,
error: errors.AccessDenied,
});
});
// by sending an empty array as the inPlay array
// async.forEachLimit below will not actually
// make any calls to metadata or data but will continue on
// to the next step to build xml
return next(null, quietSetting, errorResults, [], bucketMD);
}
return next(null, quietSetting, errorResults, inPlay, bucketMD);
});
},
function getObjMetadataAndDeleteStep(quietSetting, errorResults, inPlay, function getObjMetadataAndDeleteStep(quietSetting, errorResults, inPlay,
bucket, next) { bucket, next) {
return getObjMetadataAndDelete(authInfo, canonicalID, request, return getObjMetadataAndDelete(authInfo, canonicalID, request,

View File

@ -209,20 +209,43 @@ function objectCopy(authInfo, request, sourceBucket,
const destObjectKey = request.objectKey; const destObjectKey = request.objectKey;
const sourceIsDestination = const sourceIsDestination =
destBucketName === sourceBucket && destObjectKey === sourceObject; destBucketName === sourceBucket && destObjectKey === sourceObject;
const apiMethodsOrigin = ['objectGet'];
// add any key from request.apiMethods starting with objectPut
// to apiMethodsOrigin
if (request.apiMethods) {
request.apiMethods.forEach(key => {
if (key !== 'objectGet' && key.startsWith('objectGet')) {
apiMethodsOrigin.push(key);
}
});
}
const valGetParams = { const valGetParams = {
authInfo, authInfo,
bucketName: sourceBucket, bucketName: sourceBucket,
objectKey: sourceObject, objectKey: sourceObject,
versionId: sourceVersionId, versionId: sourceVersionId,
getDeleteMarker: true, getDeleteMarker: true,
requestType: 'objectGet', requestType: apiMethodsOrigin || 'objectGet',
request, request,
}; };
const apiMethodsDestination = ['objectPut'];
// add any key from request.apiMethods starting with objectPut
// to apiMethodsDestination
if (request.apiMethods) {
request.apiMethods.forEach(key => {
if (key !== 'objectPut' && key.startsWith('objectPut')) {
apiMethodsDestination.push(key);
}
});
}
const valPutParams = { const valPutParams = {
authInfo, authInfo,
bucketName: destBucketName, bucketName: destBucketName,
objectKey: destObjectKey, objectKey: destObjectKey,
requestType: 'objectPut', requestType: apiMethodsDestination || 'objectPut',
request, request,
}; };
const dataStoreContext = { const dataStoreContext = {
@ -249,7 +272,7 @@ function objectCopy(authInfo, request, sourceBucket,
} }
return async.waterfall([ return async.waterfall([
function checkDestAuth(next) { function checkDestAuth(next) {
return metadataValidateBucketAndObj(valPutParams, log, return metadataValidateBucketAndObj(valPutParams, request.iamAuthzResults, log,
(err, destBucketMD, destObjMD) => { (err, destBucketMD, destObjMD) => {
if (err) { if (err) {
log.debug('error validating put part of request', log.debug('error validating put part of request',
@ -267,7 +290,7 @@ function objectCopy(authInfo, request, sourceBucket,
}); });
}, },
function checkSourceAuthorization(destBucketMD, destObjMD, next) { function checkSourceAuthorization(destBucketMD, destObjMD, next) {
return metadataValidateBucketAndObj(valGetParams, log, return metadataValidateBucketAndObj(valGetParams, request.iamAuthzResults, log,
(err, sourceBucketMD, sourceObjMD) => { (err, sourceBucketMD, sourceObjMD) => {
if (err) { if (err) {
log.debug('error validating get part of request', log.debug('error validating get part of request',
@ -412,10 +435,15 @@ function objectCopy(authInfo, request, sourceBucket,
return next(null, storeMetadataParams, dataLocator, destObjMD, return next(null, storeMetadataParams, dataLocator, destObjMD,
serverSideEncryption, destBucketMD); serverSideEncryption, destBucketMD);
} }
const originalIdentityAuthzResults = request.iamAuthzResults;
// eslint-disable-next-line no-param-reassign
delete request.iamAuthzResults;
return data.copyObject(request, sourceLocationConstraintName, return data.copyObject(request, sourceLocationConstraintName,
storeMetadataParams, dataLocator, dataStoreContext, storeMetadataParams, dataLocator, dataStoreContext,
backendInfoDest, sourceBucketMD, destBucketMD, serverSideEncryption, log, backendInfoDest, sourceBucketMD, destBucketMD, serverSideEncryption, log,
(err, results) => { (err, results) => {
// eslint-disable-next-line no-param-reassign
request.iamAuthzResults = originalIdentityAuthzResults;
if (err) { if (err) {
return next(err, destBucketMD); return next(err, destBucketMD);
} }

View File

@ -52,15 +52,15 @@ function objectDelete(authInfo, request, log, cb) {
bucketName, bucketName,
objectKey, objectKey,
versionId: reqVersionId, versionId: reqVersionId,
requestType: 'objectDelete', requestType: request.apiMethods || 'objectDelete',
request, request,
}; };
const canonicalID = authInfo.getCanonicalID(); const canonicalID = authInfo.getCanonicalID();
return async.waterfall([ return async.waterfall([
function validateBucketAndObj(next) { function validateBucketAndObj(next) {
return metadataValidateBucketAndObj(valParams, log, return metadataValidateBucketAndObj(valParams, request.iamAuthzResults, log,
(err, bucketMD, objMD) => { (err, bucketMD, objMD) => {
if (err) { if (err) {
return next(err, bucketMD); return next(err, bucketMD);
} }

View File

@ -44,12 +44,12 @@ function objectDeleteTagging(authInfo, request, log, callback) {
objectKey, objectKey,
versionId: reqVersionId, versionId: reqVersionId,
getDeleteMarker: true, getDeleteMarker: true,
requestType: 'objectDeleteTagging', requestType: request.apiMethods || 'objectDeleteTagging',
request, request,
}; };
return async.waterfall([ return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log, next => metadataValidateBucketAndObj(metadataValParams, request.iamAuthzResults, log,
(err, bucket, objectMD) => { (err, bucket, objectMD) => {
if (err) { if (err) {
log.trace('request authorization failed', log.trace('request authorization failed',

View File

@ -46,11 +46,11 @@ function objectGet(authInfo, request, returnTagCount, log, callback) {
objectKey, objectKey,
versionId, versionId,
getDeleteMarker: true, getDeleteMarker: true,
requestType: 'objectGet', requestType: request.apiMethods || 'objectGet',
request, request,
}; };
return metadataValidateBucketAndObj(mdValParams, log, return metadataValidateBucketAndObj(mdValParams, request.iamAuthzResults, log,
(err, bucket, objMD) => { (err, bucket, objMD) => {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket); request.method, bucket);

View File

@ -61,7 +61,7 @@ function objectGetACL(authInfo, request, log, callback) {
bucketName, bucketName,
objectKey, objectKey,
versionId, versionId,
requestType: 'objectGetACL', requestType: request.apiMethods || 'objectGetACL',
request, request,
}; };
const grantInfo = { const grantInfo = {
@ -74,7 +74,7 @@ function objectGetACL(authInfo, request, log, callback) {
return async.waterfall([ return async.waterfall([
function validateBucketAndObj(next) { function validateBucketAndObj(next) {
return metadataValidateBucketAndObj(metadataValParams, log, return metadataValidateBucketAndObj(metadataValParams, request.iamAuthzResults, log,
(err, bucket, objectMD) => { (err, bucket, objectMD) => {
if (err) { if (err) {
log.trace('request authorization failed', log.trace('request authorization failed',

View File

@ -40,12 +40,12 @@ function objectGetLegalHold(authInfo, request, log, callback) {
bucketName, bucketName,
objectKey, objectKey,
versionId, versionId,
requestType: 'objectGetLegalHold', requestType: request.apiMethods || 'objectGetLegalHold',
request, request,
}; };
return async.waterfall([ return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log, next => metadataValidateBucketAndObj(metadataValParams, request.iamAuthzResults, log,
(err, bucket, objectMD) => { (err, bucket, objectMD) => {
if (err) { if (err) {
log.trace('request authorization failed', log.trace('request authorization failed',

View File

@ -40,12 +40,12 @@ function objectGetRetention(authInfo, request, log, callback) {
bucketName, bucketName,
objectKey, objectKey,
versionId: reqVersionId, versionId: reqVersionId,
requestType: 'objectGetRetention', requestType: request.apiMethods || 'objectGetRetention',
request, request,
}; };
return async.waterfall([ return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log, next => metadataValidateBucketAndObj(metadataValParams, request.iamAuthzResults, log,
(err, bucket, objectMD) => { (err, bucket, objectMD) => {
if (err) { if (err) {
log.trace('request authorization failed', log.trace('request authorization failed',

View File

@ -41,12 +41,11 @@ function objectGetTagging(authInfo, request, log, callback) {
bucketName, bucketName,
objectKey, objectKey,
versionId: reqVersionId, versionId: reqVersionId,
requestType: 'objectGetTagging', requestType: request.apiMethods || 'objectGetTagging',
request, request,
}; };
return async.waterfall([ return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log, next => metadataValidateBucketAndObj(metadataValParams, request.iamAuthzResults, log,
(err, bucket, objectMD) => { (err, bucket, objectMD) => {
if (err) { if (err) {
log.trace('request authorization failed', log.trace('request authorization failed',

View File

@ -46,11 +46,11 @@ function objectHead(authInfo, request, log, callback) {
objectKey, objectKey,
versionId, versionId,
getDeleteMarker: true, getDeleteMarker: true,
requestType: 'objectHead', requestType: request.apiMethods || 'objectHead',
request, request,
}; };
return metadataValidateBucketAndObj(mdValParams, log, return metadataValidateBucketAndObj(mdValParams, request.iamAuthzResults, log,
(err, bucket, objMD) => { (err, bucket, objMD) => {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket); request.method, bucket);

View File

@ -59,7 +59,7 @@ function objectPut(authInfo, request, streamingV4Params, log, callback) {
} }
const invalidSSEError = errors.InvalidArgument.customizeDescription( const invalidSSEError = errors.InvalidArgument.customizeDescription(
'The encryption method specified is not supported'); 'The encryption method specified is not supported');
const requestType = 'objectPut'; const requestType = request.apiMethods || 'objectPut';
const valParams = { authInfo, bucketName, objectKey, requestType, request }; const valParams = { authInfo, bucketName, objectKey, requestType, request };
const canonicalID = authInfo.getCanonicalID(); const canonicalID = authInfo.getCanonicalID();
@ -70,8 +70,7 @@ function objectPut(authInfo, request, streamingV4Params, log, callback) {
} }
log.trace('owner canonicalID to send to data', { canonicalID }); log.trace('owner canonicalID to send to data', { canonicalID });
return metadataValidateBucketAndObj(valParams, request.iamAuthzResults, log,
return metadataValidateBucketAndObj(valParams, log,
(err, bucket, objMD) => { (err, bucket, objMD) => {
const responseHeaders = collectCorsHeaders(headers.origin, const responseHeaders = collectCorsHeaders(headers.origin,
method, bucket); method, bucket);

View File

@ -88,7 +88,7 @@ function objectPutACL(authInfo, request, log, cb) {
objectKey, objectKey,
versionId: reqVersionId, versionId: reqVersionId,
getDeleteMarker: true, getDeleteMarker: true,
requestType: 'objectPutACL', requestType: request.apiMethods || 'objectPutACL',
}; };
const possibleGrants = ['FULL_CONTROL', 'WRITE_ACP', 'READ', 'READ_ACP']; const possibleGrants = ['FULL_CONTROL', 'WRITE_ACP', 'READ', 'READ_ACP'];
@ -112,7 +112,7 @@ function objectPutACL(authInfo, request, log, cb) {
return async.waterfall([ return async.waterfall([
function validateBucketAndObj(next) { function validateBucketAndObj(next) {
return metadataValidateBucketAndObj(metadataValParams, log, return metadataValidateBucketAndObj(metadataValParams, request.iamAuthzResults, log,
(err, bucket, objectMD) => { (err, bucket, objectMD) => {
if (err) { if (err) {
return next(err); return next(err);

View File

@ -63,7 +63,6 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
// request.query.uploadId must be called with that exact // request.query.uploadId must be called with that exact
// capitalization // capitalization
const uploadId = request.query.uploadId; const uploadId = request.query.uploadId;
const valPutParams = { const valPutParams = {
authInfo, authInfo,
bucketName: destBucketName, bucketName: destBucketName,
@ -93,7 +92,7 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
return async.waterfall([ return async.waterfall([
function checkDestAuth(next) { function checkDestAuth(next) {
return metadataValidateBucketAndObj(valPutParams, log, return metadataValidateBucketAndObj(valPutParams, request.iamAuthzResults, log,
(err, destBucketMD) => { (err, destBucketMD) => {
if (err) { if (err) {
log.debug('error validating authorization for ' + log.debug('error validating authorization for ' +
@ -112,7 +111,7 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
}); });
}, },
function checkSourceAuthorization(destBucketMD, next) { function checkSourceAuthorization(destBucketMD, next) {
return metadataValidateBucketAndObj(valGetParams, log, return metadataValidateBucketAndObj(valGetParams, request.iamAuthzResults, log,
(err, sourceBucketMD, sourceObjMD) => { (err, sourceBucketMD, sourceObjMD) => {
if (err) { if (err) {
log.debug('error validating get part of request', log.debug('error validating get part of request',
@ -253,6 +252,9 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
splitter, splitter,
next, next,
) { ) {
const originalIdentityAuthzResults = request.iamAuthzResults;
// eslint-disable-next-line no-param-reassign
delete request.iamAuthzResults;
data.uploadPartCopy( data.uploadPartCopy(
request, request,
log, log,
@ -263,6 +265,8 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
dataStoreContext, dataStoreContext,
locationConstraintCheck, locationConstraintCheck,
(error, eTag, lastModified, serverSideEncryption, locations) => { (error, eTag, lastModified, serverSideEncryption, locations) => {
// eslint-disable-next-line no-param-reassign
request.iamAuthzResults = originalIdentityAuthzResults;
if (error) { if (error) {
if (error.message === 'skip') { if (error.message === 'skip') {
return next(skipError, destBucketMD, eTag, return next(skipError, destBucketMD, eTag,

View File

@ -43,12 +43,12 @@ function objectPutLegalHold(authInfo, request, log, callback) {
objectKey, objectKey,
versionId, versionId,
getDeleteMarker: true, getDeleteMarker: true,
requestType: 'objectPutLegalHold', requestType: request.apiMethods || 'objectPutLegalHold',
request, request,
}; };
return async.waterfall([ return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log, next => metadataValidateBucketAndObj(metadataValParams, request.iamAuthzResults, log,
(err, bucket, objectMD) => { (err, bucket, objectMD) => {
if (err) { if (err) {
log.trace('request authorization failed', log.trace('request authorization failed',

View File

@ -94,6 +94,7 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
const uploadId = request.query.uploadId; const uploadId = request.query.uploadId;
const mpuBucketName = `${constants.mpuBucketPrefix}${bucketName}`; const mpuBucketName = `${constants.mpuBucketPrefix}${bucketName}`;
const objectKey = request.objectKey; const objectKey = request.objectKey;
const originalIdentityAuthzResults = request.iamAuthzResults;
return async.waterfall([ return async.waterfall([
// Get the destination bucket. // Get the destination bucket.
@ -116,7 +117,8 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
// For validating the request at the destinationBucket level the // For validating the request at the destinationBucket level the
// `requestType` is the general 'objectPut'. // `requestType` is the general 'objectPut'.
const requestType = 'objectPut'; const requestType = 'objectPut';
if (!isBucketAuthorized(destinationBucket, requestType, canonicalID, authInfo, log, request)) { if (!isBucketAuthorized(destinationBucket, request.apiMethods || requestType, canonicalID, authInfo,
request.iamAuthzResults, log, request)) {
log.debug('access denied for user on bucket', { requestType }); log.debug('access denied for user on bucket', { requestType });
return next(errors.AccessDenied, destinationBucket); return next(errors.AccessDenied, destinationBucket);
} }
@ -203,6 +205,8 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
partNumber, partNumber,
bucketName, bucketName,
}; };
// eslint-disable-next-line no-param-reassign
delete request.iamAuthzResults;
writeContinue(request, request._response); writeContinue(request, request._response);
return data.putPart(request, mpuInfo, streamingV4Params, return data.putPart(request, mpuInfo, streamingV4Params,
objectLocationConstraint, locationConstraintCheck, log, objectLocationConstraint, locationConstraintCheck, log,
@ -385,6 +389,8 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
], (err, destinationBucket, hexDigest, prevObjectSize) => { ], (err, destinationBucket, hexDigest, prevObjectSize) => {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, destinationBucket); request.method, destinationBucket);
// eslint-disable-next-line no-param-reassign
request.iamAuthzResults = originalIdentityAuthzResults;
if (err) { if (err) {
if (err === skipError) { if (err === skipError) {
return cb(null, hexDigest, corsHeaders); return cb(null, hexDigest, corsHeaders);

View File

@ -44,12 +44,27 @@ function objectPutRetention(authInfo, request, log, callback) {
objectKey, objectKey,
versionId: reqVersionId, versionId: reqVersionId,
getDeleteMarker: true, getDeleteMarker: true,
requestType: 'objectPutRetention', requestType: request.apiMethods || 'objectPutRetention',
request, request,
}; };
return async.waterfall([ return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log, next => {
log.trace('parsing retention information');
parseRetentionXml(request.post, log,
(err, retentionInfo) => {
if (err) {
log.trace('error parsing retention information',
{ error: err });
return next(err);
}
const remainingDays = Math.ceil(
(new Date(retentionInfo.date) - Date.now()) / (1000 * 3600 * 24));
metadataValParams.request.objectLockRetentionDays = remainingDays;
return next(null, retentionInfo);
});
},
(retentionInfo, next) => metadataValidateBucketAndObj(metadataValParams, request.iamAuthzResults, log,
(err, bucket, objectMD) => { (err, bucket, objectMD) => {
if (err) { if (err) {
log.trace('request authorization failed', log.trace('request authorization failed',
@ -77,13 +92,8 @@ function objectPutRetention(authInfo, request, log, callback) {
'Bucket is missing Object Lock Configuration' 'Bucket is missing Object Lock Configuration'
), bucket); ), bucket);
} }
return next(null, bucket, objectMD); return next(null, bucket, retentionInfo, objectMD);
}), }),
(bucket, objectMD, next) => {
log.trace('parsing retention information');
parseRetentionXml(request.post, log,
(err, retentionInfo) => next(err, bucket, retentionInfo, objectMD));
},
(bucket, retentionInfo, objectMD, next) => { (bucket, retentionInfo, objectMD, next) => {
const hasGovernanceBypass = hasGovernanceBypassHeader(request.headers); const hasGovernanceBypass = hasGovernanceBypassHeader(request.headers);
if (hasGovernanceBypass && authInfo.isRequesterAnIAMUser()) { if (hasGovernanceBypass && authInfo.isRequesterAnIAMUser()) {

View File

@ -45,12 +45,12 @@ function objectPutTagging(authInfo, request, log, callback) {
objectKey, objectKey,
versionId: reqVersionId, versionId: reqVersionId,
getDeleteMarker: true, getDeleteMarker: true,
requestType: 'objectPutTagging', requestType: request.apiMethods || 'objectPutTagging',
request, request,
}; };
return async.waterfall([ return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log, next => metadataValidateBucketAndObj(metadataValParams, request.iamAuthzResults, log,
(err, bucket, objectMD) => { (err, bucket, objectMD) => {
if (err) { if (err) {
log.trace('request authorization failed', log.trace('request authorization failed',

View File

@ -22,12 +22,13 @@ const monitoring = require('../utilities/metrics');
* @param {string} objectKey - object key from request (or as translated in * @param {string} objectKey - object key from request (or as translated in
* websiteGet) * websiteGet)
* @param {object} corsHeaders - CORS-related response headers * @param {object} corsHeaders - CORS-related response headers
* @param {object} request - normalized request object
* @param {object} log - Werelogs instance * @param {object} log - Werelogs instance
* @param {function} callback - callback to function in route * @param {function} callback - callback to function in route
* @return {undefined} * @return {undefined}
*/ */
function _errorActions(err, errorDocument, routingRules, function _errorActions(err, errorDocument, routingRules,
bucket, objectKey, corsHeaders, log, callback) { bucket, objectKey, corsHeaders, request, log, callback) {
const bucketName = bucket.getName(); const bucketName = bucket.getName();
const errRoutingRule = findRoutingRule(routingRules, const errRoutingRule = findRoutingRule(routingRules,
objectKey, err.code); objectKey, err.code);
@ -51,8 +52,8 @@ function _errorActions(err, errorDocument, routingRules,
} }
// return the default error message if the object is private // return the default error message if the object is private
// rather than sending a stored error file // rather than sending a stored error file
if (!isObjAuthorized(bucket, errObjMD, 'objectGet', if (!isObjAuthorized(bucket, errObjMD, request.apiMethods || 'objectGet',
constants.publicId, null, log)) { constants.publicId, null, request.iamAuthzResults, log)) {
log.trace('errorObj not authorized', { error: err }); log.trace('errorObj not authorized', { error: err });
monitoring.promMetrics( monitoring.promMetrics(
'GET', bucketName, err.code, 'getObject'); 'GET', bucketName, err.code, 'getObject');
@ -162,8 +163,8 @@ function websiteGet(request, log, callback) {
monitoring.promMetrics( monitoring.promMetrics(
'GET', bucketName, err.code, 'getObject'); 'GET', bucketName, err.code, 'getObject');
let returnErr = err; let returnErr = err;
const bucketAuthorized = isBucketAuthorized(bucket, const bucketAuthorized = isBucketAuthorized(bucket, request.apiMethods || 'bucketGet',
'bucketGet', constants.publicId, null, log, request); constants.publicId, null, request.iamAuthzResults, log, request);
// if index object does not exist and bucket is private AWS // if index object does not exist and bucket is private AWS
// returns 403 - AccessDenied error. // returns 403 - AccessDenied error.
if (err.is.NoSuchKey && !bucketAuthorized) { if (err.is.NoSuchKey && !bucketAuthorized) {
@ -171,16 +172,16 @@ function websiteGet(request, log, callback) {
} }
return _errorActions(returnErr, return _errorActions(returnErr,
websiteConfig.getErrorDocument(), routingRules, websiteConfig.getErrorDocument(), routingRules,
bucket, reqObjectKey, corsHeaders, log, bucket, reqObjectKey, corsHeaders, request, log,
callback); callback);
} }
if (!isObjAuthorized(bucket, objMD, 'objectGet', if (!isObjAuthorized(bucket, objMD, request.apiMethods || 'objectGet',
constants.publicId, null, log, request)) { constants.publicId, null, request.iamAuthzResults, log, request)) {
const err = errors.AccessDenied; const err = errors.AccessDenied;
log.trace('request not authorized', { error: err }); log.trace('request not authorized', { error: err });
return _errorActions(err, websiteConfig.getErrorDocument(), return _errorActions(err, websiteConfig.getErrorDocument(),
routingRules, bucket, routingRules, bucket,
reqObjectKey, corsHeaders, log, callback); reqObjectKey, corsHeaders, request, log, callback);
} }
const headerValResult = validateHeaders(request.headers, const headerValResult = validateHeaders(request.headers,
@ -190,7 +191,7 @@ function websiteGet(request, log, callback) {
log.trace('header validation error', { error: err }); log.trace('header validation error', { error: err });
return _errorActions(err, websiteConfig.getErrorDocument(), return _errorActions(err, websiteConfig.getErrorDocument(),
routingRules, bucket, reqObjectKey, routingRules, bucket, reqObjectKey,
corsHeaders, log, callback); corsHeaders, request, log, callback);
} }
// check if object to serve has website redirect header // check if object to serve has website redirect header
// Note: AWS prioritizes website configuration rules over // Note: AWS prioritizes website configuration rules over

View File

@ -110,8 +110,8 @@ function websiteHead(request, log, callback) {
log.trace('error retrieving object metadata', log.trace('error retrieving object metadata',
{ error: err }); { error: err });
let returnErr = err; let returnErr = err;
const bucketAuthorized = isBucketAuthorized(bucket, const bucketAuthorized = isBucketAuthorized(bucket, request.apiMethods || 'bucketGet',
'bucketGet', constants.publicId, null, log, request); constants.publicId, null, request.iamAuthzResults, log, request);
// if index object does not exist and bucket is private AWS // if index object does not exist and bucket is private AWS
// returns 403 - AccessDenied error. // returns 403 - AccessDenied error.
if (err.is.NoSuchKey && !bucketAuthorized) { if (err.is.NoSuchKey && !bucketAuthorized) {
@ -120,8 +120,8 @@ function websiteHead(request, log, callback) {
return _errorActions(returnErr, routingRules, return _errorActions(returnErr, routingRules,
reqObjectKey, corsHeaders, log, callback); reqObjectKey, corsHeaders, log, callback);
} }
if (!isObjAuthorized(bucket, objMD, 'objectGet', if (!isObjAuthorized(bucket, objMD, request.apiMethods || 'objectGet',
constants.publicId, null, log, request)) { constants.publicId, null, request.iamAuthzResults, log, request)) {
const err = errors.AccessDenied; const err = errors.AccessDenied;
log.trace('request not authorized', { error: err }); log.trace('request not authorized', { error: err });
return _errorActions(err, routingRules, reqObjectKey, return _errorActions(err, routingRules, reqObjectKey,

View File

@ -94,6 +94,7 @@ function metadataGetObject(bucketName, objectKey, versionId, log, cb) {
* @param {string} params.requestType - type of request * @param {string} params.requestType - type of request
* @param {string} [params.preciseRequestType] - precise type of request * @param {string} [params.preciseRequestType] - precise type of request
* @param {object} params.request - http request object * @param {object} params.request - http request object
* @param {object} iamAuthzResults - identity authorization results
* @param {RequestLogger} log - request logger * @param {RequestLogger} log - request logger
* @return {ArsenalError|null} returns a validation error, or null if validation OK * @return {ArsenalError|null} returns a validation error, or null if validation OK
* The following errors may be returned: * The following errors may be returned:
@ -102,8 +103,9 @@ function metadataGetObject(bucketName, objectKey, versionId, log, cb) {
* bucket policy operation * bucket policy operation
* - AccessDenied: bucket is not authorized * - AccessDenied: bucket is not authorized
*/ */
function validateBucket(bucket, params, log) { function validateBucket(bucket, params, iamAuthzResults, log) {
const { authInfo, requestType, preciseRequestType, request } = params; const { authInfo, preciseRequestType, request } = params;
let requestType = params.requestType;
if (bucketShield(bucket, requestType)) { if (bucketShield(bucket, requestType)) {
log.debug('bucket is shielded from request', { log.debug('bucket is shielded from request', {
requestType, requestType,
@ -115,11 +117,14 @@ function validateBucket(bucket, params, log) {
// MethodNotAllowed error // MethodNotAllowed error
const onlyOwnerAllowed = ['bucketDeletePolicy', 'bucketGetPolicy', 'bucketPutPolicy']; const onlyOwnerAllowed = ['bucketDeletePolicy', 'bucketGetPolicy', 'bucketPutPolicy'];
const canonicalID = authInfo.getCanonicalID(); const canonicalID = authInfo.getCanonicalID();
if (bucket.getOwner() !== canonicalID && onlyOwnerAllowed.includes(requestType)) { if (!Array.isArray(requestType)) {
requestType = [requestType];
}
if (bucket.getOwner() !== canonicalID && requestType.some(type => onlyOwnerAllowed.includes(type))) {
return errors.MethodNotAllowed; return errors.MethodNotAllowed;
} }
if (!isBucketAuthorized(bucket, (preciseRequestType || requestType), canonicalID, if (!isBucketAuthorized(bucket, (preciseRequestType || requestType), canonicalID,
authInfo, log, request)) { authInfo, iamAuthzResults, log, request)) {
log.debug('access denied for user on bucket', { requestType }); log.debug('access denied for user on bucket', { requestType });
return errors.AccessDenied; return errors.AccessDenied;
} }
@ -135,13 +140,17 @@ function validateBucket(bucket, params, log) {
* @param {string} [params.versionId] - version id if getting specific version * @param {string} [params.versionId] - version id if getting specific version
* @param {string} params.requestType - type of request * @param {string} params.requestType - type of request
* @param {object} params.request - http request object * @param {object} params.request - http request object
* @param {boolean} iamAuthzResults - identity authorization results
* @param {RequestLogger} log - request logger * @param {RequestLogger} log - request logger
* @param {function} callback - callback * @param {function} callback - callback
* @return {undefined} - and call callback with params err, bucket md * @return {undefined} - and call callback with params err, bucket md
*/ */
function metadataValidateBucketAndObj(params, log, callback) { function metadataValidateBucketAndObj(params, iamAuthzResults, log, callback) {
const { authInfo, bucketName, objectKey, versionId, getDeleteMarker, const { authInfo, bucketName, objectKey, versionId, getDeleteMarker, request } = params;
requestType, request } = params; let requestType = params.requestType;
if (!Array.isArray(requestType)) {
requestType = [requestType];
}
async.waterfall([ async.waterfall([
next => { next => {
// versionId may be 'null', which asks metadata to fetch the null key specifically // versionId may be 'null', which asks metadata to fetch the null key specifically
@ -149,7 +158,17 @@ function metadataValidateBucketAndObj(params, log, callback) {
if (getDeleteMarker) { if (getDeleteMarker) {
getOptions.getDeleteMarker = true; getOptions.getDeleteMarker = true;
} }
return metadata.getBucketAndObjectMD(bucketName, objectKey, getOptions, log, next); return metadata.getBucketAndObjectMD(bucketName, objectKey, getOptions, log, (err, getResult) => {
if (err) {
// if some implicit iamAuthzResults, return AccessDenied
// before leaking any state information
if (iamAuthzResults && Object.values(iamAuthzResults).some(v => v === true)) {
return next(errors.AccessDenied);
}
return next(err);
}
return next(null, getResult);
});
}, },
(getResult, next) => { (getResult, next) => {
const bucket = getResult.bucket ? const bucket = getResult.bucket ?
@ -161,7 +180,7 @@ function metadataValidateBucketAndObj(params, log, callback) {
}); });
return next(errors.NoSuchBucket); return next(errors.NoSuchBucket);
} }
const validationError = validateBucket(bucket, params, log); const validationError = validateBucket(bucket, params, iamAuthzResults, log);
if (validationError) { if (validationError) {
return next(validationError, bucket); return next(validationError, bucket);
} }
@ -174,7 +193,8 @@ function metadataValidateBucketAndObj(params, log, callback) {
}, },
(bucket, objMD, next) => { (bucket, objMD, next) => {
const canonicalID = authInfo.getCanonicalID(); const canonicalID = authInfo.getCanonicalID();
if (!isObjAuthorized(bucket, objMD, requestType, canonicalID, authInfo, log, request)) { if (!isObjAuthorized(bucket, objMD, requestType, canonicalID, authInfo, iamAuthzResults,
log, request)) {
log.debug('access denied for user on object', { requestType }); log.debug('access denied for user on object', { requestType });
return next(errors.AccessDenied, bucket); return next(errors.AccessDenied, bucket);
} }
@ -196,18 +216,24 @@ function metadataValidateBucketAndObj(params, log, callback) {
* @param {string} params.bucketName - name of bucket * @param {string} params.bucketName - name of bucket
* @param {string} params.requestType - type of request * @param {string} params.requestType - type of request
* @param {string} params.request - http request object * @param {string} params.request - http request object
* @param {boolean} iamAuthzResults - identity authorization results
* @param {RequestLogger} log - request logger * @param {RequestLogger} log - request logger
* @param {function} callback - callback * @param {function} callback - callback
* @return {undefined} - and call callback with params err, bucket md * @return {undefined} - and call callback with params err, bucket md
*/ */
function metadataValidateBucket(params, log, callback) { function metadataValidateBucket(params, iamAuthzResults, log, callback) {
const { bucketName } = params; const { bucketName } = params;
return metadata.getBucket(bucketName, log, (err, bucket) => { return metadata.getBucket(bucketName, log, (err, bucket) => {
if (err) { if (err) {
// if some implicit iamAuthzResults, return AccessDenied before
// leaking any state information
if (iamAuthzResults && Object.values(iamAuthzResults).some(v => v === true)) {
return callback(errors.AccessDenied);
}
log.debug('metadata getbucket failed', { error: err }); log.debug('metadata getbucket failed', { error: err });
return callback(err); return callback(err);
} }
const validationError = validateBucket(bucket, params, log); const validationError = validateBucket(bucket, params, iamAuthzResults, log);
return callback(validationError, bucket); return callback(validationError, bucket);
}); });
} }

View File

@ -1147,6 +1147,8 @@ function routeBackbeat(clientIP, request, response, log) {
// Attach the apiMethod method to the request, so it can used by monitoring in the server // Attach the apiMethod method to the request, so it can used by monitoring in the server
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
request.apiMethod = 'routeBackbeat'; request.apiMethod = 'routeBackbeat';
// eslint-disable-next-line no-param-reassign
request.iamAuthzResults = false;
log.debug('routing request', { log.debug('routing request', {
method: 'routeBackbeat', method: 'routeBackbeat',
@ -1268,10 +1270,10 @@ function routeBackbeat(clientIP, request, response, log) {
objectKey: request.objectKey, objectKey: request.objectKey,
authInfo: userInfo, authInfo: userInfo,
versionId, versionId,
requestType: 'ReplicateObject', requestType: request.apiMethods || 'ReplicateObject',
request, request,
}; };
return metadataValidateBucketAndObj(mdValParams, log, next); return metadataValidateBucketAndObj(mdValParams, request.iamAuthzResults, log, next);
}, },
(bucketInfo, objMd, next) => { (bucketInfo, objMd, next) => {
if (useMultipleBackend) { if (useMultipleBackend) {

View File

@ -20,7 +20,7 @@
"homepage": "https://github.com/scality/S3#readme", "homepage": "https://github.com/scality/S3#readme",
"dependencies": { "dependencies": {
"@hapi/joi": "^17.1.0", "@hapi/joi": "^17.1.0",
"arsenal": "git+https://github.com/scality/arsenal#7.70.4", "arsenal": "git+https://github.com/scality/arsenal#f4894a6d6ebb36ba1a559de4811180cb942d55a7",
"async": "~2.5.0", "async": "~2.5.0",
"aws-sdk": "2.905.0", "aws-sdk": "2.905.0",
"azure-storage": "^2.1.0", "azure-storage": "^2.1.0",

View File

@ -276,7 +276,7 @@ function _checkHeaders(action, params, origin, expectedHeaders, callback) {
request.send(); request.send();
} }
describe('Cross Origin Resource Sharing requests', () => { describe.skip('Cross Origin Resource Sharing requests', () => {
beforeEach(done => { beforeEach(done => {
s3.createBucket({ Bucket: bucket, ACL: 'public-read-write' }, err => { s3.createBucket({ Bucket: bucket, ACL: 'public-read-write' }, err => {
if (err) { if (err) {

View File

@ -76,7 +76,7 @@ function hydrateSSEConfig({ algo: SSEAlgorithm, masterKeyId: KMSMasterKeyID }) {
); );
} }
describe('per object encryption headers', () => { describe.skip('per object encryption headers', () => {
withV4(sigCfg => { withV4(sigCfg => {
let bucket; let bucket;
let bucket2; let bucket2;

View File

@ -54,6 +54,7 @@ const bucketPutRequest = {
url: '/', url: '/',
post: '', post: '',
parsedHost: 'localhost', parsedHost: 'localhost',
iamAuthzResults: false,
}; };
const awsETag = 'be747eb4b75517bf6b3cf7c5fbb62f3a'; const awsETag = 'be747eb4b75517bf6b3cf7c5fbb62f3a';
@ -73,6 +74,7 @@ const completeBody = '<CompleteMultipartUpload>' +
const basicParams = { const basicParams = {
bucketName, bucketName,
namespace, namespace,
iamAuthzResults: false,
}; };
function getObjectGetRequest(objectKey) { function getObjectGetRequest(objectKey) {
@ -270,6 +272,7 @@ function mpuSetup(location, key, cb) {
'x-amz-meta-scal-location-constraint': location }, 'x-amz-meta-scal-location-constraint': location },
url: `/${key}?uploads`, url: `/${key}?uploads`,
parsedHost: 'localhost', parsedHost: 'localhost',
iamAuthzResults: false,
}; };
initiateMultipartUpload(authInfo, initiateRequest, log, initiateMultipartUpload(authInfo, initiateRequest, log,
(err, result) => { (err, result) => {
@ -342,6 +345,7 @@ describe('Multipart Upload API with AWS Backend', function mpuTestSuite() {
'x-amz-meta-scal-location-constraint': `${awsLocation}` }, 'x-amz-meta-scal-location-constraint': `${awsLocation}` },
url: `/${objectKey}?uploads`, url: `/${objectKey}?uploads`,
parsedHost: 'localhost', parsedHost: 'localhost',
iamAuthzResults: false,
}; };
initiateMultipartUpload(authInfo, initiateRequest, log, initiateMultipartUpload(authInfo, initiateRequest, log,
@ -365,6 +369,7 @@ describe('Multipart Upload API with AWS Backend', function mpuTestSuite() {
`${awsLocationMismatch}` }, `${awsLocationMismatch}` },
url: `/${objectKey}?uploads`, url: `/${objectKey}?uploads`,
parsedHost: 'localhost', parsedHost: 'localhost',
iamAuthzResults: false,
}; };
initiateMultipartUpload(authInfo, initiateRequest, log, initiateMultipartUpload(authInfo, initiateRequest, log,
@ -389,6 +394,7 @@ describe('Multipart Upload API with AWS Backend', function mpuTestSuite() {
}, },
url: `/${objectKey}?uploads`, url: `/${objectKey}?uploads`,
parsedHost: 'localhost', parsedHost: 'localhost',
iamAuthzResults: false,
}; };
initiateMultipartUpload(authInfo, initiateRequest, log, initiateMultipartUpload(authInfo, initiateRequest, log,
@ -612,6 +618,7 @@ describe('Multipart Upload API with AWS Backend', function mpuTestSuite() {
'x-amz-meta-scal-location-constraint': awsLocation }, 'x-amz-meta-scal-location-constraint': awsLocation },
url: `/${objectKey}?uploads`, url: `/${objectKey}?uploads`,
parsedHost: 'localhost', parsedHost: 'localhost',
iamAuthzResults: false,
}; };
initiateMultipartUpload(authInfo, initiateRequest, log, initiateMultipartUpload(authInfo, initiateRequest, log,
err => { err => {
@ -712,6 +719,7 @@ describe('Multipart Upload API with AWS Backend', function mpuTestSuite() {
headers: { host: '/' }, headers: { host: '/' },
url: `/${bucketName}?uploads`, url: `/${bucketName}?uploads`,
query: {}, query: {},
iamAuthzResults: false,
}; };
listMultipartUploads(authInfo, listMpuParams, log, listMultipartUploads(authInfo, listMpuParams, log,
(err, mpuListXml) => { (err, mpuListXml) => {

View File

@ -80,6 +80,7 @@ errorPutCopyPart) {
objectKey: destObjName, objectKey: destObjName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: `/${destObjName}?uploads`, url: `/${destObjName}?uploads`,
iamAuthzResults: false,
}; };
if (mpuLoc) { if (mpuLoc) {
initiateReq.headers = { 'host': `${bucketName}.s3.amazonaws.com`, initiateReq.headers = { 'host': `${bucketName}.s3.amazonaws.com`,
@ -94,6 +95,7 @@ errorPutCopyPart) {
objectKey: sourceObjName, objectKey: sourceObjName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
if (srcObjLoc) { if (srcObjLoc) {
sourceObjPutParams.headers = { 'host': `${bucketName}.s3.amazonaws.com`, sourceObjPutParams.headers = { 'host': `${bucketName}.s3.amazonaws.com`,

View File

@ -70,6 +70,7 @@ errorDescription) {
objectKey: objectName, objectKey: objectName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: `/${objectName}?uploads`, url: `/${objectName}?uploads`,
iamAuthzResults: false,
}; };
if (mpuLoc) { if (mpuLoc) {
initiateReq.headers = { 'host': `${bucketName}.s3.amazonaws.com`, initiateReq.headers = { 'host': `${bucketName}.s3.amazonaws.com`,

View File

@ -16,7 +16,7 @@ class DummyRequest extends http.IncomingMessage {
this.parsedContentLength = 0; this.parsedContentLength = 0;
} }
} }
this.iamAuthzResults = false;
if (Array.isArray(msg)) { if (Array.isArray(msg)) {
msg.forEach(part => { msg.forEach(part => {
this.push(part); this.push(part);

View File

@ -24,6 +24,7 @@ const bucketPutReq = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
const taggingUtil = new TaggingConfigTester(); const taggingUtil = new TaggingConfigTester();

View File

@ -106,7 +106,7 @@ describe('bucket authorization for bucketGet, bucketHead, ' +
} }
bucket.setCannedAcl(value.canned); bucket.setCannedAcl(value.canned);
const results = requestTypes.map(type => const results = requestTypes.map(type =>
isBucketAuthorized(bucket, type, value.id, value.auth, log)); isBucketAuthorized(bucket, type, value.id, value.auth, false, log));
assert.deepStrictEqual(results, value.response); assert.deepStrictEqual(results, value.response);
done(); done();
}); });
@ -128,13 +128,13 @@ describe('bucket authorization for bucketGetACL', () => {
it('should allow access to bucket owner', () => { it('should allow access to bucket owner', () => {
const result = isBucketAuthorized(bucket, 'bucketGetACL', const result = isBucketAuthorized(bucket, 'bucketGetACL',
ownerCanonicalId, authInfo); ownerCanonicalId, authInfo, false);
assert.strictEqual(result, true); assert.strictEqual(result, true);
}); });
it('should allow access to user in bucket owner account', () => { it('should allow access to user in bucket owner account', () => {
const result = isBucketAuthorized(bucket, 'bucketGetACL', const result = isBucketAuthorized(bucket, 'bucketGetACL',
ownerCanonicalId, userAuthInfo); ownerCanonicalId, userAuthInfo, false);
assert.strictEqual(result, true); assert.strictEqual(result, true);
}); });
@ -157,7 +157,7 @@ describe('bucket authorization for bucketGetACL', () => {
orders.forEach(value => { orders.forEach(value => {
it(`should allow access to ${value.it}`, done => { it(`should allow access to ${value.it}`, done => {
const noAuthResult = isBucketAuthorized(bucket, 'bucketGetACL', const noAuthResult = isBucketAuthorized(bucket, 'bucketGetACL',
value.id); value.id, null, false);
assert.strictEqual(noAuthResult, false); assert.strictEqual(noAuthResult, false);
if (value.aclParam) { if (value.aclParam) {
bucket.setSpecificAcl(value.aclParam[1], value.aclParam[0]); bucket.setSpecificAcl(value.aclParam[1], value.aclParam[0]);
@ -165,7 +165,7 @@ describe('bucket authorization for bucketGetACL', () => {
bucket.setCannedAcl(value.canned); bucket.setCannedAcl(value.canned);
} }
const authorizedResult = isBucketAuthorized(bucket, 'bucketGetACL', const authorizedResult = isBucketAuthorized(bucket, 'bucketGetACL',
value.id, value.auth); value.id, value.auth, false);
assert.strictEqual(authorizedResult, true); assert.strictEqual(authorizedResult, true);
done(); done();
}); });
@ -187,13 +187,13 @@ describe('bucket authorization for bucketPutACL', () => {
it('should allow access to bucket owner', () => { it('should allow access to bucket owner', () => {
const result = isBucketAuthorized(bucket, 'bucketPutACL', const result = isBucketAuthorized(bucket, 'bucketPutACL',
ownerCanonicalId, authInfo); ownerCanonicalId, authInfo, false);
assert.strictEqual(result, true); assert.strictEqual(result, true);
}); });
it('should allow access to user in bucket owner account', () => { it('should allow access to user in bucket owner account', () => {
const result = isBucketAuthorized(bucket, 'bucketPutACL', const result = isBucketAuthorized(bucket, 'bucketPutACL',
ownerCanonicalId, userAuthInfo); ownerCanonicalId, userAuthInfo, false);
assert.strictEqual(result, true); assert.strictEqual(result, true);
}); });
@ -202,11 +202,11 @@ describe('bucket authorization for bucketPutACL', () => {
it('should allow access to account if ' + it('should allow access to account if ' +
`account was granted ${value} right`, done => { `account was granted ${value} right`, done => {
const noAuthResult = isBucketAuthorized(bucket, 'bucketPutACL', const noAuthResult = isBucketAuthorized(bucket, 'bucketPutACL',
accountToVet, altAcctAuthInfo); accountToVet, altAcctAuthInfo, false);
assert.strictEqual(noAuthResult, false); assert.strictEqual(noAuthResult, false);
bucket.setSpecificAcl(accountToVet, value); bucket.setSpecificAcl(accountToVet, value);
const authorizedResult = isBucketAuthorized(bucket, 'bucketPutACL', const authorizedResult = isBucketAuthorized(bucket, 'bucketPutACL',
accountToVet, altAcctAuthInfo); accountToVet, altAcctAuthInfo, false);
assert.strictEqual(authorizedResult, true); assert.strictEqual(authorizedResult, true);
done(); done();
}); });
@ -228,13 +228,13 @@ describe('bucket authorization for bucketOwnerAction', () => {
it('should allow access to bucket owner', () => { it('should allow access to bucket owner', () => {
const result = isBucketAuthorized(bucket, 'bucketDeleteCors', const result = isBucketAuthorized(bucket, 'bucketDeleteCors',
ownerCanonicalId, authInfo); ownerCanonicalId, authInfo, false);
assert.strictEqual(result, true); assert.strictEqual(result, true);
}); });
it('should allow access to user in bucket owner account', () => { it('should allow access to user in bucket owner account', () => {
const result = isBucketAuthorized(bucket, 'bucketDeleteCors', const result = isBucketAuthorized(bucket, 'bucketDeleteCors',
ownerCanonicalId, userAuthInfo); ownerCanonicalId, userAuthInfo, false);
assert.strictEqual(result, true); assert.strictEqual(result, true);
}); });
@ -256,7 +256,7 @@ describe('bucket authorization for bucketOwnerAction', () => {
} }
bucket.setCannedAcl(value.canned); bucket.setCannedAcl(value.canned);
const result = isBucketAuthorized(bucket, 'bucketDeleteCors', const result = isBucketAuthorized(bucket, 'bucketDeleteCors',
value.id, value.auth); value.id, value.auth, false);
assert.strictEqual(result, false); assert.strictEqual(result, false);
done(); done();
}); });
@ -278,13 +278,13 @@ describe('bucket authorization for bucketDelete', () => {
it('should allow access to bucket owner', () => { it('should allow access to bucket owner', () => {
const result = isBucketAuthorized(bucket, 'bucketDelete', const result = isBucketAuthorized(bucket, 'bucketDelete',
ownerCanonicalId, authInfo); ownerCanonicalId, authInfo, false);
assert.strictEqual(result, true); assert.strictEqual(result, true);
}); });
it('should allow access to user in bucket owner account', () => { it('should allow access to user in bucket owner account', () => {
const result = isBucketAuthorized(bucket, 'bucketDelete', const result = isBucketAuthorized(bucket, 'bucketDelete',
ownerCanonicalId, userAuthInfo); ownerCanonicalId, userAuthInfo, false);
assert.strictEqual(result, true); assert.strictEqual(result, true);
}); });
@ -305,7 +305,7 @@ describe('bucket authorization for bucketDelete', () => {
bucket.setSpecificAcl(value.aclParam[1], value.aclParam[0]); bucket.setSpecificAcl(value.aclParam[1], value.aclParam[0]);
} }
bucket.setCannedAcl(value.canned); bucket.setCannedAcl(value.canned);
const result = isBucketAuthorized(bucket, 'bucketDelete', value.id, value.auth); const result = isBucketAuthorized(bucket, 'bucketDelete', value.id, value.auth, false);
assert.strictEqual(result, false); assert.strictEqual(result, false);
done(); done();
}); });
@ -329,13 +329,13 @@ describe('bucket authorization for objectDelete and objectPut', () => {
it('should allow access to bucket owner', () => { it('should allow access to bucket owner', () => {
const results = requestTypes.map(type => const results = requestTypes.map(type =>
isBucketAuthorized(bucket, type, ownerCanonicalId, authInfo)); isBucketAuthorized(bucket, type, ownerCanonicalId, authInfo, false));
assert.deepStrictEqual(results, [true, true]); assert.deepStrictEqual(results, [true, true]);
}); });
it('should allow access to user in bucket owner account', () => { it('should allow access to user in bucket owner account', () => {
const results = requestTypes.map(type => const results = requestTypes.map(type =>
isBucketAuthorized(bucket, type, ownerCanonicalId, userAuthInfo)); isBucketAuthorized(bucket, type, ownerCanonicalId, userAuthInfo, false));
assert.deepStrictEqual(results, [true, true]); assert.deepStrictEqual(results, [true, true]);
}); });
@ -360,13 +360,13 @@ describe('bucket authorization for objectDelete and objectPut', () => {
it(`should allow access to ${value.it}`, done => { it(`should allow access to ${value.it}`, done => {
bucket.setCannedAcl(value.canned); bucket.setCannedAcl(value.canned);
const noAuthResults = requestTypes.map(type => const noAuthResults = requestTypes.map(type =>
isBucketAuthorized(bucket, type, value.id, value.auth)); isBucketAuthorized(bucket, type, value.id, value.auth, false));
assert.deepStrictEqual(noAuthResults, value.response); assert.deepStrictEqual(noAuthResults, value.response);
if (value.aclParam) { if (value.aclParam) {
bucket.setSpecificAcl(value.aclParam[1], value.aclParam[0]); bucket.setSpecificAcl(value.aclParam[1], value.aclParam[0]);
} }
const authResults = requestTypes.map(type => const authResults = requestTypes.map(type =>
isBucketAuthorized(bucket, type, accountToVet, altAcctAuthInfo)); isBucketAuthorized(bucket, type, accountToVet, altAcctAuthInfo, false));
assert.deepStrictEqual(authResults, [true, true]); assert.deepStrictEqual(authResults, [true, true]);
done(); done();
}); });
@ -378,10 +378,10 @@ describe('bucket authorization for objectPutACL and objectGetACL', () => {
'are done at object level', done => { 'are done at object level', done => {
const requestTypes = ['objectPutACL', 'objectGetACL']; const requestTypes = ['objectPutACL', 'objectGetACL'];
const results = requestTypes.map(type => const results = requestTypes.map(type =>
isBucketAuthorized(bucket, type, accountToVet, altAcctAuthInfo)); isBucketAuthorized(bucket, type, accountToVet, altAcctAuthInfo, false));
assert.deepStrictEqual(results, [true, true]); assert.deepStrictEqual(results, [true, true]);
const publicUserResults = requestTypes.map(type => const publicUserResults = requestTypes.map(type =>
isBucketAuthorized(bucket, type, constants.publicId)); isBucketAuthorized(bucket, type, constants.publicId, null, false));
assert.deepStrictEqual(publicUserResults, [true, true]); assert.deepStrictEqual(publicUserResults, [true, true]);
done(); done();
}); });

View File

@ -88,6 +88,7 @@ describe('bucketDelete API', () => {
namespace, namespace,
headers: {}, headers: {},
url: `/${bucketName}`, url: `/${bucketName}`,
iamAuthzResults: false,
}; };
const initiateRequest = { const initiateRequest = {
@ -96,6 +97,7 @@ describe('bucketDelete API', () => {
objectKey: objectName, objectKey: objectName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: `/${objectName}?uploads`, url: `/${objectName}?uploads`,
iamAuthzResults: false,
}; };
it('should return an error if the bucket is not empty', done => { it('should return an error if the bucket is not empty', done => {

View File

@ -19,6 +19,7 @@ const testBucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
const testBucketPutCorsRequest = const testBucketPutCorsRequest =
corsUtil.createBucketCorsRequest('PUT', bucketName); corsUtil.createBucketCorsRequest('PUT', bucketName);

View File

@ -13,6 +13,7 @@ const bucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
describe('bucketDeleteEncryption API', () => { describe('bucketDeleteEncryption API', () => {

View File

@ -19,6 +19,7 @@ function _makeRequest(includeXml) {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
if (includeXml) { if (includeXml) {
request.post = '<LifecycleConfiguration ' + request.post = '<LifecycleConfiguration ' +

View File

@ -19,6 +19,7 @@ function _makeRequest(includePolicy) {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
if (includePolicy) { if (includePolicy) {
const examplePolicy = { const examplePolicy = {

View File

@ -20,6 +20,7 @@ const testBucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
const testBucketDeleteWebsiteRequest = { const testBucketDeleteWebsiteRequest = {
bucketName, bucketName,
@ -28,6 +29,7 @@ const testBucketDeleteWebsiteRequest = {
}, },
url: '/?website', url: '/?website',
query: { website: '' }, query: { website: '' },
iamAuthzResults: false,
}; };
const testBucketPutWebsiteRequest = Object.assign({ post: config.getXml() }, const testBucketPutWebsiteRequest = Object.assign({ post: config.getXml() },
testBucketDeleteWebsiteRequest); testBucketDeleteWebsiteRequest);

View File

@ -63,6 +63,7 @@ const baseGetRequest = {
bucketName, bucketName,
namespace, namespace,
headers: { host: '/' }, headers: { host: '/' },
iamAuthzResults: false,
}; };
const baseUrl = `/${bucketName}`; const baseUrl = `/${bucketName}`;

View File

@ -25,6 +25,7 @@ describe('bucketGetACL API', () => {
namespace, namespace,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
const testGetACLRequest = { const testGetACLRequest = {
bucketName, bucketName,
@ -32,6 +33,7 @@ describe('bucketGetACL API', () => {
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
it('should get a canned private ACL', done => { it('should get a canned private ACL', done => {
@ -44,6 +46,7 @@ describe('bucketGetACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
async.waterfall([ async.waterfall([
@ -76,6 +79,7 @@ describe('bucketGetACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
async.waterfall([ async.waterfall([
@ -119,6 +123,7 @@ describe('bucketGetACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
async.waterfall([ async.waterfall([
@ -156,6 +161,7 @@ describe('bucketGetACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
async.waterfall([ async.waterfall([
@ -194,6 +200,7 @@ describe('bucketGetACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
async.waterfall([ async.waterfall([
@ -248,6 +255,7 @@ describe('bucketGetACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
const canonicalIDforSample1 = const canonicalIDforSample1 =
'79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be'; '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be';
@ -338,6 +346,7 @@ describe('bucketGetACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
async.waterfall([ async.waterfall([
@ -377,6 +386,7 @@ describe('bucketGetACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
async.waterfall([ async.waterfall([

View File

@ -16,6 +16,7 @@ const testBucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
function _makeCorsRequest(xml) { function _makeCorsRequest(xml) {
@ -26,6 +27,7 @@ function _makeCorsRequest(xml) {
}, },
url: '/?cors', url: '/?cors',
query: { cors: '' }, query: { cors: '' },
iamAuthzResults: false,
}; };
if (xml) { if (xml) {

View File

@ -17,6 +17,7 @@ const testBucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
describe('getBucketLifecycle API', () => { describe('getBucketLifecycle API', () => {

View File

@ -16,6 +16,7 @@ const testBucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
const testGetLocationRequest = { const testGetLocationRequest = {
@ -25,6 +26,7 @@ const testGetLocationRequest = {
}, },
url: '/?location', url: '/?location',
query: { location: '' }, query: { location: '' },
iamAuthzResults: false,
}; };
const locationConstraints = config.locationConstraints; const locationConstraints = config.locationConstraints;

View File

@ -15,6 +15,7 @@ const testBucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
function getNotificationRequest(bucketName, xml) { function getNotificationRequest(bucketName, xml) {
@ -23,6 +24,7 @@ function getNotificationRequest(bucketName, xml) {
headers: { headers: {
host: `${bucketName}.s3.amazonaws.com`, host: `${bucketName}.s3.amazonaws.com`,
}, },
iamAuthzResults: false,
}; };
if (xml) { if (xml) {
request.post = xml; request.post = xml;

View File

@ -14,6 +14,7 @@ const bucketPutReq = {
host: `${bucketName}.s3.amazonaws.com`, host: `${bucketName}.s3.amazonaws.com`,
}, },
url: '/', url: '/',
iamAuthzResults: false,
}; };
const testBucketPutReqWithObjLock = { const testBucketPutReqWithObjLock = {
@ -23,6 +24,7 @@ const testBucketPutReqWithObjLock = {
'x-amz-bucket-object-lock-enabled': 'True', 'x-amz-bucket-object-lock-enabled': 'True',
}, },
url: '/', url: '/',
iamAuthzResults: false,
}; };
function getObjectLockConfigRequest(bucketName, xml) { function getObjectLockConfigRequest(bucketName, xml) {
@ -33,6 +35,7 @@ function getObjectLockConfigRequest(bucketName, xml) {
'x-amz-bucket-object-lock-enabled': 'true', 'x-amz-bucket-object-lock-enabled': 'true',
}, },
url: '/?object-lock', url: '/?object-lock',
iamAuthzResults: false,
}; };
if (xml) { if (xml) {
request.post = xml; request.post = xml;

View File

@ -16,6 +16,7 @@ const testBasicRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
const expectedBucketPolicy = { const expectedBucketPolicy = {
@ -34,6 +35,7 @@ const testPutPolicyRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
post: JSON.stringify(expectedBucketPolicy), post: JSON.stringify(expectedBucketPolicy),
iamAuthzResults: false,
}; };
describe('getBucketPolicy API', () => { describe('getBucketPolicy API', () => {

View File

@ -15,6 +15,7 @@ const testBucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
function _makeWebsiteRequest(xml) { function _makeWebsiteRequest(xml) {
@ -25,6 +26,7 @@ function _makeWebsiteRequest(xml) {
}, },
url: '/?website', url: '/?website',
query: { website: '' }, query: { website: '' },
iamAuthzResults: false,
}; };
if (xml) { if (xml) {

View File

@ -14,6 +14,7 @@ const testRequest = {
namespace, namespace,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
describe('bucketHead API', () => { describe('bucketHead API', () => {
beforeEach(() => { beforeEach(() => {

View File

@ -244,7 +244,7 @@ describe('bucket policy authorization', () => {
describe('isBucketAuthorized with no policy set', () => { describe('isBucketAuthorized with no policy set', () => {
it('should allow access to bucket owner', done => { it('should allow access to bucket owner', done => {
const allowed = isBucketAuthorized(bucket, 'bucketPut', const allowed = isBucketAuthorized(bucket, 'bucketPut',
bucketOwnerCanonicalId, null, log); bucketOwnerCanonicalId, null, false, log);
assert.equal(allowed, true); assert.equal(allowed, true);
done(); done();
}); });
@ -252,7 +252,7 @@ describe('bucket policy authorization', () => {
it('should deny access to non-bucket owner', it('should deny access to non-bucket owner',
done => { done => {
const allowed = isBucketAuthorized(bucket, 'bucketPut', const allowed = isBucketAuthorized(bucket, 'bucketPut',
altAcctCanonicalId, null, log); altAcctCanonicalId, null, false, log);
assert.equal(allowed, false); assert.equal(allowed, false);
done(); done();
}); });
@ -268,7 +268,7 @@ describe('bucket policy authorization', () => {
it('should allow access to non-bucket owner if principal is set to "*"', it('should allow access to non-bucket owner if principal is set to "*"',
done => { done => {
const allowed = isBucketAuthorized(bucket, bucAction, const allowed = isBucketAuthorized(bucket, bucAction,
altAcctCanonicalId, null, log); altAcctCanonicalId, null, false, log);
assert.equal(allowed, true); assert.equal(allowed, true);
done(); done();
}); });
@ -276,7 +276,7 @@ describe('bucket policy authorization', () => {
it('should allow access to public user if principal is set to "*"', it('should allow access to public user if principal is set to "*"',
done => { done => {
const allowed = isBucketAuthorized(bucket, bucAction, const allowed = isBucketAuthorized(bucket, bucAction,
constants.publicId, null, log); constants.publicId, null, false, log);
assert.equal(allowed, true); assert.equal(allowed, true);
done(); done();
}); });
@ -287,7 +287,7 @@ describe('bucket policy authorization', () => {
newPolicy.Statement[0][t.keyToChange] = t.bucketValue; newPolicy.Statement[0][t.keyToChange] = t.bucketValue;
bucket.setBucketPolicy(newPolicy); bucket.setBucketPolicy(newPolicy);
const allowed = isBucketAuthorized(bucket, bucAction, const allowed = isBucketAuthorized(bucket, bucAction,
t.bucketId, t.bucketAuthInfo, log); t.bucketId, t.bucketAuthInfo, false, log);
assert.equal(allowed, t.expected); assert.equal(allowed, t.expected);
done(); done();
}); });
@ -304,7 +304,7 @@ describe('bucket policy authorization', () => {
}; };
bucket.setBucketPolicy(newPolicy); bucket.setBucketPolicy(newPolicy);
const allowed = isBucketAuthorized(bucket, bucAction, const allowed = isBucketAuthorized(bucket, bucAction,
altAcctCanonicalId, null, log); altAcctCanonicalId, null, false, log);
assert.equal(allowed, false); assert.equal(allowed, false);
done(); done();
}); });
@ -312,7 +312,7 @@ describe('bucket policy authorization', () => {
it('should deny access to non-bucket owner with an unsupported action type', it('should deny access to non-bucket owner with an unsupported action type',
done => { done => {
const allowed = isBucketAuthorized(bucket, 'unsupportedAction', const allowed = isBucketAuthorized(bucket, 'unsupportedAction',
altAcctCanonicalId, null, log); altAcctCanonicalId, null, false, log);
assert.equal(allowed, false); assert.equal(allowed, false);
done(); done();
}); });
@ -325,7 +325,7 @@ describe('bucket policy authorization', () => {
it('should allow access to object owner', done => { it('should allow access to object owner', done => {
const allowed = isObjAuthorized(bucket, object, objAction, const allowed = isObjAuthorized(bucket, object, objAction,
objectOwnerCanonicalId, null, log); objectOwnerCanonicalId, null, false, log);
assert.equal(allowed, true); assert.equal(allowed, true);
done(); done();
}); });
@ -333,7 +333,7 @@ describe('bucket policy authorization', () => {
it('should deny access to non-object owner', it('should deny access to non-object owner',
done => { done => {
const allowed = isObjAuthorized(bucket, object, objAction, const allowed = isObjAuthorized(bucket, object, objAction,
altAcctCanonicalId, null, log); altAcctCanonicalId, null, false, log);
assert.equal(allowed, false); assert.equal(allowed, false);
done(); done();
}); });
@ -352,7 +352,7 @@ describe('bucket policy authorization', () => {
it('should allow access to non-object owner if principal is set to "*"', it('should allow access to non-object owner if principal is set to "*"',
done => { done => {
const allowed = isObjAuthorized(bucket, object, objAction, const allowed = isObjAuthorized(bucket, object, objAction,
altAcctCanonicalId, null, log); altAcctCanonicalId, null, false, log);
assert.equal(allowed, true); assert.equal(allowed, true);
done(); done();
}); });
@ -360,7 +360,7 @@ describe('bucket policy authorization', () => {
it('should allow access to public user if principal is set to "*"', it('should allow access to public user if principal is set to "*"',
done => { done => {
const allowed = isObjAuthorized(bucket, object, objAction, const allowed = isObjAuthorized(bucket, object, objAction,
constants.publicId, null, log); constants.publicId, null, false, log);
assert.equal(allowed, true); assert.equal(allowed, true);
done(); done();
}); });
@ -371,7 +371,7 @@ describe('bucket policy authorization', () => {
newPolicy.Statement[0][t.keyToChange] = t.objectValue; newPolicy.Statement[0][t.keyToChange] = t.objectValue;
bucket.setBucketPolicy(newPolicy); bucket.setBucketPolicy(newPolicy);
const allowed = isObjAuthorized(bucket, object, objAction, const allowed = isObjAuthorized(bucket, object, objAction,
t.objectId, t.objectAuthInfo, log); t.objectId, t.objectAuthInfo, false, log);
assert.equal(allowed, t.expected); assert.equal(allowed, t.expected);
done(); done();
}); });
@ -383,7 +383,7 @@ describe('bucket policy authorization', () => {
newPolicy.Statement[0].Action = ['s3:GetObject']; newPolicy.Statement[0].Action = ['s3:GetObject'];
bucket.setBucketPolicy(newPolicy); bucket.setBucketPolicy(newPolicy);
const allowed = isObjAuthorized(bucket, object, 'objectHead', const allowed = isObjAuthorized(bucket, object, 'objectHead',
altAcctCanonicalId, altAcctAuthInfo, log); altAcctCanonicalId, altAcctAuthInfo, false, log);
assert.equal(allowed, true); assert.equal(allowed, true);
done(); done();
}); });
@ -393,7 +393,7 @@ describe('bucket policy authorization', () => {
newPolicy.Statement[0].Action = ['s3:PutObject']; newPolicy.Statement[0].Action = ['s3:PutObject'];
bucket.setBucketPolicy(newPolicy); bucket.setBucketPolicy(newPolicy);
const allowed = isObjAuthorized(bucket, object, 'objectHead', const allowed = isObjAuthorized(bucket, object, 'objectHead',
altAcctCanonicalId, altAcctAuthInfo, log); altAcctCanonicalId, altAcctAuthInfo, false, log);
assert.equal(allowed, false); assert.equal(allowed, false);
done(); done();
}); });
@ -408,7 +408,7 @@ describe('bucket policy authorization', () => {
}; };
bucket.setBucketPolicy(newPolicy); bucket.setBucketPolicy(newPolicy);
const allowed = isObjAuthorized(bucket, object, objAction, const allowed = isObjAuthorized(bucket, object, objAction,
altAcctCanonicalId, null, log); altAcctCanonicalId, null, false, log);
assert.equal(allowed, false); assert.equal(allowed, false);
done(); done();
}); });
@ -416,7 +416,7 @@ describe('bucket policy authorization', () => {
it('should deny access to non-object owner with an unsupported action type', it('should deny access to non-object owner with an unsupported action type',
done => { done => {
const allowed = isObjAuthorized(bucket, object, 'unsupportedAction', const allowed = isObjAuthorized(bucket, object, 'unsupportedAction',
altAcctCanonicalId, null, log); altAcctCanonicalId, null, false, log);
assert.equal(allowed, false); assert.equal(allowed, false);
done(); done();
}); });

View File

@ -18,6 +18,7 @@ const testBucketPutRequest = {
namespace, namespace,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
const canonicalIDforSample1 = const canonicalIDforSample1 =
'79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be'; '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be';
@ -72,6 +73,7 @@ describe('putBucketACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
@ -90,6 +92,7 @@ describe('putBucketACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
assert.strictEqual(err, undefined); assert.strictEqual(err, undefined);
@ -111,6 +114,7 @@ describe('putBucketACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
const testACLRequest2 = { const testACLRequest2 = {
bucketName, bucketName,
@ -121,6 +125,7 @@ describe('putBucketACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
assert.strictEqual(err, undefined); assert.strictEqual(err, undefined);
@ -149,6 +154,7 @@ describe('putBucketACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
const testACLRequest2 = { const testACLRequest2 = {
bucketName, bucketName,
@ -159,6 +165,7 @@ describe('putBucketACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
@ -197,6 +204,7 @@ describe('putBucketACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
assert.strictEqual(err, undefined); assert.strictEqual(err, undefined);
@ -238,6 +246,7 @@ describe('putBucketACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
assert.strictEqual(err, undefined); assert.strictEqual(err, undefined);
@ -271,6 +280,7 @@ describe('putBucketACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
return bucketPutACL(authInfo, testACLRequest, log, err => { return bucketPutACL(authInfo, testACLRequest, log, err => {
assert.deepStrictEqual(err, errors.InvalidArgument); assert.deepStrictEqual(err, errors.InvalidArgument);
@ -292,6 +302,7 @@ describe('putBucketACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
@ -351,6 +362,7 @@ describe('putBucketACL API', () => {
'</AccessControlPolicy>', '</AccessControlPolicy>',
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
@ -386,6 +398,7 @@ describe('putBucketACL API', () => {
'</AccessControlPolicy>', '</AccessControlPolicy>',
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
@ -418,6 +431,7 @@ describe('putBucketACL API', () => {
'</AccessControlPolicy>', '</AccessControlPolicy>',
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
@ -461,6 +475,7 @@ describe('putBucketACL API', () => {
'</AccessControlPolicy>', '</AccessControlPolicy>',
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
@ -493,6 +508,7 @@ describe('putBucketACL API', () => {
'</AccessControlPolicy>', '</AccessControlPolicy>',
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
return bucketPutACL(authInfo, testACLRequest, log, err => { return bucketPutACL(authInfo, testACLRequest, log, err => {
@ -525,6 +541,7 @@ describe('putBucketACL API', () => {
'</AccessControlPolicy>', '</AccessControlPolicy>',
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
assert.deepStrictEqual(err, errors.UnresolvableGrantByEmailAddress); assert.deepStrictEqual(err, errors.UnresolvableGrantByEmailAddress);
@ -560,6 +577,7 @@ describe('putBucketACL API', () => {
'</AccessControlPolicy>', '</AccessControlPolicy>',
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
@ -605,6 +623,7 @@ describe('putBucketACL API', () => {
'</AccessControlPolicy>', '</AccessControlPolicy>',
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
@ -640,6 +659,7 @@ describe('putBucketACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
@ -674,6 +694,7 @@ describe('putBucketACL API', () => {
'</AccessControlPolicy>', '</AccessControlPolicy>',
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {
@ -695,6 +716,7 @@ describe('putBucketACL API', () => {
}, },
url: '/?acl', url: '/?acl',
query: { acl: '' }, query: { acl: '' },
iamAuthzResults: false,
}; };
bucketPutACL(authInfo, testACLRequest, log, err => { bucketPutACL(authInfo, testACLRequest, log, err => {

View File

@ -19,6 +19,7 @@ const testBucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
function _testPutBucketCors(authInfo, request, log, errCode, cb) { function _testPutBucketCors(authInfo, request, log, errCode, cb) {

View File

@ -14,6 +14,7 @@ const bucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
describe('bucketPutEncryption API', () => { describe('bucketPutEncryption API', () => {

View File

@ -17,6 +17,7 @@ const testBucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
const expectedLifecycleConfig = { const expectedLifecycleConfig = {

View File

@ -15,6 +15,7 @@ const bucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
const expectedNotifConfig = { const expectedNotifConfig = {
@ -52,6 +53,7 @@ function getNotifRequest(empty) {
host: `${bucketName}.s3.amazonaws.com`, host: `${bucketName}.s3.amazonaws.com`,
}, },
post: notifXml, post: notifXml,
iamAuthzResults: false,
}; };
return putNotifConfigRequest; return putNotifConfigRequest;
} }

View File

@ -15,6 +15,7 @@ const bucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
const objectLockXml = '<ObjectLockConfiguration ' + const objectLockXml = '<ObjectLockConfiguration ' +
@ -30,6 +31,7 @@ const putObjLockRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
post: objectLockXml, post: objectLockXml,
iamAuthzResults: false,
}; };
const expectedObjectLockConfig = { const expectedObjectLockConfig = {

View File

@ -15,6 +15,7 @@ const testBucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
let expectedBucketPolicy = {}; let expectedBucketPolicy = {};
@ -25,6 +26,7 @@ function getPolicyRequest(policy) {
host: `${bucketName}.s3.amazonaws.com`, host: `${bucketName}.s3.amazonaws.com`,
}, },
post: JSON.stringify(policy), post: JSON.stringify(policy),
iamAuthzResults: false,
}; };
} }
@ -76,7 +78,7 @@ describe('putBucketPolicy API', () => {
}); });
}); });
it('should return error if policy contains conditions', done => { it.skip('should return error if policy contains conditions', done => {
expectedBucketPolicy.Statement[0].Condition = expectedBucketPolicy.Statement[0].Condition =
{ StringEquals: { 's3:x-amz-acl': ['public-read'] } }; { StringEquals: { 's3:x-amz-acl': ['public-read'] } };
bucketPutPolicy(authInfo, getPolicyRequest(expectedBucketPolicy), log, bucketPutPolicy(authInfo, getPolicyRequest(expectedBucketPolicy), log,

View File

@ -19,6 +19,7 @@ const testBucketPutRequest = {
bucketName, bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}; };
function _getPutWebsiteRequest(xml) { function _getPutWebsiteRequest(xml) {
@ -29,6 +30,7 @@ function _getPutWebsiteRequest(xml) {
}, },
url: '/?website', url: '/?website',
query: { website: '' }, query: { website: '' },
iamAuthzResults: false,
}; };
request.post = xml; request.post = xml;
return request; return request;

View File

@ -28,6 +28,7 @@ const testPutBucketRequest = new DummyRequest({
namespace, namespace,
headers: { host: `${bucketName}.s3.amazonaws.com` }, headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/', url: '/',
iamAuthzResults: false,
}); });
const testDeleteRequest = new DummyRequest({ const testDeleteRequest = new DummyRequest({
bucketName, bucketName,
@ -35,6 +36,7 @@ const testDeleteRequest = new DummyRequest({
objectKey: objectName, objectKey: objectName,
headers: {}, headers: {},
url: `/${bucketName}/${objectName}`, url: `/${bucketName}/${objectName}`,
iamAuthzResults: false,
}); });
function _createBucketPutVersioningReq(status) { function _createBucketPutVersioningReq(status) {
@ -45,6 +47,7 @@ function _createBucketPutVersioningReq(status) {
}, },
url: '/?versioning', url: '/?versioning',
query: { versioning: '' }, query: { versioning: '' },
iamAuthzResults: false,
}; };
const xml = '<VersioningConfiguration ' + const xml = '<VersioningConfiguration ' +
'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">' + 'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">' +
@ -62,6 +65,7 @@ function _createMultiObjectDeleteRequest(numObjects) {
}, },
url: '/?delete', url: '/?delete',
query: { delete: '' }, query: { delete: '' },
iamAuthzResults: false,
}; };
const xml = []; const xml = [];
xml.push('<?xml version="1.0" encoding="UTF-8"?>'); xml.push('<?xml version="1.0" encoding="UTF-8"?>');

Some files were not shown because too many files have changed in this diff Show More