Compare commits
51 Commits
developmen
...
improvemen
Author | SHA1 | Date |
---|---|---|
williamlardier | 6a36259833 | |
williamlardier | 85d0c8fc48 | |
williamlardier | 76ac3a1bdc | |
williamlardier | 083de41f3c | |
williamlardier | bbe46d08ff | |
williamlardier | 64eb593d1f | |
williamlardier | 731b70cbff | |
williamlardier | 4360c00c99 | |
williamlardier | 3e8b8e58cc | |
williamlardier | e1e51261ff | |
williamlardier | 93f14f0fcb | |
williamlardier | b392385893 | |
williamlardier | ca0a6f4262 | |
Will Toozs | 3773bcf61c | |
Will Toozs | c82ce08fdb | |
Will Toozs | 9bf9130ca9 | |
Will Toozs | b4a986ec3a | |
Will Toozs | 8725888bbd | |
Will Toozs | 111ecf875b | |
Will Toozs | 15ddc02c25 | |
Will Toozs | 482e6576b1 | |
Will Toozs | 5f3fa488d5 | |
Will Toozs | e035d5550b | |
Will Toozs | b119898ed1 | |
Will Toozs | e9d5c17707 | |
Will Toozs | a7ce187499 | |
Will Toozs | 6f7b2d6638 | |
Will Toozs | f10cacb033 | |
Will Toozs | 9bf18d806a | |
Will Toozs | dfa80a41ed | |
Will Toozs | 9f753af13a | |
Will Toozs | c214bb8ee1 | |
Will Toozs | 3fcd479639 | |
Will Toozs | 1bb2f11f6c | |
Will Toozs | bfe9d34547 | |
Will Toozs | cc8f8a7213 | |
Will Toozs | f53a8ea8c7 | |
Will Toozs | 1d55be193a | |
Will Toozs | 00ba0d9377 | |
Will Toozs | fe6578b276 | |
Will Toozs | da526b5092 | |
Will Toozs | 159906dc74 | |
Will Toozs | 3ff2b5e9ed | |
Will Toozs | f69f72363b | |
Will Toozs | 7f9562bfda | |
Will Toozs | c01898f1a0 | |
Will Toozs | 0f64e7c337 | |
Will Toozs | de334d16e9 | |
Will Toozs | e99bb1e1cc | |
Will Toozs | cd6320c432 | |
Will Toozs | 3bc66f8cea |
|
@ -153,6 +153,8 @@ const constants = {
|
|||
'objectDeleteTagging',
|
||||
'objectGetTagging',
|
||||
'objectPutTagging',
|
||||
'objectPutLegalHold',
|
||||
'objectPutRetention',
|
||||
],
|
||||
// response header to be sent when there are invalid
|
||||
// user metadata in the object's metadata
|
||||
|
|
|
@ -107,6 +107,7 @@ const api = {
|
|||
// no need to check auth on website or cors preflight requests
|
||||
if (apiMethod === 'websiteGet' || apiMethod === 'websiteHead' ||
|
||||
apiMethod === 'corsPreflight') {
|
||||
request.actionImplicitDenies = false;
|
||||
return this[apiMethod](request, log, callback);
|
||||
}
|
||||
|
||||
|
@ -129,15 +130,25 @@ const api = {
|
|||
|
||||
const requestContexts = prepareRequestContexts(apiMethod, request,
|
||||
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) {
|
||||
let returnTagCount = true;
|
||||
const isImplicitDeny = {};
|
||||
let isOnlyImplicitDeny = true;
|
||||
if (apiMethod === 'objectGet') {
|
||||
// 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');
|
||||
return errors.AccessDenied;
|
||||
}
|
||||
// TODO add support for returnTagCount in the bucket policy
|
||||
// checks
|
||||
isImplicitDeny[authResults[0].action] = authResults[0].isImplicit;
|
||||
// second item checks s3:GetObject(Version)Tagging action
|
||||
if (!authResults[1].isAllowed) {
|
||||
log.trace('get tagging authorization denial ' +
|
||||
|
@ -146,13 +157,25 @@ const api = {
|
|||
}
|
||||
} else {
|
||||
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');
|
||||
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([
|
||||
|
@ -230,7 +253,16 @@ const api = {
|
|||
if (checkedResults instanceof Error) {
|
||||
return callback(checkedResults);
|
||||
}
|
||||
returnTagCount = checkedResults;
|
||||
returnTagCount = checkedResults.returnTagCount;
|
||||
request.actionImplicitDenies = checkedResults.isImplicitDeny;
|
||||
} else {
|
||||
// create an object of keys apiMethods with all values to false:
|
||||
// for backward compatibility, all apiMethods are allowed by default
|
||||
// thus it is explicitly allowed, so implicit deny is false
|
||||
request.actionImplicitDenies = apiMethods.reduce((acc, curr) => {
|
||||
acc[curr] = false;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
if (apiMethod === 'objectPut' || apiMethod === 'objectPutPart') {
|
||||
request._response = response;
|
||||
|
|
|
@ -1,29 +1,51 @@
|
|||
const { evaluators, actionMaps, RequestContext } = require('arsenal').policies;
|
||||
const constants = require('../../../../constants');
|
||||
|
||||
const { allAuthedUsersId, bucketOwnerActions, logId, publicId,
|
||||
assumedRoleArnResourceType, backbeatLifecycleSessionName } = constants;
|
||||
const {
|
||||
allAuthedUsersId, bucketOwnerActions, logId, publicId,
|
||||
assumedRoleArnResourceType, backbeatLifecycleSessionName,
|
||||
} = constants;
|
||||
|
||||
// whitelist buckets to allow public read on objects
|
||||
const publicReadBuckets = process.env.ALLOW_PUBLIC_READ_BUCKETS ?
|
||||
process.env.ALLOW_PUBLIC_READ_BUCKETS.split(',') : [];
|
||||
const publicReadBuckets = process.env.ALLOW_PUBLIC_READ_BUCKETS
|
||||
? 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) {
|
||||
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();
|
||||
if (requestType === 'bucketGet' || requestType === 'bucketHead') {
|
||||
if (requestTypeParsed === 'bucketGet' || requestTypeParsed === 'bucketHead') {
|
||||
if (bucketAcl.Canned === 'public-read'
|
||||
|| bucketAcl.Canned === 'public-read-write'
|
||||
|| (bucketAcl.Canned === 'authenticated-read'
|
||||
&& canonicalID !== publicId)) {
|
||||
return true;
|
||||
} else if (bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
|
||||
} if (bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
|
||||
|| bucketAcl.READ.indexOf(canonicalID) > -1) {
|
||||
return true;
|
||||
} else if (bucketAcl.READ.indexOf(publicId) > -1
|
||||
} if (bucketAcl.READ.indexOf(publicId) > -1
|
||||
|| (bucketAcl.READ.indexOf(allAuthedUsersId) > -1
|
||||
&& canonicalID !== publicId)
|
||||
|| (bucketAcl.FULL_CONTROL.indexOf(allAuthedUsersId) > -1
|
||||
|
@ -32,13 +54,13 @@ function checkBucketAcls(bucket, requestType, canonicalID) {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
if (requestType === 'bucketGetACL') {
|
||||
if (requestTypeParsed === 'bucketGetACL') {
|
||||
if ((bucketAcl.Canned === 'log-delivery-write'
|
||||
&& canonicalID === logId)
|
||||
|| bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
|
||||
|| bucketAcl.READ_ACP.indexOf(canonicalID) > -1) {
|
||||
return true;
|
||||
} else if (bucketAcl.READ_ACP.indexOf(publicId) > -1
|
||||
} if (bucketAcl.READ_ACP.indexOf(publicId) > -1
|
||||
|| (bucketAcl.READ_ACP.indexOf(allAuthedUsersId) > -1
|
||||
&& canonicalID !== publicId)
|
||||
|| (bucketAcl.FULL_CONTROL.indexOf(allAuthedUsersId) > -1
|
||||
|
@ -48,11 +70,11 @@ function checkBucketAcls(bucket, requestType, canonicalID) {
|
|||
}
|
||||
}
|
||||
|
||||
if (requestType === 'bucketPutACL') {
|
||||
if (requestTypeParsed === 'bucketPutACL') {
|
||||
if (bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
|
||||
|| bucketAcl.WRITE_ACP.indexOf(canonicalID) > -1) {
|
||||
return true;
|
||||
} else if (bucketAcl.WRITE_ACP.indexOf(publicId) > -1
|
||||
} if (bucketAcl.WRITE_ACP.indexOf(publicId) > -1
|
||||
|| (bucketAcl.WRITE_ACP.indexOf(allAuthedUsersId) > -1
|
||||
&& canonicalID !== publicId)
|
||||
|| (bucketAcl.FULL_CONTROL.indexOf(allAuthedUsersId) > -1
|
||||
|
@ -62,16 +84,12 @@ function checkBucketAcls(bucket, requestType, canonicalID) {
|
|||
}
|
||||
}
|
||||
|
||||
if (requestType === 'bucketDelete' && bucket.getOwner() === canonicalID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (requestType === 'objectDelete' || requestType === 'objectPut') {
|
||||
if (requestTypeParsed === 'objectDelete' || requestTypeParsed === 'objectPut') {
|
||||
if (bucketAcl.Canned === 'public-read-write'
|
||||
|| bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
|
||||
|| bucketAcl.WRITE.indexOf(canonicalID) > -1) {
|
||||
return true;
|
||||
} else if (bucketAcl.WRITE.indexOf(publicId) > -1
|
||||
} if (bucketAcl.WRITE.indexOf(publicId) > -1
|
||||
|| (bucketAcl.WRITE.indexOf(allAuthedUsersId) > -1
|
||||
&& canonicalID !== publicId)
|
||||
|| (bucketAcl.FULL_CONTROL.indexOf(allAuthedUsersId) > -1
|
||||
|
@ -86,11 +104,12 @@ function checkBucketAcls(bucket, requestType, canonicalID) {
|
|||
// objectPutACL, objectGetACL, objectHead or objectGet, the bucket
|
||||
// authorization check should just return true so can move on to check
|
||||
// rights at the object level.
|
||||
return (requestType === 'objectPutACL' || requestType === 'objectGetACL' ||
|
||||
requestType === 'objectGet' || requestType === 'objectHead');
|
||||
return (requestTypeParsed === 'objectPutACL' || requestTypeParsed === 'objectGetACL'
|
||||
|| requestTypeParsed === 'objectGet' || requestTypeParsed === 'objectHead');
|
||||
}
|
||||
|
||||
function checkObjectAcls(bucket, objectMD, requestType, canonicalID) {
|
||||
function checkObjectAcls(bucket, objectMD, requestType, canonicalID, requesterIsNotUser,
|
||||
isUserUnauthenticated, mainApiCall) {
|
||||
const bucketOwner = bucket.getOwner();
|
||||
// acls don't distinguish between users and accounts, so both should be allowed
|
||||
if (bucketOwnerActions.includes(requestType)
|
||||
|
@ -100,6 +119,15 @@ function checkObjectAcls(bucket, objectMD, requestType, canonicalID) {
|
|||
if (objectMD['owner-id'] === canonicalID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Backward compatibility
|
||||
if (mainApiCall === 'objectGet') {
|
||||
if ((isUserUnauthenticated || (requesterIsNotUser && bucketOwner === objectMD['owner-id']))
|
||||
&& requestType === 'objectGetTagging') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!objectMD.acl) {
|
||||
return false;
|
||||
}
|
||||
|
@ -110,15 +138,15 @@ function checkObjectAcls(bucket, objectMD, requestType, canonicalID) {
|
|||
|| (objectMD.acl.Canned === 'authenticated-read'
|
||||
&& canonicalID !== publicId)) {
|
||||
return true;
|
||||
} else if (objectMD.acl.Canned === 'bucket-owner-read'
|
||||
} if (objectMD.acl.Canned === 'bucket-owner-read'
|
||||
&& bucketOwner === canonicalID) {
|
||||
return true;
|
||||
} else if ((objectMD.acl.Canned === 'bucket-owner-full-control'
|
||||
} if ((objectMD.acl.Canned === 'bucket-owner-full-control'
|
||||
&& bucketOwner === canonicalID)
|
||||
|| objectMD.acl.FULL_CONTROL.indexOf(canonicalID) > -1
|
||||
|| objectMD.acl.READ.indexOf(canonicalID) > -1) {
|
||||
return true;
|
||||
} else if (objectMD.acl.READ.indexOf(publicId) > -1
|
||||
} if (objectMD.acl.READ.indexOf(publicId) > -1
|
||||
|| (objectMD.acl.READ.indexOf(allAuthedUsersId) > -1
|
||||
&& canonicalID !== publicId)
|
||||
|| (objectMD.acl.FULL_CONTROL.indexOf(allAuthedUsersId) > -1
|
||||
|
@ -140,7 +168,7 @@ function checkObjectAcls(bucket, objectMD, requestType, canonicalID) {
|
|||
|| objectMD.acl.FULL_CONTROL.indexOf(canonicalID) > -1
|
||||
|| objectMD.acl.WRITE_ACP.indexOf(canonicalID) > -1) {
|
||||
return true;
|
||||
} else if (objectMD.acl.WRITE_ACP.indexOf(publicId) > -1
|
||||
} if (objectMD.acl.WRITE_ACP.indexOf(publicId) > -1
|
||||
|| (objectMD.acl.WRITE_ACP.indexOf(allAuthedUsersId) > -1
|
||||
&& canonicalID !== publicId)
|
||||
|| (objectMD.acl.FULL_CONTROL.indexOf(allAuthedUsersId) > -1
|
||||
|
@ -156,7 +184,7 @@ function checkObjectAcls(bucket, objectMD, requestType, canonicalID) {
|
|||
|| objectMD.acl.FULL_CONTROL.indexOf(canonicalID) > -1
|
||||
|| objectMD.acl.READ_ACP.indexOf(canonicalID) > -1) {
|
||||
return true;
|
||||
} else if (objectMD.acl.READ_ACP.indexOf(publicId) > -1
|
||||
} if (objectMD.acl.READ_ACP.indexOf(publicId) > -1
|
||||
|| (objectMD.acl.READ_ACP.indexOf(allAuthedUsersId) > -1
|
||||
&& canonicalID !== publicId)
|
||||
|| (objectMD.acl.FULL_CONTROL.indexOf(allAuthedUsersId) > -1
|
||||
|
@ -246,9 +274,10 @@ function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, l
|
|||
let permission = 'defaultDeny';
|
||||
// if requester is user within bucket owner account, actions should be
|
||||
// allowed unless explicitly denied (assumes allowed by IAM policy)
|
||||
if (bucketOwner === canonicalID) {
|
||||
permission = 'allow';
|
||||
}
|
||||
// Update: manual testing on the 18/9/2023 found this not to be the case
|
||||
// if (bucketOwner === canonicalID) {
|
||||
// permission = 'allow';
|
||||
// }
|
||||
let copiedStatement = JSON.parse(JSON.stringify(policy.Statement));
|
||||
while (copiedStatement.length > 0) {
|
||||
const s = copiedStatement[0];
|
||||
|
@ -268,75 +297,195 @@ function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, l
|
|||
return permission;
|
||||
}
|
||||
|
||||
function isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request) {
|
||||
function isBucketAuthorized(bucket, requestTypes, canonicalID, authInfo, actionImplicitDenies, log, request) {
|
||||
if (!Array.isArray(requestTypes)) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
requestTypes = [requestTypes];
|
||||
}
|
||||
if (!actionImplicitDenies) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
actionImplicitDenies = {};
|
||||
}
|
||||
// By default, all missing actions are defined as allowed from IAM, to be
|
||||
// backward compatible
|
||||
requestTypes.forEach(requestType => {
|
||||
if (actionImplicitDenies[requestType] === undefined) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
actionImplicitDenies[requestType] = false;
|
||||
}
|
||||
});
|
||||
const mainApiCall = requestTypes[0];
|
||||
const results = {};
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
const aclPermission = checkBucketAcls(bucket, requestType, canonicalID);
|
||||
const bucketPolicy = bucket.getBucketPolicy();
|
||||
if (!bucketPolicy) {
|
||||
return aclPermission;
|
||||
}
|
||||
const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType,
|
||||
canonicalID, arn, bucket.getOwner(), log, request);
|
||||
if (bucketPolicyPermission === 'explicitDeny') {
|
||||
return false;
|
||||
}
|
||||
return (aclPermission || (bucketPolicyPermission === 'allow'));
|
||||
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] = actionImplicitDenies[_requestType] === false;
|
||||
return;
|
||||
}
|
||||
const aclPermission = checkBucketAcls(bucket, _requestType, canonicalID, mainApiCall);
|
||||
const bucketPolicy = bucket.getBucketPolicy();
|
||||
if (!bucketPolicy) {
|
||||
results[_requestType] = actionImplicitDenies[_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] = actionImplicitDenies[_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) {
|
||||
const bucketOwner = bucket.getOwner();
|
||||
if (!objectMD) {
|
||||
// User is already authorized on the bucket for FULL_CONTROL or WRITE or
|
||||
// bucket has canned ACL public-read-write
|
||||
if (requestType === 'objectPut' || requestType === 'objectDelete') {
|
||||
return true;
|
||||
function evaluateBucketPolicyWithIAM(bucket, requestTypes, canonicalID, authInfo, actionImplicitDenies, log, request) {
|
||||
if (!Array.isArray(requestTypes)) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
requestTypes = [requestTypes];
|
||||
}
|
||||
if (iamAuthzResults === false) {
|
||||
// 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 (actionImplicitDenies[requestType] === undefined) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
actionImplicitDenies[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] = actionImplicitDenies[_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] = actionImplicitDenies[_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, actionImplicitDenies, log, request) {
|
||||
if (!Array.isArray(requestTypes)) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
requestTypes = [requestTypes];
|
||||
}
|
||||
let requesterIsNotUser = true;
|
||||
let arn = null;
|
||||
if (authInfo) {
|
||||
requesterIsNotUser = !authInfo.isRequesterAnIAMUser();
|
||||
arn = authInfo.getArn();
|
||||
// By default, all missing actions are defined as allowed from IAM, to be
|
||||
// backward compatible
|
||||
if (!actionImplicitDenies) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
actionImplicitDenies = {};
|
||||
}
|
||||
if (objectMD['owner-id'] === canonicalID && requesterIsNotUser) {
|
||||
return true;
|
||||
}
|
||||
// account is authorized if:
|
||||
// - requesttype is included in bucketOwnerActions and
|
||||
// - account is the bucket owner
|
||||
// - requester is account, not user
|
||||
if (bucketOwnerActions.includes(requestType)
|
||||
&& (bucketOwner === canonicalID)
|
||||
&& requesterIsNotUser) {
|
||||
return true;
|
||||
}
|
||||
const aclPermission = checkObjectAcls(bucket, objectMD, requestType,
|
||||
canonicalID);
|
||||
const bucketPolicy = bucket.getBucketPolicy();
|
||||
if (!bucketPolicy) {
|
||||
return aclPermission;
|
||||
}
|
||||
const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType,
|
||||
canonicalID, arn, bucket.getOwner(), log, request);
|
||||
if (bucketPolicyPermission === 'explicitDeny') {
|
||||
return false;
|
||||
}
|
||||
return (aclPermission || (bucketPolicyPermission === 'allow'));
|
||||
requestTypes.forEach(requestType => {
|
||||
if (actionImplicitDenies[requestType] === undefined) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
actionImplicitDenies[requestType] = false;
|
||||
}
|
||||
});
|
||||
const results = {};
|
||||
const mainApiCall = requestTypes[0];
|
||||
requestTypes.forEach(_requestType => {
|
||||
const parsedMethodName = _requestType.endsWith('Version') ?
|
||||
_requestType.slice(0, -7) : _requestType;
|
||||
const bucketOwner = bucket.getOwner();
|
||||
if (!objectMD) {
|
||||
// User is already authorized on the bucket for FULL_CONTROL or WRITE or
|
||||
// bucket has canned ACL public-read-write
|
||||
if (parsedMethodName === 'objectPut' || parsedMethodName === 'objectDelete') {
|
||||
results[_requestType] = actionImplicitDenies[_requestType] === false;
|
||||
return;
|
||||
}
|
||||
// check bucket has read access
|
||||
// 'bucketGet' covers listObjects and listMultipartUploads, bucket read actions
|
||||
results[_requestType] = isBucketAuthorized(bucket, 'bucketGet', canonicalID, authInfo,
|
||||
actionImplicitDenies, log, request);
|
||||
return;
|
||||
}
|
||||
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] = actionImplicitDenies[_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] = actionImplicitDenies[_requestType] === false;
|
||||
return;
|
||||
}
|
||||
const aclPermission = checkObjectAcls(bucket, objectMD, parsedMethodName,
|
||||
canonicalID, requesterIsNotUser, isUserUnauthenticated, mainApiCall);
|
||||
const bucketPolicy = bucket.getBucketPolicy();
|
||||
if (!bucketPolicy) {
|
||||
results[_requestType] = actionImplicitDenies[_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] = actionImplicitDenies[_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) {
|
||||
|
@ -383,13 +532,14 @@ function isLifecycleSession(arn) {
|
|||
const resourceType = resourceNames[0];
|
||||
const sessionName = resourceNames[resourceNames.length - 1];
|
||||
|
||||
return (service === 'sts' &&
|
||||
resourceType === assumedRoleArnResourceType &&
|
||||
sessionName === backbeatLifecycleSessionName);
|
||||
return (service === 'sts'
|
||||
&& resourceType === assumedRoleArnResourceType
|
||||
&& sessionName === backbeatLifecycleSessionName);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isBucketAuthorized,
|
||||
evaluateBucketPolicyWithIAM,
|
||||
isObjAuthorized,
|
||||
checkBucketAcls,
|
||||
checkObjectAcls,
|
||||
|
|
|
@ -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) => {
|
||||
const splitterChar = mpu.key.includes(oldSplitter) ?
|
||||
oldSplitter : splitter;
|
||||
|
@ -40,7 +40,7 @@ function _deleteOngoingMPUs(authInfo, bucketName, bucketMD, mpus, log, cb) {
|
|||
byteLength: partSizeSum,
|
||||
});
|
||||
next(err);
|
||||
});
|
||||
}, request);
|
||||
}, cb);
|
||||
}
|
||||
/**
|
||||
|
@ -49,11 +49,13 @@ function _deleteOngoingMPUs(authInfo, bucketName, bucketMD, mpus, log, cb) {
|
|||
* @param {object} bucketMD - bucket attributes/metadata
|
||||
* @param {string} bucketName - bucket in which objectMetadata is stored
|
||||
* @param {string} canonicalID - account canonicalID of requester
|
||||
* @param {object} request - request object given by router
|
||||
* including normalized headers
|
||||
* @param {object} log - Werelogs logger
|
||||
* @param {function} cb - callback from async.waterfall in bucketDelete
|
||||
* @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');
|
||||
assert.strictEqual(typeof bucketName, 'string');
|
||||
assert.strictEqual(typeof canonicalID, 'string');
|
||||
|
@ -100,7 +102,7 @@ function deleteBucket(authInfo, bucketMD, bucketName, canonicalID, log, cb) {
|
|||
}
|
||||
if (objectsListRes.Contents.length) {
|
||||
return _deleteOngoingMPUs(authInfo, bucketName,
|
||||
bucketMD, objectsListRes.Contents, log, err => {
|
||||
bucketMD, objectsListRes.Contents, request, log, err => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ function abortMultipartUpload(authInfo, bucketName, objectKey, uploadId, log,
|
|||
bucketName,
|
||||
objectKey,
|
||||
uploadId,
|
||||
preciseRequestType: 'multipartDelete',
|
||||
preciseRequestType: request.apiMethods || 'multipartDelete',
|
||||
request,
|
||||
};
|
||||
// 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'
|
||||
const metadataValParams = Object.assign({}, metadataValMPUparams);
|
||||
metadataValParams.requestType = 'objectPut';
|
||||
const authzIdentityResult = request ? request.actionImplicitDenies : true;
|
||||
|
||||
async.waterfall([
|
||||
function checkDestBucketVal(next) {
|
||||
metadataValidateBucketAndObj(metadataValParams, log,
|
||||
metadataValidateBucketAndObj(metadataValParams, authzIdentityResult, log,
|
||||
(err, destinationBucket) => {
|
||||
if (err) {
|
||||
return next(err, destinationBucket);
|
||||
|
@ -56,9 +57,15 @@ function abortMultipartUpload(authInfo, bucketName, objectKey, uploadId, log,
|
|||
function abortExternalMpu(mpuBucket, mpuOverviewObj, destBucket,
|
||||
next) {
|
||||
const location = mpuOverviewObj.controllingLocationConstraint;
|
||||
const originalIdentityImpDenies = request.actionImplicitDenies;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
delete request.actionImplicitDenies;
|
||||
return data.abortMPU(objectKey, uploadId, location, bucketName,
|
||||
request, destBucket, locationConstraintCheck, log,
|
||||
request, destBucket, locationConstraintCheck, log,
|
||||
(err, skipDataDelete) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
request.actionImplicitDenies = originalIdentityImpDenies;
|
||||
if (err) {
|
||||
return next(err, destBucket);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ const moment = require('moment');
|
|||
|
||||
const { config } = require('../../../Config');
|
||||
const vault = require('../../../auth/vault');
|
||||
const { evaluateBucketPolicyWithIAM } = require('../authorization/permissionChecks');
|
||||
|
||||
/**
|
||||
* Calculates retain until date for the locked object version
|
||||
|
@ -302,7 +303,11 @@ function checkUserGovernanceBypass(request, authInfo, bucketMD, objectKey, log,
|
|||
if (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',
|
||||
{
|
||||
'method': 'checkUserPolicyGovernanceBypass',
|
||||
|
@ -310,7 +315,25 @@ function checkUserGovernanceBypass(request, authInfo, bucketMD, objectKey, log,
|
|||
});
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -322,4 +345,4 @@ module.exports = {
|
|||
hasGovernanceBypassHeader,
|
||||
checkUserGovernanceBypass,
|
||||
ObjectLockInfo,
|
||||
};
|
||||
};
|
|
@ -27,11 +27,11 @@ function bucketDelete(authInfo, request, log, cb) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketDelete',
|
||||
requestType: request.apiMethods || 'bucketDelete',
|
||||
request,
|
||||
};
|
||||
|
||||
return metadataValidateBucket(metadataValParams, log,
|
||||
return metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucketMD) => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucketMD);
|
||||
|
@ -43,7 +43,7 @@ function bucketDelete(authInfo, request, log, cb) {
|
|||
log.trace('passed checks',
|
||||
{ method: 'metadataValidateBucket' });
|
||||
return deleteBucket(authInfo, bucketMD, bucketName,
|
||||
authInfo.getCanonicalID(), log, err => {
|
||||
authInfo.getCanonicalID(), request, log, err => {
|
||||
if (err) {
|
||||
return cb(err, corsHeaders);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,8 @@ function bucketDeleteCors(authInfo, request, log, callback) {
|
|||
}
|
||||
log.trace('found bucket in metadata');
|
||||
|
||||
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
|
||||
if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
|
||||
request.actionImplicitDenies, log, request)) {
|
||||
log.debug('access denied for user on bucket', {
|
||||
requestType,
|
||||
method: 'bucketDeleteCors',
|
||||
|
|
|
@ -21,12 +21,12 @@ function bucketDeleteEncryption(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketDeleteEncryption',
|
||||
requestType: request.apiMethods || 'bucketDeleteEncryption',
|
||||
request,
|
||||
};
|
||||
|
||||
return async.waterfall([
|
||||
next => metadataValidateBucket(metadataValParams, log, next),
|
||||
next => metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, next),
|
||||
(bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => next(err, bucket)),
|
||||
(bucket, next) => {
|
||||
const sseConfig = bucket.getServerSideEncryption();
|
||||
|
|
|
@ -17,10 +17,10 @@ function bucketDeleteLifecycle(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketDeleteLifecycle',
|
||||
requestType: request.apiMethods || 'bucketDeleteLifecycle',
|
||||
request,
|
||||
};
|
||||
return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
return metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
|
||||
if (err) {
|
||||
log.debug('error processing request', {
|
||||
|
|
|
@ -16,10 +16,10 @@ function bucketDeletePolicy(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketDeletePolicy',
|
||||
requestType: request.apiMethods || 'bucketDeletePolicy',
|
||||
request,
|
||||
};
|
||||
return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
return metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
|
||||
if (err) {
|
||||
log.debug('error processing request', {
|
||||
|
|
|
@ -17,10 +17,10 @@ function bucketDeleteReplication(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketDeleteReplication',
|
||||
requestType: request.apiMethods || 'bucketDeleteReplication',
|
||||
request,
|
||||
};
|
||||
return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
return metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
|
||||
if (err) {
|
||||
log.debug('error processing request', {
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
const { metadataValidateBucket } = require('../metadata/metadataUtils');
|
||||
const { pushMetric } = require('../utapi/utilities');
|
||||
const metadata = require('../metadata/wrapper');
|
||||
const util = require('node:util');
|
||||
const monitoring = require('../utilities/metrics');
|
||||
|
||||
/**
|
||||
* Bucket Delete Tagging - Delete a bucket's Tagging
|
||||
* @param {AuthInfo} authInfo - Instance of AuthInfo class with requester's info
|
||||
* @param {object} request - http request object
|
||||
* @param {object} log - Werelogs logger
|
||||
* @param {function} callback - callback to server
|
||||
* @return {undefined}
|
||||
*/
|
||||
async function bucketDeleteTagging(authInfo, request, log, callback) {
|
||||
const bucketName = request.bucketName;
|
||||
let error = null;
|
||||
log.debug('processing request', { method: 'bucketDeleteTagging', bucketName });
|
||||
|
||||
let bucket;
|
||||
const metadataValidateBucketPromise = util.promisify(metadataValidateBucket);
|
||||
let updateBucketPromise = util.promisify(metadata.updateBucket);
|
||||
// necessary to bind metadata as updateBucket calls 'this', causing undefined otherwise
|
||||
updateBucketPromise = updateBucketPromise.bind(metadata);
|
||||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: request.apiMethods || 'bucketDeleteTagging',
|
||||
};
|
||||
|
||||
try {
|
||||
bucket = await metadataValidateBucketPromise(metadataValParams, request.actionImplicitDenies, log);
|
||||
bucket.setTags([]);
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
await updateBucketPromise(bucket.getName(), bucket, log);
|
||||
pushMetric('deleteBucketTagging', log, {
|
||||
authInfo,
|
||||
bucket: bucketName,
|
||||
});
|
||||
monitoring.promMetrics(
|
||||
'DELETE', bucketName, '200', 'deleteBucketTagging');
|
||||
} catch (err) {
|
||||
error = err;
|
||||
log.error('error processing request', { error: err,
|
||||
method: 'deleteBucketTagging', bucketName });
|
||||
monitoring.promMetrics('DELETE', bucketName, err.code,
|
||||
'deleteBucketTagging');
|
||||
}
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
return callback(error, corsHeaders);
|
||||
}
|
||||
|
||||
module.exports = bucketDeleteTagging;
|
|
@ -25,7 +25,8 @@ function bucketDeleteWebsite(authInfo, request, log, callback) {
|
|||
}
|
||||
log.trace('found bucket in metadata');
|
||||
|
||||
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
|
||||
if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
|
||||
request.actionImplicitDenies, log, request)) {
|
||||
log.debug('access denied for user on bucket', {
|
||||
requestType,
|
||||
method: 'bucketDeleteWebsite',
|
||||
|
|
|
@ -322,7 +322,7 @@ function bucketGet(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketGet',
|
||||
requestType: request.apiMethods || 'bucketGet',
|
||||
request,
|
||||
};
|
||||
const listParams = {
|
||||
|
@ -345,7 +345,7 @@ function bucketGet(authInfo, request, log, callback) {
|
|||
listParams.marker = params.marker;
|
||||
}
|
||||
|
||||
metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
if (err) {
|
||||
|
|
|
@ -43,7 +43,7 @@ function bucketGetACL(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketGetACL',
|
||||
requestType: request.apiMethods || 'bucketGetACL',
|
||||
request,
|
||||
};
|
||||
const grantInfo = {
|
||||
|
@ -54,7 +54,7 @@ function bucketGetACL(authInfo, request, log, callback) {
|
|||
},
|
||||
};
|
||||
|
||||
metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
if (err) {
|
||||
|
|
|
@ -34,7 +34,8 @@ function bucketGetCors(authInfo, request, log, callback) {
|
|||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
|
||||
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
|
||||
if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
|
||||
request.actionImplicitDenies, log, request)) {
|
||||
log.debug('access denied for user on bucket', {
|
||||
requestType,
|
||||
method: 'bucketGetCors',
|
||||
|
|
|
@ -22,12 +22,12 @@ function bucketGetEncryption(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketGetEncryption',
|
||||
requestType: request.apiMethods || 'bucketGetEncryption',
|
||||
request,
|
||||
};
|
||||
|
||||
return async.waterfall([
|
||||
next => metadataValidateBucket(metadataValParams, log, next),
|
||||
next => metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, next),
|
||||
(bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => next(err, bucket)),
|
||||
(bucket, next) => {
|
||||
// If sseInfo is present but the `mandatory` flag is not set
|
||||
|
|
|
@ -20,10 +20,10 @@ function bucketGetLifecycle(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketGetLifecycle',
|
||||
requestType: request.apiMethods || 'bucketGetLifecycle',
|
||||
request,
|
||||
};
|
||||
return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
return metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
|
||||
if (err) {
|
||||
log.debug('error processing request', {
|
||||
|
|
|
@ -36,7 +36,8 @@ function bucketGetLocation(authInfo, request, log, callback) {
|
|||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
|
||||
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
|
||||
if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
|
||||
request.actionImplicitDenies, log, request)) {
|
||||
log.debug('access denied for account on bucket', {
|
||||
requestType,
|
||||
method: 'bucketGetLocation',
|
||||
|
|
|
@ -37,11 +37,11 @@ function bucketGetNotification(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketGetNotification',
|
||||
requestType: request.apiMethods || 'bucketGetNotification',
|
||||
request,
|
||||
};
|
||||
|
||||
return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
return metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
|
||||
if (err) {
|
||||
log.debug('error processing request', {
|
||||
|
|
|
@ -33,10 +33,10 @@ function bucketGetObjectLock(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketGetObjectLock',
|
||||
requestType: request.apiMethods || 'bucketGetObjectLock',
|
||||
request,
|
||||
};
|
||||
return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
return metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
|
||||
if (err) {
|
||||
log.debug('error processing request', {
|
||||
|
|
|
@ -17,11 +17,11 @@ function bucketGetPolicy(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketGetPolicy',
|
||||
requestType: request.apiMethods || 'bucketGetPolicy',
|
||||
request,
|
||||
};
|
||||
|
||||
return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
return metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
|
||||
if (err) {
|
||||
log.debug('error processing request', {
|
||||
|
|
|
@ -20,10 +20,10 @@ function bucketGetReplication(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketGetReplication',
|
||||
requestType: request.apiMethods || 'bucketGetReplication',
|
||||
request,
|
||||
};
|
||||
return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
return metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
|
||||
if (err) {
|
||||
log.debug('error processing request', {
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
const { metadataValidateBucket } = require('../metadata/metadataUtils');
|
||||
const util = require('node:util');
|
||||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
const { checkExpectedBucketOwner } = require('./apiUtils/authorization/bucketOwner');
|
||||
const { pushMetric } = require('../utapi/utilities');
|
||||
const monitoring = require('../utilities/metrics');
|
||||
const { errors, s3middleware } = require('arsenal');
|
||||
const escapeForXml = s3middleware.escapeForXml;
|
||||
|
||||
// Sample XML response:
|
||||
/*
|
||||
<Tagging>
|
||||
<TagSet>
|
||||
<Tag>
|
||||
<Key>string</Key>
|
||||
<Value>string</Value>
|
||||
</Tag>
|
||||
<Tag>
|
||||
<Key>string</Key>
|
||||
<Value>string</Value>
|
||||
</Tag>
|
||||
</TagSet>
|
||||
</Tagging>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Tag
|
||||
* @type {object}
|
||||
* @property {string} Value - Value of the tag.
|
||||
* @property {string} Key - Key of the tag.
|
||||
*/
|
||||
/**
|
||||
* Convert Versioning Configuration object of a bucket into xml format.
|
||||
* @param {array.<Tag>} tags - set of bucket tag
|
||||
* @return {string} - the converted xml string of the versioning configuration
|
||||
*/
|
||||
function tagsToXml(tags) {
|
||||
const xml = [];
|
||||
|
||||
xml.push('<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Tagging><TagSet>');
|
||||
|
||||
tags.forEach(tag => {
|
||||
xml.push('<Tag>');
|
||||
xml.push(`<Key>${escapeForXml(tag.Key)}</Key>`);
|
||||
xml.push(`<Value>${escapeForXml(tag.Value)}</Value>`);
|
||||
xml.push('</Tag>');
|
||||
});
|
||||
|
||||
xml.push('</TagSet></Tagging>');
|
||||
|
||||
return xml.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* bucketGetVersioning - Return Versioning Configuration for bucket
|
||||
* @param {AuthInfo} authInfo - Instance of AuthInfo class with requester's info
|
||||
* @param {object} request - http request object
|
||||
* @param {object} log - Werelogs logger
|
||||
* @param {function} callback - callback to respond to http request
|
||||
* with either error code or xml response body
|
||||
* @return {undefined}
|
||||
*/
|
||||
async function bucketGetTagging(authInfo, request, log, callback) {
|
||||
log.debug('processing request', { method: 'bucketGetTagging' });
|
||||
|
||||
const { bucketName, headers } = request;
|
||||
const metadataValidateBucketPromise = util.promisify(metadataValidateBucket);
|
||||
const checkExpectedBucketOwnerPromise = util.promisify(checkExpectedBucketOwner);
|
||||
|
||||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: request.apiMethods || 'bucketGetTagging',
|
||||
request,
|
||||
};
|
||||
|
||||
let bucket;
|
||||
let xml = null;
|
||||
|
||||
try {
|
||||
bucket = await metadataValidateBucketPromise(metadataValParams, request.actionImplicitDenies, log);
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
await checkExpectedBucketOwnerPromise(headers, bucket, log);
|
||||
const tags = bucket.getTags();
|
||||
if (!tags || !tags.length) {
|
||||
log.debug('bucket TagSet does not exist', {
|
||||
method: 'bucketGetTagging',
|
||||
});
|
||||
throw errors.NoSuchTagSet;
|
||||
}
|
||||
xml = tagsToXml(tags);
|
||||
pushMetric('getBucketTagging', log, {
|
||||
authInfo,
|
||||
bucket: bucketName,
|
||||
});
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
monitoring.promMetrics(
|
||||
'GET', bucketName, '200', 'getBucketTagging');
|
||||
return callback(null, xml, corsHeaders);
|
||||
} catch (err) {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
log.debug('error processing request',
|
||||
{ method: 'bucketGetTagging', error: err });
|
||||
monitoring.promMetrics('GET', bucketName, err.code,
|
||||
'getBucketTagging');
|
||||
return callback(err, corsHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = bucketGetTagging;
|
|
@ -53,11 +53,11 @@ function bucketGetVersioning(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketGetVersioning',
|
||||
requestType: request.apiMethods || 'bucketGetVersioning',
|
||||
request,
|
||||
};
|
||||
|
||||
metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
if (err) {
|
||||
|
|
|
@ -34,7 +34,8 @@ function bucketGetWebsite(authInfo, request, log, callback) {
|
|||
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
|
||||
if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
|
||||
request.actionImplicitDenies, log, request)) {
|
||||
log.debug('access denied for user on bucket', {
|
||||
requestType,
|
||||
method: 'bucketGetWebsite',
|
||||
|
|
|
@ -18,10 +18,10 @@ function bucketHead(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketHead',
|
||||
requestType: request.apiMethods || 'bucketHead',
|
||||
request,
|
||||
};
|
||||
metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
if (err) {
|
||||
|
|
|
@ -43,7 +43,7 @@ const { pushMetric } = require('../utapi/utilities');
|
|||
function bucketPutACL(authInfo, request, log, callback) {
|
||||
log.debug('processing request', { method: 'bucketPutACL' });
|
||||
|
||||
const bucketName = request.bucketName;
|
||||
const { bucketName } = request;
|
||||
const canonicalID = authInfo.getCanonicalID();
|
||||
const newCannedACL = request.headers['x-amz-acl'];
|
||||
const possibleCannedACL = [
|
||||
|
@ -53,17 +53,6 @@ function bucketPutACL(authInfo, request, log, callback) {
|
|||
'authenticated-read',
|
||||
'log-delivery-write',
|
||||
];
|
||||
if (newCannedACL && possibleCannedACL.indexOf(newCannedACL) === -1) {
|
||||
log.trace('invalid canned acl argument', {
|
||||
acl: newCannedACL,
|
||||
method: 'bucketPutACL',
|
||||
});
|
||||
return callback(errors.InvalidArgument);
|
||||
}
|
||||
if (!aclUtils.checkGrantHeaderValidity(request.headers)) {
|
||||
log.trace('invalid acl header');
|
||||
return callback(errors.InvalidArgument);
|
||||
}
|
||||
const possibleGroups = [constants.allAuthedUsersId,
|
||||
constants.publicId,
|
||||
constants.logId,
|
||||
|
@ -71,7 +60,7 @@ function bucketPutACL(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketPutACL',
|
||||
requestType: request.apiMethods || 'bucketPutACL',
|
||||
request,
|
||||
};
|
||||
const possibleGrants = ['FULL_CONTROL', 'WRITE',
|
||||
|
@ -85,34 +74,43 @@ function bucketPutACL(authInfo, request, log, callback) {
|
|||
READ_ACP: [],
|
||||
};
|
||||
|
||||
const grantReadHeader =
|
||||
aclUtils.parseGrant(request.headers[
|
||||
'x-amz-grant-read'], 'READ');
|
||||
const grantWriteHeader =
|
||||
aclUtils.parseGrant(request.headers['x-amz-grant-write'], 'WRITE');
|
||||
const grantReadACPHeader =
|
||||
aclUtils.parseGrant(request.headers['x-amz-grant-read-acp'],
|
||||
'READ_ACP');
|
||||
const grantWriteACPHeader =
|
||||
aclUtils.parseGrant(request.headers['x-amz-grant-write-acp'],
|
||||
'WRITE_ACP');
|
||||
const grantFullControlHeader =
|
||||
aclUtils.parseGrant(request.headers['x-amz-grant-full-control'],
|
||||
'FULL_CONTROL');
|
||||
const grantReadHeader = aclUtils.parseGrant(request.headers[
|
||||
'x-amz-grant-read'], 'READ');
|
||||
const grantWriteHeader = aclUtils.parseGrant(request.headers['x-amz-grant-write'], 'WRITE');
|
||||
const grantReadACPHeader = aclUtils.parseGrant(request.headers['x-amz-grant-read-acp'],
|
||||
'READ_ACP');
|
||||
const grantWriteACPHeader = aclUtils.parseGrant(request.headers['x-amz-grant-write-acp'],
|
||||
'WRITE_ACP');
|
||||
const grantFullControlHeader = aclUtils.parseGrant(request.headers['x-amz-grant-full-control'],
|
||||
'FULL_CONTROL');
|
||||
|
||||
return async.waterfall([
|
||||
function waterfall1(next) {
|
||||
metadataValidateBucket(metadataValParams, log,
|
||||
(err, bucket) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed', {
|
||||
error: err,
|
||||
method: 'metadataValidateBucket',
|
||||
});
|
||||
return next(err, bucket);
|
||||
}
|
||||
return next(null, bucket);
|
||||
});
|
||||
metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed', {
|
||||
error: err,
|
||||
method: 'metadataValidateBucket',
|
||||
});
|
||||
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);
|
||||
});
|
||||
},
|
||||
function waterfall2(bucket, next) {
|
||||
// If not setting acl through headers, parse body
|
||||
|
@ -179,7 +177,7 @@ function bucketPutACL(authInfo, request, log, callback) {
|
|||
if (!skip && granteeType === 'Group') {
|
||||
if (possibleGroups.indexOf(grantee.URI[0]) < 0) {
|
||||
log.trace('invalid user group',
|
||||
{ userGroup: grantee.URI[0] });
|
||||
{ userGroup: grantee.URI[0] });
|
||||
return next(errors.InvalidArgument, bucket);
|
||||
}
|
||||
return usersIdentifiedByGroup.push({
|
||||
|
@ -193,22 +191,23 @@ function bucketPutACL(authInfo, request, log, callback) {
|
|||
} else {
|
||||
// If no canned ACL and no parsed xml, loop
|
||||
// through the access headers
|
||||
const allGrantHeaders =
|
||||
[].concat(grantReadHeader, grantWriteHeader,
|
||||
const allGrantHeaders = [].concat(grantReadHeader, grantWriteHeader,
|
||||
grantReadACPHeader, grantWriteACPHeader,
|
||||
grantFullControlHeader);
|
||||
|
||||
usersIdentifiedByEmail = allGrantHeaders.filter(item =>
|
||||
item && item.userIDType.toLowerCase() === 'emailaddress');
|
||||
usersIdentifiedByEmail = allGrantHeaders.filter(item => item
|
||||
&& item.userIDType.toLowerCase() === 'emailaddress');
|
||||
|
||||
usersIdentifiedByGroup = allGrantHeaders
|
||||
.filter(itm => itm && itm.userIDType
|
||||
.toLowerCase() === 'uri');
|
||||
.toLowerCase() === 'uri');
|
||||
for (let i = 0; i < usersIdentifiedByGroup.length; i++) {
|
||||
const userGroup = usersIdentifiedByGroup[i].identifier;
|
||||
if (possibleGroups.indexOf(userGroup) < 0) {
|
||||
log.trace('invalid user group', { userGroup,
|
||||
method: 'bucketPutACL' });
|
||||
log.trace('invalid user group', {
|
||||
userGroup,
|
||||
method: 'bucketPutACL',
|
||||
});
|
||||
return next(errors.InvalidArgument, bucket);
|
||||
}
|
||||
}
|
||||
|
@ -241,8 +240,8 @@ function bucketPutACL(authInfo, request, log, callback) {
|
|||
return vault.getCanonicalIds(justEmails, log,
|
||||
(err, results) => {
|
||||
if (err) {
|
||||
log.trace('error looking up canonical ids', {
|
||||
error: err, method: 'vault.getCanonicalIDs' });
|
||||
log.trace('error looking up canonical ids',
|
||||
{ error: err, method: 'vault.getCanonicalIDs' });
|
||||
return next(err, bucket);
|
||||
}
|
||||
const reconstructedUsersIdentifiedByEmail = aclUtils
|
||||
|
@ -251,7 +250,8 @@ function bucketPutACL(authInfo, request, log, callback) {
|
|||
const allUsers = [].concat(
|
||||
reconstructedUsersIdentifiedByEmail,
|
||||
usersIdentifiedByID,
|
||||
usersIdentifiedByGroup);
|
||||
usersIdentifiedByGroup,
|
||||
);
|
||||
const revisedAddACLParams = aclUtils
|
||||
.sortHeaderGrants(allUsers, addACLParams);
|
||||
return next(null, bucket, revisedAddACLParams);
|
||||
|
@ -259,9 +259,9 @@ function bucketPutACL(authInfo, request, log, callback) {
|
|||
}
|
||||
const allUsers = [].concat(
|
||||
usersIdentifiedByID,
|
||||
usersIdentifiedByGroup);
|
||||
const revisedAddACLParams =
|
||||
aclUtils.sortHeaderGrants(allUsers, addACLParams);
|
||||
usersIdentifiedByGroup,
|
||||
);
|
||||
const revisedAddACLParams = aclUtils.sortHeaderGrants(allUsers, addACLParams);
|
||||
return next(null, bucket, revisedAddACLParams);
|
||||
},
|
||||
function waterfall4(bucket, addACLParams, next) {
|
||||
|
@ -272,12 +272,10 @@ function bucketPutACL(authInfo, request, log, callback) {
|
|||
if (bucket.hasTransientFlag() || bucket.hasDeletedFlag()) {
|
||||
log.trace('transient or deleted flag so cleaning up bucket');
|
||||
bucket.setFullAcl(addACLParams);
|
||||
return cleanUpBucket(bucket, canonicalID, log, err =>
|
||||
next(err, bucket));
|
||||
return cleanUpBucket(bucket, canonicalID, log, err => next(err, bucket));
|
||||
}
|
||||
// If no bucket flags, just add acl's to bucket metadata
|
||||
return acl.addACL(bucket, addACLParams, log, err =>
|
||||
next(err, bucket));
|
||||
return acl.addACL(bucket, addACLParams, log, err => next(err, bucket));
|
||||
},
|
||||
], (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
|
|
|
@ -4,8 +4,7 @@ const { errors } = require('arsenal');
|
|||
|
||||
const bucketShield = require('./apiUtils/bucket/bucketShield');
|
||||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
const { isBucketAuthorized } =
|
||||
require('./apiUtils/authorization/permissionChecks');
|
||||
const { isBucketAuthorized } = require('./apiUtils/authorization/permissionChecks');
|
||||
const metadata = require('../metadata/wrapper');
|
||||
const { parseCorsXml } = require('./apiUtils/bucket/bucketCors');
|
||||
const { pushMetric } = require('../utapi/utilities');
|
||||
|
@ -22,7 +21,7 @@ const requestType = 'bucketPutCors';
|
|||
*/
|
||||
function bucketPutCors(authInfo, request, log, callback) {
|
||||
log.debug('processing request', { method: 'bucketPutCors' });
|
||||
const bucketName = request.bucketName;
|
||||
const { bucketName } = request;
|
||||
const canonicalID = authInfo.getCanonicalID();
|
||||
|
||||
if (!request.post) {
|
||||
|
@ -66,7 +65,8 @@ function bucketPutCors(authInfo, request, log, callback) {
|
|||
});
|
||||
},
|
||||
function validateBucketAuthorization(bucket, rules, corsHeaders, next) {
|
||||
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
|
||||
if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
|
||||
request.actionImplicitDenies, log, request)) {
|
||||
log.debug('access denied for account on bucket', {
|
||||
requestType,
|
||||
});
|
||||
|
@ -77,8 +77,7 @@ function bucketPutCors(authInfo, request, log, callback) {
|
|||
function updateBucketMetadata(bucket, rules, corsHeaders, next) {
|
||||
log.trace('updating bucket cors rules in metadata');
|
||||
bucket.setCors(rules);
|
||||
metadata.updateBucket(bucketName, bucket, log, err =>
|
||||
next(err, corsHeaders));
|
||||
metadata.updateBucket(bucketName, bucket, log, err => next(err, corsHeaders));
|
||||
},
|
||||
], (err, corsHeaders) => {
|
||||
if (err) {
|
||||
|
|
|
@ -18,17 +18,17 @@ const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
|||
*/
|
||||
|
||||
function bucketPutEncryption(authInfo, request, log, callback) {
|
||||
const bucketName = request.bucketName;
|
||||
const { bucketName } = request;
|
||||
|
||||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketPutEncryption',
|
||||
requestType: request.apiMethods || 'bucketPutEncryption',
|
||||
request,
|
||||
};
|
||||
|
||||
return async.waterfall([
|
||||
next => metadataValidateBucket(metadataValParams, log, next),
|
||||
next => metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, next),
|
||||
(bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => next(err, bucket)),
|
||||
(bucket, next) => {
|
||||
log.trace('parsing encryption config', { method: 'bucketPutEncryption' });
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const { waterfall } = require('async');
|
||||
const uuid = require('uuid/v4');
|
||||
const LifecycleConfiguration =
|
||||
require('arsenal').models.LifecycleConfiguration;
|
||||
const { LifecycleConfiguration } = require('arsenal').models;
|
||||
|
||||
const parseXML = require('../utilities/parseXML');
|
||||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
|
@ -21,11 +20,11 @@ const { pushMetric } = require('../utapi/utilities');
|
|||
function bucketPutLifecycle(authInfo, request, log, callback) {
|
||||
log.debug('processing request', { method: 'bucketPutLifecycle' });
|
||||
|
||||
const bucketName = request.bucketName;
|
||||
const { bucketName } = request;
|
||||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketPutLifecycle',
|
||||
requestType: request.apiMethods || 'bucketPutLifecycle',
|
||||
request,
|
||||
};
|
||||
return waterfall([
|
||||
|
@ -42,7 +41,7 @@ function bucketPutLifecycle(authInfo, request, log, callback) {
|
|||
return next(null, configObj);
|
||||
});
|
||||
},
|
||||
(lcConfig, next) => metadataValidateBucket(metadataValParams, log,
|
||||
(lcConfig, next) => metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket) => {
|
||||
if (err) {
|
||||
return next(err, bucket);
|
||||
|
@ -54,8 +53,7 @@ function bucketPutLifecycle(authInfo, request, log, callback) {
|
|||
bucket.setUid(uuid());
|
||||
}
|
||||
bucket.setLifecycleConfiguration(lcConfig);
|
||||
metadata.updateBucket(bucket.getName(), bucket, log, err =>
|
||||
next(err, bucket));
|
||||
metadata.updateBucket(bucket.getName(), bucket, log, err => next(err, bucket));
|
||||
},
|
||||
], (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
|
|
|
@ -19,11 +19,11 @@ const { pushMetric } = require('../utapi/utilities');
|
|||
function bucketPutNotification(authInfo, request, log, callback) {
|
||||
log.debug('processing request', { method: 'bucketPutNotification' });
|
||||
|
||||
const bucketName = request.bucketName;
|
||||
const { bucketName } = request;
|
||||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketPutNotification',
|
||||
requestType: request.apiMethods || 'bucketPutNotification',
|
||||
request,
|
||||
};
|
||||
|
||||
|
@ -34,7 +34,7 @@ function bucketPutNotification(authInfo, request, log, callback) {
|
|||
const notifConfig = notificationConfig.error ? undefined : notificationConfig;
|
||||
process.nextTick(() => next(notificationConfig.error, notifConfig));
|
||||
},
|
||||
(notifConfig, next) => metadataValidateBucket(metadataValParams, log,
|
||||
(notifConfig, next) => metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket) => next(err, bucket, notifConfig)),
|
||||
(bucket, notifConfig, next) => {
|
||||
bucket.setNotificationConfiguration(notifConfig);
|
||||
|
@ -45,8 +45,10 @@ function bucketPutNotification(authInfo, request, log, callback) {
|
|||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
if (err) {
|
||||
log.trace('error processing request', { error: err,
|
||||
method: 'bucketPutNotification' });
|
||||
log.trace('error processing request', {
|
||||
error: err,
|
||||
method: 'bucketPutNotification',
|
||||
});
|
||||
return callback(err, corsHeaders);
|
||||
}
|
||||
pushMetric('putBucketNotification', log, {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const { waterfall } = require('async');
|
||||
const arsenal = require('arsenal');
|
||||
|
||||
const errors = arsenal.errors;
|
||||
const ObjectLockConfiguration = arsenal.models.ObjectLockConfiguration;
|
||||
const { errors } = arsenal;
|
||||
const { ObjectLockConfiguration } = arsenal.models;
|
||||
|
||||
const parseXML = require('../utilities/parseXML');
|
||||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
|
@ -22,11 +22,11 @@ const { pushMetric } = require('../utapi/utilities');
|
|||
function bucketPutObjectLock(authInfo, request, log, callback) {
|
||||
log.debug('processing request', { method: 'bucketPutObjectLock' });
|
||||
|
||||
const bucketName = request.bucketName;
|
||||
const { bucketName } = request;
|
||||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketPutObjectLock',
|
||||
requestType: request.apiMethods || 'bucketPutObjectLock',
|
||||
request,
|
||||
};
|
||||
return waterfall([
|
||||
|
@ -36,12 +36,12 @@ function bucketPutObjectLock(authInfo, request, log, callback) {
|
|||
// if there was an error getting object lock configuration,
|
||||
// returned configObj will contain 'error' key
|
||||
process.nextTick(() => {
|
||||
const configObj = lockConfigClass.
|
||||
getValidatedObjectLockConfiguration();
|
||||
const configObj = lockConfigClass
|
||||
.getValidatedObjectLockConfiguration();
|
||||
return next(configObj.error || null, configObj);
|
||||
});
|
||||
},
|
||||
(objectLockConfig, next) => metadataValidateBucket(metadataValParams,
|
||||
(objectLockConfig, next) => metadataValidateBucket(metadataValParams, request.actionImplicitDenies,
|
||||
log, (err, bucket) => {
|
||||
if (err) {
|
||||
return next(err, bucket);
|
||||
|
@ -53,23 +53,25 @@ function bucketPutObjectLock(authInfo, request, log, callback) {
|
|||
process.nextTick(() => {
|
||||
if (!isObjectLockEnabled) {
|
||||
return next(errors.InvalidBucketState.customizeDescription(
|
||||
'Object Lock configuration cannot be enabled on ' +
|
||||
'existing buckets'), bucket);
|
||||
'Object Lock configuration cannot be enabled on '
|
||||
+ 'existing buckets',
|
||||
), bucket);
|
||||
}
|
||||
return next(null, bucket, objectLockConfig);
|
||||
});
|
||||
},
|
||||
(bucket, objectLockConfig, next) => {
|
||||
bucket.setObjectLockConfiguration(objectLockConfig);
|
||||
metadata.updateBucket(bucket.getName(), bucket, log, err =>
|
||||
next(err, bucket));
|
||||
metadata.updateBucket(bucket.getName(), bucket, log, err => next(err, bucket));
|
||||
},
|
||||
], (err, bucket) => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
if (err) {
|
||||
log.trace('error processing request', { error: err,
|
||||
method: 'bucketPutObjectLock' });
|
||||
log.trace('error processing request', {
|
||||
error: err,
|
||||
method: 'bucketPutObjectLock',
|
||||
});
|
||||
return callback(err, corsHeaders);
|
||||
}
|
||||
pushMetric('putBucketObjectLock', log, {
|
||||
|
|
|
@ -17,8 +17,7 @@ const { BucketPolicy } = models;
|
|||
function _checkNotImplementedPolicy(policyString) {
|
||||
// bucket names and key names cannot include "", so including those
|
||||
// isolates not implemented keys
|
||||
return policyString.includes('"Condition"')
|
||||
|| policyString.includes('"Service"')
|
||||
return policyString.includes('"Service"')
|
||||
|| policyString.includes('"Federated"');
|
||||
}
|
||||
|
||||
|
@ -37,7 +36,7 @@ function bucketPutPolicy(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketPutPolicy',
|
||||
requestType: request.apiMethods || 'bucketPutPolicy',
|
||||
request,
|
||||
};
|
||||
|
||||
|
@ -70,7 +69,7 @@ function bucketPutPolicy(authInfo, request, log, callback) {
|
|||
return next(null, bucketPolicy);
|
||||
});
|
||||
},
|
||||
(bucketPolicy, next) => metadataValidateBucket(metadataValParams, log,
|
||||
(bucketPolicy, next) => metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket) => {
|
||||
if (err) {
|
||||
return next(err, bucket);
|
||||
|
|
|
@ -27,7 +27,7 @@ function bucketPutReplication(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketPutReplication',
|
||||
requestType: request.apiMethods || 'bucketPutReplication',
|
||||
request,
|
||||
};
|
||||
return waterfall([
|
||||
|
@ -36,7 +36,7 @@ function bucketPutReplication(authInfo, request, log, callback) {
|
|||
// Check bucket user privileges and ensure versioning is 'Enabled'.
|
||||
(config, next) =>
|
||||
// TODO: Validate that destination bucket exists and has versioning.
|
||||
metadataValidateBucket(metadataValParams, log, (err, bucket) => {
|
||||
metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
const { waterfall } = require('async');
|
||||
const { s3middleware } = require('arsenal');
|
||||
|
||||
|
||||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
const { metadataValidateBucket } = require('../metadata/metadataUtils');
|
||||
const metadata = require('../metadata/wrapper');
|
||||
const { pushMetric } = require('../utapi/utilities');
|
||||
const { checkExpectedBucketOwner } = require('./apiUtils/authorization/bucketOwner');
|
||||
const monitoring = require('../utilities/metrics');
|
||||
const { parseTagXml } = s3middleware.tagging;
|
||||
|
||||
/**
|
||||
* Format of xml request:
|
||||
|
||||
<Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<TagSet>
|
||||
<Tag>
|
||||
<Key>string</Key>
|
||||
<Value>string</Value>
|
||||
</Tag>
|
||||
</TagSet>
|
||||
</Tagging>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Bucket Put Tagging - Create or update bucket Tagging
|
||||
* @param {AuthInfo} authInfo - Instance of AuthInfo class with requester's info
|
||||
* @param {object} request - http request object
|
||||
* @param {object} log - Werelogs logger
|
||||
* @param {function} callback - callback to server
|
||||
* @return {undefined}
|
||||
*/
|
||||
function bucketPutTagging(authInfo, request, log, callback) {
|
||||
log.debug('processing request', { method: 'bucketPutTagging' });
|
||||
|
||||
const { bucketName, headers } = request;
|
||||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: request.apiMethods || 'bucketPutTagging',
|
||||
};
|
||||
let bucket = null;
|
||||
return waterfall([
|
||||
next => metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, b) => {
|
||||
bucket = b;
|
||||
return next(err);
|
||||
}),
|
||||
next => checkExpectedBucketOwner(headers, bucket, log, next),
|
||||
next => parseTagXml(request.post, log, next),
|
||||
(tags, next) => {
|
||||
const tagArray = [];
|
||||
Object.keys(tags).forEach(key => {
|
||||
tagArray.push({ Value: tags[key], Key: key });
|
||||
});
|
||||
bucket.setTags(tagArray);
|
||||
metadata.updateBucket(bucket.getName(), bucket, log, err =>
|
||||
next(err));
|
||||
},
|
||||
], err => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
if (err) {
|
||||
log.debug('error processing request', {
|
||||
error: err,
|
||||
method: 'bucketPutTagging',
|
||||
});
|
||||
monitoring.promMetrics('PUT', bucketName, err.code,
|
||||
'putBucketTagging');
|
||||
} else {
|
||||
monitoring.promMetrics(
|
||||
'PUT', bucketName, '200', 'putBucketTagging');
|
||||
pushMetric('putBucketTagging', log, {
|
||||
authInfo,
|
||||
bucket: bucketName,
|
||||
});
|
||||
}
|
||||
return callback(err, corsHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = bucketPutTagging;
|
|
@ -87,13 +87,13 @@ function bucketPutVersioning(authInfo, request, log, callback) {
|
|||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
requestType: 'bucketPutVersioning',
|
||||
requestType: request.apiMethods || 'bucketPutVersioning',
|
||||
request,
|
||||
};
|
||||
|
||||
return waterfall([
|
||||
next => _parseXML(request, log, next),
|
||||
next => metadataValidateBucket(metadataValParams, log,
|
||||
next => metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket) => next(err, bucket)), // ignore extra null object,
|
||||
(bucket, next) => parseString(request.post, (err, result) => {
|
||||
// just for linting; there should not be any parsing error here
|
||||
|
|
|
@ -46,7 +46,8 @@ function bucketPutWebsite(authInfo, request, log, callback) {
|
|||
});
|
||||
},
|
||||
function validateBucketAuthorization(bucket, config, next) {
|
||||
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
|
||||
if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
|
||||
request.actionImplicitDenies, log, request)) {
|
||||
log.debug('access denied for user on bucket', {
|
||||
requestType,
|
||||
method: 'bucketPutWebsite',
|
||||
|
|
|
@ -118,9 +118,9 @@ function completeMultipartUpload(authInfo, request, log, callback) {
|
|||
bucketName,
|
||||
// Required permissions for this action
|
||||
// at the destinationBucket level are same as objectPut
|
||||
requestType: 'objectPut',
|
||||
requestType: request.apiMethods || 'objectPut',
|
||||
};
|
||||
metadataValidateBucketAndObj(metadataValParams, log, next);
|
||||
metadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log, next);
|
||||
},
|
||||
function validateMultipart(destBucket, objMD, next) {
|
||||
if (objMD) {
|
||||
|
@ -190,9 +190,14 @@ function completeMultipartUpload(authInfo, request, log, callback) {
|
|||
const mdInfo = { storedParts, mpuOverviewKey, splitter };
|
||||
const mpuInfo =
|
||||
{ objectKey, uploadId, jsonList, bucketName, destBucket };
|
||||
const originalIdentityImpDenies = request.actionImplicitDenies;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
delete request.actionImplicitDenies;
|
||||
return data.completeMPU(request, mpuInfo, mdInfo, location,
|
||||
null, null, null, locationConstraintCheck, log,
|
||||
(err, completeObjData) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
request.actionImplicitDenies = originalIdentityImpDenies;
|
||||
if (err) {
|
||||
return next(err, destBucket);
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ function initiateMultipartUpload(authInfo, request, log, callback) {
|
|||
authInfo,
|
||||
bucketName,
|
||||
// Required permissions for this action are same as objectPut
|
||||
requestType: 'objectPut',
|
||||
requestType: request.apiMethods || 'objectPut',
|
||||
request,
|
||||
};
|
||||
const accountCanonicalID = authInfo.getCanonicalID();
|
||||
|
@ -253,7 +253,7 @@ function initiateMultipartUpload(authInfo, request, log, callback) {
|
|||
}
|
||||
|
||||
async.waterfall([
|
||||
next => metadataValidateBucketAndObj(metadataValParams, log,
|
||||
next => metadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
|
||||
(error, destinationBucket) => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, destinationBucket);
|
||||
|
|
|
@ -95,8 +95,8 @@ function listMultipartUploads(authInfo, request, log, callback) {
|
|||
// to list the multipart uploads so we have provided here that
|
||||
// the authorization to list multipart uploads is the same
|
||||
// as listing objects in a bucket.
|
||||
requestType: 'bucketGet',
|
||||
preciseRequestType: 'listMultipartUploads',
|
||||
requestType: request.apiMethods || 'bucketGet',
|
||||
preciseRequestType: request.apiMethods || 'listMultipartUploads',
|
||||
request,
|
||||
};
|
||||
|
||||
|
@ -104,7 +104,7 @@ function listMultipartUploads(authInfo, request, log, callback) {
|
|||
function waterfall1(next) {
|
||||
// Check final destination bucket for authorization rather
|
||||
// than multipart upload bucket
|
||||
metadataValidateBucket(metadataValParams, log,
|
||||
metadataValidateBucket(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket) => next(err, bucket));
|
||||
},
|
||||
function getMPUBucket(bucket, next) {
|
||||
|
|
|
@ -95,7 +95,7 @@ function listParts(authInfo, request, log, callback) {
|
|||
bucketName,
|
||||
objectKey,
|
||||
uploadId,
|
||||
preciseRequestType: 'listParts',
|
||||
preciseRequestType: request.apiMethods || 'listParts',
|
||||
request,
|
||||
};
|
||||
// For validating the request at the destinationBucket level
|
||||
|
@ -112,7 +112,7 @@ function listParts(authInfo, request, log, callback) {
|
|||
|
||||
async.waterfall([
|
||||
function checkDestBucketVal(next) {
|
||||
metadataValidateBucketAndObj(metadataValParams, log,
|
||||
metadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, destinationBucket) => {
|
||||
if (err) {
|
||||
return next(err, destinationBucket, null);
|
||||
|
@ -150,8 +150,13 @@ function listParts(authInfo, request, log, callback) {
|
|||
mpuOverviewObj,
|
||||
destBucket,
|
||||
};
|
||||
const originalIdentityImpDenies = request.actionImplicitDenies;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
delete request.actionImplicitDenies;
|
||||
return data.listParts(mpuInfo, request, locationConstraintCheck,
|
||||
log, (err, backendPartList) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
request.actionImplicitDenies = originalIdentityImpDenies;
|
||||
if (err) {
|
||||
return next(err, destBucket);
|
||||
}
|
||||
|
|
|
@ -11,11 +11,12 @@ const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
|||
const metadata = require('../metadata/wrapper');
|
||||
const services = require('../services');
|
||||
const vault = require('../auth/vault');
|
||||
const { isBucketAuthorized } =
|
||||
const { isBucketAuthorized, evaluateBucketPolicyWithIAM } =
|
||||
require('./apiUtils/authorization/permissionChecks');
|
||||
const { preprocessingVersioningDelete }
|
||||
= require('./apiUtils/object/versioning');
|
||||
const createAndStoreObject = require('./apiUtils/object/createAndStoreObject');
|
||||
const monitoring = require('../utilities/metrics');
|
||||
const { metadataGetObject } = require('../metadata/metadataUtils');
|
||||
const { config } = require('../Config');
|
||||
const { hasGovernanceBypassHeader, checkUserGovernanceBypass, ObjectLockInfo }
|
||||
|
@ -202,6 +203,8 @@ function getObjMetadataAndDelete(authInfo, canonicalID, request,
|
|||
'null' : versionIdUtils.decode(entry.versionId);
|
||||
}
|
||||
if (decodedVersionId instanceof Error) {
|
||||
monitoring.promMetrics('DELETE', bucketName, 404,
|
||||
'multiObjectDelete');
|
||||
return callback(errors.NoSuchVersion);
|
||||
}
|
||||
return callback(null, decodedVersionId);
|
||||
|
@ -212,6 +215,8 @@ function getObjMetadataAndDelete(authInfo, canonicalID, request,
|
|||
versionId, log, (err, objMD) => {
|
||||
// if general error from metadata return error
|
||||
if (err) {
|
||||
monitoring.promMetrics('DELETE', bucketName, err.code,
|
||||
'multiObjectDelete');
|
||||
return callback(err);
|
||||
}
|
||||
if (!objMD) {
|
||||
|
@ -279,14 +284,16 @@ function getObjMetadataAndDelete(authInfo, canonicalID, request,
|
|||
|
||||
return callback(null, objMD, versionId);
|
||||
},
|
||||
(objMD, versionId, callback) =>
|
||||
preprocessingVersioningDelete(bucketName, bucket, objMD,
|
||||
versionId, log, (err, options) => callback(err, options,
|
||||
objMD)),
|
||||
(options, objMD, callback) => {
|
||||
(objMD, versionId, callback) => {
|
||||
const options = preprocessingVersioningDelete(
|
||||
bucketName, bucket, objMD, versionId, config.nullVersionCompatMode);
|
||||
const deleteInfo = {};
|
||||
if (options && options.deleteData) {
|
||||
deleteInfo.deleted = true;
|
||||
if (objMD.uploadId) {
|
||||
// eslint-disable-next-line
|
||||
options.replayId = objMD.uploadId;
|
||||
}
|
||||
return services.deleteObject(bucketName, objMD,
|
||||
entry.key, options, log, err =>
|
||||
callback(err, objMD, deleteInfo));
|
||||
|
@ -364,11 +371,15 @@ function getObjMetadataAndDelete(authInfo, canonicalID, request,
|
|||
function multiObjectDelete(authInfo, request, log, callback) {
|
||||
log.debug('processing request', { method: 'multiObjectDelete' });
|
||||
if (!request.post) {
|
||||
monitoring.promMetrics('DELETE', request.bucketName, 400,
|
||||
'multiObjectDelete');
|
||||
return callback(errors.MissingRequestBodyError);
|
||||
}
|
||||
const md5 = crypto.createHash('md5')
|
||||
.update(request.post, 'utf8').digest('base64');
|
||||
if (md5 !== request.headers['content-md5']) {
|
||||
monitoring.promMetrics('DELETE', request.bucketName, 400,
|
||||
'multiObjectDelete');
|
||||
return callback(errors.BadDigest);
|
||||
}
|
||||
|
||||
|
@ -385,15 +396,37 @@ function multiObjectDelete(authInfo, request, log, callback) {
|
|||
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.actionImplicitDenies, 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
|
||||
const inPlay = [];
|
||||
const errorResults = [];
|
||||
|
||||
// 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()) {
|
||||
return next(null, quietSetting, errorResults, objects);
|
||||
return next(null, quietSetting, errorResults, objects, bucketMD);
|
||||
}
|
||||
|
||||
// TODO: once arsenal's extractParams is separated from doAuth
|
||||
|
@ -429,7 +462,6 @@ function multiObjectDelete(authInfo, request, log, callback) {
|
|||
};
|
||||
return vault.checkPolicies(requestContextParams, authInfo.getArn(),
|
||||
log, (err, authorizationResults) => {
|
||||
// there were no policies so received a blanket AccessDenied
|
||||
if (err && err.is.AccessDenied) {
|
||||
objects.forEach(entry => {
|
||||
errorResults.push({
|
||||
|
@ -437,7 +469,7 @@ function multiObjectDelete(authInfo, request, log, callback) {
|
|||
error: errors.AccessDenied });
|
||||
});
|
||||
// send empty array for inPlay
|
||||
return next(null, quietSetting, errorResults, []);
|
||||
return next(null, quietSetting, errorResults, [], bucketMD);
|
||||
}
|
||||
if (err) {
|
||||
log.trace('error checking policies', {
|
||||
|
@ -455,6 +487,16 @@ function multiObjectDelete(authInfo, request, log, callback) {
|
|||
});
|
||||
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++) {
|
||||
const result = authorizationResults[i];
|
||||
// result is { isAllowed: true,
|
||||
|
@ -470,7 +512,27 @@ function multiObjectDelete(authInfo, request, log, callback) {
|
|||
key: result.arn.slice(slashIndex + 1),
|
||||
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);
|
||||
} else {
|
||||
errorResults.push({
|
||||
|
@ -479,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,
|
||||
bucket, next) {
|
||||
return getObjMetadataAndDelete(authInfo, canonicalID, request,
|
||||
|
@ -534,6 +555,8 @@ function multiObjectDelete(authInfo, request, log, callback) {
|
|||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
if (err) {
|
||||
monitoring.promMetrics('DELETE', bucketName, err.code,
|
||||
'multiObjectDelete');
|
||||
return callback(err, null, corsHeaders);
|
||||
}
|
||||
const xml = _formatXML(quietSetting, errorResults,
|
||||
|
@ -552,6 +575,10 @@ function multiObjectDelete(authInfo, request, log, callback) {
|
|||
removedDeleteMarkers,
|
||||
isDelete: true,
|
||||
});
|
||||
monitoring.promMetrics('DELETE', bucketName, '200',
|
||||
'multiObjectDelete',
|
||||
Number.parseInt(totalContentLengthDeleted, 10), null, null,
|
||||
numOfObjectsRemoved);
|
||||
return callback(null, xml, corsHeaders);
|
||||
});
|
||||
}
|
||||
|
@ -559,4 +586,4 @@ function multiObjectDelete(authInfo, request, log, callback) {
|
|||
module.exports = {
|
||||
getObjMetadataAndDelete,
|
||||
multiObjectDelete,
|
||||
};
|
||||
};
|
|
@ -245,7 +245,7 @@ function objectCopy(authInfo, request, sourceBucket,
|
|||
}
|
||||
return async.waterfall([
|
||||
function checkDestAuth(next) {
|
||||
return metadataValidateBucketAndObj(valPutParams, log,
|
||||
return metadataValidateBucketAndObj(valPutParams, request.actionImplicitDenies, log,
|
||||
(err, destBucketMD, destObjMD) => {
|
||||
if (err) {
|
||||
log.debug('error validating put part of request',
|
||||
|
@ -263,7 +263,7 @@ function objectCopy(authInfo, request, sourceBucket,
|
|||
});
|
||||
},
|
||||
function checkSourceAuthorization(destBucketMD, destObjMD, next) {
|
||||
return metadataValidateBucketAndObj(valGetParams, log,
|
||||
return metadataValidateBucketAndObj(valGetParams, request.actionImplicitDenies, log,
|
||||
(err, sourceBucketMD, sourceObjMD) => {
|
||||
if (err) {
|
||||
log.debug('error validating get part of request',
|
||||
|
@ -408,10 +408,15 @@ function objectCopy(authInfo, request, sourceBucket,
|
|||
return next(null, storeMetadataParams, dataLocator, destObjMD,
|
||||
serverSideEncryption, destBucketMD);
|
||||
}
|
||||
const originalIdentityImpDenies = request.actionImplicitDenies;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
delete request.actionImplicitDenies;
|
||||
return data.copyObject(request, sourceLocationConstraintName,
|
||||
storeMetadataParams, dataLocator, dataStoreContext,
|
||||
backendInfoDest, sourceBucketMD, destBucketMD, serverSideEncryption, log,
|
||||
(err, results) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
request.actionImplicitDenies = originalIdentityImpDenies;
|
||||
if (err) {
|
||||
return next(err, destBucketMD);
|
||||
}
|
||||
|
|
|
@ -49,15 +49,15 @@ function objectDelete(authInfo, request, log, cb) {
|
|||
bucketName,
|
||||
objectKey,
|
||||
versionId: reqVersionId,
|
||||
requestType: 'objectDelete',
|
||||
requestType: request.apiMethods || 'objectDelete',
|
||||
request,
|
||||
};
|
||||
|
||||
const canonicalID = authInfo.getCanonicalID();
|
||||
return async.waterfall([
|
||||
function validateBucketAndObj(next) {
|
||||
return metadataValidateBucketAndObj(valParams, log,
|
||||
(err, bucketMD, objMD) => {
|
||||
return metadataValidateBucketAndObj(valParams, request.actionImplicitDenies, log,
|
||||
(err, bucketMD, objMD) => {
|
||||
if (err) {
|
||||
return next(err, bucketMD);
|
||||
}
|
||||
|
|
|
@ -40,13 +40,14 @@ function objectDeleteTagging(authInfo, request, log, callback) {
|
|||
authInfo,
|
||||
bucketName,
|
||||
objectKey,
|
||||
requestType: 'objectDeleteTagging',
|
||||
versionId: reqVersionId,
|
||||
getDeleteMarker: true,
|
||||
requestType: request.apiMethods || 'objectDeleteTagging',
|
||||
request,
|
||||
};
|
||||
|
||||
return async.waterfall([
|
||||
next => metadataValidateBucketAndObj(metadataValParams, log,
|
||||
next => metadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket, objectMD) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed',
|
||||
|
|
|
@ -44,11 +44,12 @@ function objectGet(authInfo, request, returnTagCount, log, callback) {
|
|||
bucketName,
|
||||
objectKey,
|
||||
versionId,
|
||||
requestType: 'objectGet',
|
||||
getDeleteMarker: true,
|
||||
requestType: request.apiMethods || 'objectGet',
|
||||
request,
|
||||
};
|
||||
|
||||
return metadataValidateBucketAndObj(mdValParams, log,
|
||||
return metadataValidateBucketAndObj(mdValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket, objMD) => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
|
|
|
@ -58,7 +58,7 @@ function objectGetACL(authInfo, request, log, callback) {
|
|||
bucketName,
|
||||
objectKey,
|
||||
versionId,
|
||||
requestType: 'objectGetACL',
|
||||
requestType: request.apiMethods || 'objectGetACL',
|
||||
request,
|
||||
};
|
||||
const grantInfo = {
|
||||
|
@ -71,7 +71,7 @@ function objectGetACL(authInfo, request, log, callback) {
|
|||
|
||||
return async.waterfall([
|
||||
function validateBucketAndObj(next) {
|
||||
return metadataValidateBucketAndObj(metadataValParams, log,
|
||||
return metadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket, objectMD) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed',
|
||||
|
|
|
@ -37,13 +37,13 @@ function objectGetLegalHold(authInfo, request, log, callback) {
|
|||
authInfo,
|
||||
bucketName,
|
||||
objectKey,
|
||||
requestType: 'objectGetLegalHold',
|
||||
versionId,
|
||||
requestType: request.apiMethods || 'objectGetLegalHold',
|
||||
request,
|
||||
};
|
||||
|
||||
return async.waterfall([
|
||||
next => metadataValidateBucketAndObj(metadataValParams, log,
|
||||
next => metadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket, objectMD) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed',
|
||||
|
|
|
@ -37,13 +37,13 @@ function objectGetRetention(authInfo, request, log, callback) {
|
|||
authInfo,
|
||||
bucketName,
|
||||
objectKey,
|
||||
requestType: 'objectGetRetention',
|
||||
versionId: reqVersionId,
|
||||
requestType: request.apiMethods || 'objectGetRetention',
|
||||
request,
|
||||
};
|
||||
|
||||
return async.waterfall([
|
||||
next => metadataValidateBucketAndObj(metadataValParams, log,
|
||||
next => metadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket, objectMD) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed',
|
||||
|
|
|
@ -37,13 +37,12 @@ function objectGetTagging(authInfo, request, log, callback) {
|
|||
authInfo,
|
||||
bucketName,
|
||||
objectKey,
|
||||
requestType: 'objectGetTagging',
|
||||
versionId: reqVersionId,
|
||||
requestType: request.apiMethods || 'objectGetTagging',
|
||||
request,
|
||||
};
|
||||
|
||||
return async.waterfall([
|
||||
next => metadataValidateBucketAndObj(metadataValParams, log,
|
||||
next => metadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket, objectMD) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed',
|
||||
|
|
|
@ -44,11 +44,12 @@ function objectHead(authInfo, request, log, callback) {
|
|||
bucketName,
|
||||
objectKey,
|
||||
versionId,
|
||||
requestType: 'objectHead',
|
||||
getDeleteMarker: true,
|
||||
requestType: request.apiMethods || 'objectHead',
|
||||
request,
|
||||
};
|
||||
|
||||
return metadataValidateBucketAndObj(mdValParams, log,
|
||||
return metadataValidateBucketAndObj(mdValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket, objMD) => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
|
|
|
@ -57,7 +57,7 @@ function objectPut(authInfo, request, streamingV4Params, log, callback) {
|
|||
}
|
||||
const invalidSSEError = errors.InvalidArgument.customizeDescription(
|
||||
'The encryption method specified is not supported');
|
||||
const requestType = 'objectPut';
|
||||
const requestType = request.apiMethods || 'objectPut';
|
||||
const valParams = { authInfo, bucketName, objectKey, requestType, request };
|
||||
const canonicalID = authInfo.getCanonicalID();
|
||||
|
||||
|
@ -68,8 +68,7 @@ function objectPut(authInfo, request, streamingV4Params, log, callback) {
|
|||
}
|
||||
|
||||
log.trace('owner canonicalID to send to data', { canonicalID });
|
||||
|
||||
return metadataValidateBucketAndObj(valParams, log,
|
||||
return metadataValidateBucketAndObj(valParams, request.actionImplicitDenies, log,
|
||||
(err, bucket, objMD) => {
|
||||
const responseHeaders = collectCorsHeaders(headers.origin,
|
||||
method, bucket);
|
||||
|
|
|
@ -7,8 +7,7 @@ const { pushMetric } = require('../utapi/utilities');
|
|||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
const constants = require('../../constants');
|
||||
const vault = require('../auth/vault');
|
||||
const { decodeVersionId, getVersionIdResHeader }
|
||||
= require('./apiUtils/object/versioning');
|
||||
const { decodeVersionId, getVersionIdResHeader } = require('./apiUtils/object/versioning');
|
||||
const { metadataValidateBucketAndObj } = require('../metadata/metadataUtils');
|
||||
|
||||
/*
|
||||
|
@ -43,8 +42,8 @@ const { metadataValidateBucketAndObj } = require('../metadata/metadataUtils');
|
|||
*/
|
||||
function objectPutACL(authInfo, request, log, cb) {
|
||||
log.debug('processing request', { method: 'objectPutACL' });
|
||||
const bucketName = request.bucketName;
|
||||
const objectKey = request.objectKey;
|
||||
const { bucketName } = request;
|
||||
const { objectKey } = request;
|
||||
const newCannedACL = request.headers['x-amz-acl'];
|
||||
const possibleCannedACL = [
|
||||
'private',
|
||||
|
@ -82,8 +81,9 @@ function objectPutACL(authInfo, request, log, cb) {
|
|||
authInfo,
|
||||
bucketName,
|
||||
objectKey,
|
||||
requestType: 'objectPutACL',
|
||||
versionId: reqVersionId,
|
||||
getDeleteMarker: true,
|
||||
requestType: request.apiMethods || 'objectPutACL',
|
||||
};
|
||||
|
||||
const possibleGrants = ['FULL_CONTROL', 'WRITE_ACP', 'READ', 'READ_ACP'];
|
||||
|
@ -95,26 +95,26 @@ function objectPutACL(authInfo, request, log, cb) {
|
|||
READ_ACP: [],
|
||||
};
|
||||
|
||||
const grantReadHeader =
|
||||
aclUtils.parseGrant(request.headers['x-amz-grant-read'], 'READ');
|
||||
const grantReadACPHeader =
|
||||
aclUtils.parseGrant(request.headers['x-amz-grant-read-acp'],
|
||||
'READ_ACP');
|
||||
const grantReadHeader = aclUtils.parseGrant(request.headers['x-amz-grant-read'], 'READ');
|
||||
const grantReadACPHeader = aclUtils.parseGrant(request.headers['x-amz-grant-read-acp'],
|
||||
'READ_ACP');
|
||||
const grantWriteACPHeader = aclUtils.parseGrant(
|
||||
request.headers['x-amz-grant-write-acp'], 'WRITE_ACP');
|
||||
request.headers['x-amz-grant-write-acp'], 'WRITE_ACP',
|
||||
);
|
||||
const grantFullControlHeader = aclUtils.parseGrant(
|
||||
request.headers['x-amz-grant-full-control'], 'FULL_CONTROL');
|
||||
request.headers['x-amz-grant-full-control'], 'FULL_CONTROL',
|
||||
);
|
||||
|
||||
return async.waterfall([
|
||||
function validateBucketAndObj(next) {
|
||||
return metadataValidateBucketAndObj(metadataValParams, log,
|
||||
return metadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket, objectMD) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!objectMD) {
|
||||
const err = reqVersionId ? errors.NoSuchVersion :
|
||||
errors.NoSuchKey;
|
||||
const err = reqVersionId ? errors.NoSuchVersion
|
||||
: errors.NoSuchKey;
|
||||
return next(err, bucket);
|
||||
}
|
||||
if (objectMD.isDeleteMarker) {
|
||||
|
@ -202,7 +202,7 @@ function objectPutACL(authInfo, request, log, cb) {
|
|||
if (!skip && granteeType === 'Group') {
|
||||
if (possibleGroups.indexOf(grantee.URI[0]) < 0) {
|
||||
log.trace('invalid user group',
|
||||
{ userGroup: grantee.URI[0] });
|
||||
{ userGroup: grantee.URI[0] });
|
||||
return next(errors.InvalidArgument, bucket);
|
||||
}
|
||||
return usersIdentifiedByGroup.push({
|
||||
|
@ -216,22 +216,24 @@ function objectPutACL(authInfo, request, log, cb) {
|
|||
} else {
|
||||
// If no canned ACL and no parsed xml, loop
|
||||
// through the access headers
|
||||
const allGrantHeaders =
|
||||
[].concat(grantReadHeader,
|
||||
const allGrantHeaders = [].concat(grantReadHeader,
|
||||
grantReadACPHeader, grantWriteACPHeader,
|
||||
grantFullControlHeader);
|
||||
|
||||
usersIdentifiedByEmail = allGrantHeaders.filter(item =>
|
||||
item && item.userIDType.toLowerCase() === 'emailaddress');
|
||||
usersIdentifiedByEmail = allGrantHeaders.filter(item => item
|
||||
&& item.userIDType.toLowerCase() === 'emailaddress');
|
||||
usersIdentifiedByGroup = allGrantHeaders
|
||||
.filter(itm => itm && itm.userIDType
|
||||
.toLowerCase() === 'uri');
|
||||
for (let i = 0; i < usersIdentifiedByGroup.length; i++) {
|
||||
.toLowerCase() === 'uri');
|
||||
for (let i = 0; i < usersIdentifiedByGroup.length; i += 1) {
|
||||
if (possibleGroups.indexOf(
|
||||
usersIdentifiedByGroup[i].identifier) < 0) {
|
||||
usersIdentifiedByGroup[i].identifier,
|
||||
) < 0) {
|
||||
log.trace('invalid user group',
|
||||
{ userGroup: usersIdentifiedByGroup[i]
|
||||
.identifier });
|
||||
{
|
||||
userGroup: usersIdentifiedByGroup[i]
|
||||
.identifier,
|
||||
});
|
||||
return next(errors.InvalidArgument, bucket);
|
||||
}
|
||||
}
|
||||
|
@ -259,18 +261,20 @@ function objectPutACL(authInfo, request, log, cb) {
|
|||
const allUsers = [].concat(
|
||||
reconstructedUsersIdentifiedByEmail,
|
||||
usersIdentifiedByID,
|
||||
usersIdentifiedByGroup);
|
||||
usersIdentifiedByGroup,
|
||||
);
|
||||
const revisedAddACLParams = aclUtils
|
||||
.sortHeaderGrants(allUsers, addACLParams);
|
||||
return next(null, bucket, objectMD,
|
||||
revisedAddACLParams);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
const allUsers = [].concat(
|
||||
usersIdentifiedByID,
|
||||
usersIdentifiedByGroup);
|
||||
const revisedAddACLParams =
|
||||
aclUtils.sortHeaderGrants(allUsers, addACLParams);
|
||||
usersIdentifiedByGroup,
|
||||
);
|
||||
const revisedAddACLParams = aclUtils.sortHeaderGrants(allUsers, addACLParams);
|
||||
return next(null, bucket, objectMD, revisedAddACLParams);
|
||||
},
|
||||
function addAclsToObjMD(bucket, objectMD, ACLParams, next) {
|
||||
|
@ -292,8 +296,7 @@ function objectPutACL(authInfo, request, log, cb) {
|
|||
}
|
||||
|
||||
const verCfg = bucket.getVersioningConfiguration();
|
||||
resHeaders['x-amz-version-id'] =
|
||||
getVersionIdResHeader(verCfg, objectMD);
|
||||
resHeaders['x-amz-version-id'] = getVersionIdResHeader(verCfg, objectMD);
|
||||
|
||||
log.trace('processed request successfully in object put acl api');
|
||||
pushMetric('putObjectAcl', log, {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
const async = require('async');
|
||||
const { errors, versioning, s3middleware } = require('arsenal');
|
||||
|
||||
const validateHeaders = s3middleware.validateConditionalHeaders;
|
||||
|
||||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
const constants = require('../../constants');
|
||||
const { data } = require('../data/wrapper');
|
||||
const locationConstraintCheck =
|
||||
require('./apiUtils/object/locationConstraintCheck');
|
||||
const locationConstraintCheck = require('./apiUtils/object/locationConstraintCheck');
|
||||
const metadata = require('../metadata/wrapper');
|
||||
const { pushMetric } = require('../utapi/utilities');
|
||||
const logger = require('../utilities/logger');
|
||||
|
@ -43,6 +43,7 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
bucketName: sourceBucket,
|
||||
objectKey: sourceObject,
|
||||
versionId: reqVersionId,
|
||||
getDeleteMarker: true,
|
||||
requestType: 'objectGet',
|
||||
request,
|
||||
};
|
||||
|
@ -59,7 +60,6 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
// request.query.uploadId must be called with that exact
|
||||
// capitalization
|
||||
const uploadId = request.query.uploadId;
|
||||
|
||||
const valPutParams = {
|
||||
authInfo,
|
||||
bucketName: destBucketName,
|
||||
|
@ -89,26 +89,26 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
|
||||
return async.waterfall([
|
||||
function checkDestAuth(next) {
|
||||
return metadataValidateBucketAndObj(valPutParams, log,
|
||||
return metadataValidateBucketAndObj(valPutParams, request.actionImplicitDenies, log,
|
||||
(err, destBucketMD) => {
|
||||
if (err) {
|
||||
log.debug('error validating authorization for ' +
|
||||
'destination bucket',
|
||||
{ error: err });
|
||||
log.debug('error validating authorization for '
|
||||
+ 'destination bucket',
|
||||
{ error: err });
|
||||
return next(err, destBucketMD);
|
||||
}
|
||||
const flag = destBucketMD.hasDeletedFlag()
|
||||
|| destBucketMD.hasTransientFlag();
|
||||
if (flag) {
|
||||
log.trace('deleted flag or transient flag ' +
|
||||
'on destination bucket', { flag });
|
||||
log.trace('deleted flag or transient flag '
|
||||
+ 'on destination bucket', { flag });
|
||||
return next(errors.NoSuchBucket);
|
||||
}
|
||||
return next(null, destBucketMD);
|
||||
});
|
||||
},
|
||||
function checkSourceAuthorization(destBucketMD, next) {
|
||||
return metadataValidateBucketAndObj(valGetParams, log,
|
||||
return metadataValidateBucketAndObj(valGetParams, request.actionImplicitDenies, log,
|
||||
(err, sourceBucketMD, sourceObjMD) => {
|
||||
if (err) {
|
||||
log.debug('error validating get part of request',
|
||||
|
@ -117,28 +117,26 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
}
|
||||
if (!sourceObjMD) {
|
||||
log.debug('no source object', { sourceObject });
|
||||
const err = reqVersionId ? errors.NoSuchVersion :
|
||||
errors.NoSuchKey;
|
||||
const err = reqVersionId ? errors.NoSuchVersion
|
||||
: errors.NoSuchKey;
|
||||
return next(err, destBucketMD);
|
||||
}
|
||||
let sourceLocationConstraintName =
|
||||
sourceObjMD.dataStoreName;
|
||||
let sourceLocationConstraintName = sourceObjMD.dataStoreName;
|
||||
// for backwards compatibility before storing dataStoreName
|
||||
// TODO: handle in objectMD class
|
||||
if (!sourceLocationConstraintName &&
|
||||
sourceObjMD.location[0] &&
|
||||
sourceObjMD.location[0].dataStoreName) {
|
||||
sourceLocationConstraintName =
|
||||
sourceObjMD.location[0].dataStoreName;
|
||||
if (!sourceLocationConstraintName
|
||||
&& sourceObjMD.location[0]
|
||||
&& sourceObjMD.location[0].dataStoreName) {
|
||||
sourceLocationConstraintName = sourceObjMD.location[0].dataStoreName;
|
||||
}
|
||||
if (sourceObjMD.isDeleteMarker) {
|
||||
log.debug('delete marker on source object',
|
||||
{ sourceObject });
|
||||
{ sourceObject });
|
||||
if (reqVersionId) {
|
||||
const err = errors.InvalidRequest
|
||||
.customizeDescription('The source of a copy ' +
|
||||
'request may not specifically refer to a delete' +
|
||||
'marker by version id.');
|
||||
.customizeDescription('The source of a copy '
|
||||
+ 'request may not specifically refer to a delete'
|
||||
+ 'marker by version id.');
|
||||
return next(err, destBucketMD);
|
||||
}
|
||||
// if user specifies a key in a versioned source bucket
|
||||
|
@ -146,8 +144,7 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
// delete marker, return NoSuchKey
|
||||
return next(errors.NoSuchKey, destBucketMD);
|
||||
}
|
||||
const headerValResult =
|
||||
validateHeaders(request.headers,
|
||||
const headerValResult = validateHeaders(request.headers,
|
||||
sourceObjMD['last-modified'],
|
||||
sourceObjMD['content-md5']);
|
||||
if (headerValResult.error) {
|
||||
|
@ -162,15 +159,15 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
// If specific version requested, include copy source
|
||||
// version id in response. Include in request by default
|
||||
// if versioning is enabled or suspended.
|
||||
if (sourceBucketMD.getVersioningConfiguration() ||
|
||||
reqVersionId) {
|
||||
if (sourceBucketMD.getVersioningConfiguration()
|
||||
|| reqVersionId) {
|
||||
if (sourceObjMD.isNull || !sourceObjMD.versionId) {
|
||||
sourceVerId = 'null';
|
||||
} else {
|
||||
sourceVerId =
|
||||
versionIdUtils.encode(
|
||||
sourceObjMD.versionId,
|
||||
config.versionIdEncodingType);
|
||||
sourceVerId = versionIdUtils.encode(
|
||||
sourceObjMD.versionId,
|
||||
config.versionIdEncodingType,
|
||||
);
|
||||
}
|
||||
}
|
||||
return next(null, copyLocator.dataLocator, destBucketMD,
|
||||
|
@ -195,7 +192,7 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
});
|
||||
return next(err);
|
||||
}
|
||||
let splitter = constants.splitter;
|
||||
let { splitter } = constants;
|
||||
if (mpuBucket.getMdBucketModelVersion() < 2) {
|
||||
splitter = constants.oldSplitter;
|
||||
}
|
||||
|
@ -209,35 +206,33 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
function getMpuOverviewObject(dataLocator, destBucketMD,
|
||||
copyObjectSize, sourceVerId, splitter,
|
||||
sourceLocationConstraintName, next) {
|
||||
const mpuOverviewKey =
|
||||
`overview${splitter}${destObjectKey}${splitter}${uploadId}`;
|
||||
const mpuOverviewKey = `overview${splitter}${destObjectKey}${splitter}${uploadId}`;
|
||||
return metadata.getObjectMD(mpuBucketName, mpuOverviewKey,
|
||||
null, log, (err, res) => {
|
||||
if (err) {
|
||||
// TODO: move to `.is` once BKTCLT-9 is done and bumped in Cloudserver
|
||||
if (err.NoSuchKey) {
|
||||
return next(errors.NoSuchUpload);
|
||||
}
|
||||
log.error('error getting overview object from ' +
|
||||
'mpu bucket', {
|
||||
error: err,
|
||||
method: 'objectPutCopyPart::' +
|
||||
'metadata.getObjectMD',
|
||||
});
|
||||
return next(err);
|
||||
null, log, (err, res) => {
|
||||
if (err) {
|
||||
// TODO: move to `.is` once BKTCLT-9 is done and bumped in Cloudserver
|
||||
if (err.NoSuchKey) {
|
||||
return next(errors.NoSuchUpload);
|
||||
}
|
||||
const initiatorID = res.initiator.ID;
|
||||
const requesterID = authInfo.isRequesterAnIAMUser() ?
|
||||
authInfo.getArn() : authInfo.getCanonicalID();
|
||||
if (initiatorID !== requesterID) {
|
||||
return next(errors.AccessDenied);
|
||||
}
|
||||
const destObjLocationConstraint =
|
||||
res.controllingLocationConstraint;
|
||||
return next(null, dataLocator, destBucketMD,
|
||||
destObjLocationConstraint, copyObjectSize,
|
||||
sourceVerId, sourceLocationConstraintName, splitter);
|
||||
});
|
||||
log.error('error getting overview object from '
|
||||
+ 'mpu bucket', {
|
||||
error: err,
|
||||
method: 'objectPutCopyPart::'
|
||||
+ 'metadata.getObjectMD',
|
||||
});
|
||||
return next(err);
|
||||
}
|
||||
const initiatorID = res.initiator.ID;
|
||||
const requesterID = authInfo.isRequesterAnIAMUser()
|
||||
? authInfo.getArn() : authInfo.getCanonicalID();
|
||||
if (initiatorID !== requesterID) {
|
||||
return next(errors.AccessDenied);
|
||||
}
|
||||
const destObjLocationConstraint = res.controllingLocationConstraint;
|
||||
return next(null, dataLocator, destBucketMD,
|
||||
destObjLocationConstraint, copyObjectSize,
|
||||
sourceVerId, sourceLocationConstraintName, splitter);
|
||||
});
|
||||
},
|
||||
function goGetData(
|
||||
dataLocator,
|
||||
|
@ -249,6 +244,9 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
splitter,
|
||||
next,
|
||||
) {
|
||||
const originalIdentityImpDenies = request.actionImplicitDenies;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
delete request.actionImplicitDenies;
|
||||
data.uploadPartCopy(
|
||||
request,
|
||||
log,
|
||||
|
@ -259,31 +257,33 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
dataStoreContext,
|
||||
locationConstraintCheck,
|
||||
(error, eTag, lastModified, serverSideEncryption, locations) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
request.actionImplicitDenies = originalIdentityImpDenies;
|
||||
if (error) {
|
||||
if (error.message === 'skip') {
|
||||
return next(skipError, destBucketMD, eTag,
|
||||
lastModified, sourceVerId,
|
||||
serverSideEncryption);
|
||||
lastModified, sourceVerId,
|
||||
serverSideEncryption);
|
||||
}
|
||||
return next(error, destBucketMD);
|
||||
}
|
||||
return next(null, destBucketMD, locations, eTag,
|
||||
copyObjectSize, sourceVerId, serverSideEncryption,
|
||||
lastModified, splitter);
|
||||
});
|
||||
copyObjectSize, sourceVerId, serverSideEncryption,
|
||||
lastModified, splitter);
|
||||
},
|
||||
);
|
||||
},
|
||||
function getExistingPartInfo(destBucketMD, locations, totalHash,
|
||||
copyObjectSize, sourceVerId, serverSideEncryption, lastModified,
|
||||
splitter, next) {
|
||||
const partKey =
|
||||
`${uploadId}${constants.splitter}${paddedPartNumber}`;
|
||||
const partKey = `${uploadId}${constants.splitter}${paddedPartNumber}`;
|
||||
metadata.getObjectMD(mpuBucketName, partKey, {}, log,
|
||||
(err, result) => {
|
||||
// If there is nothing being overwritten just move on
|
||||
// TODO: move to `.is` once BKTCLT-9 is done and bumped in Cloudserver
|
||||
if (err && !err.NoSuchKey) {
|
||||
log.debug('error getting current part (if any)',
|
||||
{ error: err });
|
||||
{ error: err });
|
||||
return next(err);
|
||||
}
|
||||
let oldLocations;
|
||||
|
@ -294,8 +294,8 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
// Pull locations to clean up any potential orphans
|
||||
// in data if object put is an overwrite of
|
||||
// already existing object with same key and part number
|
||||
oldLocations = Array.isArray(oldLocations) ?
|
||||
oldLocations : [oldLocations];
|
||||
oldLocations = Array.isArray(oldLocations)
|
||||
? oldLocations : [oldLocations];
|
||||
}
|
||||
return next(null, destBucketMD, locations, totalHash,
|
||||
prevObjectSize, copyObjectSize, sourceVerId,
|
||||
|
@ -317,7 +317,7 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
locations, metaStoreParams, log, err => {
|
||||
if (err) {
|
||||
log.debug('error storing new metadata',
|
||||
{ error: err, method: 'storeNewPartMetadata' });
|
||||
{ error: err, method: 'storeNewPartMetadata' });
|
||||
return next(err);
|
||||
}
|
||||
return next(null, locations, oldLocations, destBucketMD, totalHash,
|
||||
|
@ -370,7 +370,8 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
// data locations) has been stored
|
||||
if (oldLocationsToDelete) {
|
||||
const delLog = logger.newRequestLoggerFromSerializedUids(
|
||||
log.getSerializedUids());
|
||||
log.getSerializedUids(),
|
||||
);
|
||||
return data.batchDelete(oldLocationsToDelete, request.method, null,
|
||||
delLog, err => {
|
||||
if (err) {
|
||||
|
@ -409,11 +410,9 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
|
|||
|
||||
const additionalHeaders = corsHeaders || {};
|
||||
if (serverSideEncryption) {
|
||||
additionalHeaders['x-amz-server-side-encryption'] =
|
||||
serverSideEncryption.algorithm;
|
||||
additionalHeaders['x-amz-server-side-encryption'] = serverSideEncryption.algorithm;
|
||||
if (serverSideEncryption.algorithm === 'aws:kms') {
|
||||
additionalHeaders['x-amz-server-side-encryption-aws-kms-key-id']
|
||||
= serverSideEncryption.masterKeyId;
|
||||
additionalHeaders['x-amz-server-side-encryption-aws-kms-key-id'] = serverSideEncryption.masterKeyId;
|
||||
}
|
||||
}
|
||||
additionalHeaders['x-amz-copy-source-version-id'] = sourceVerId;
|
||||
|
|
|
@ -40,13 +40,14 @@ function objectPutLegalHold(authInfo, request, log, callback) {
|
|||
authInfo,
|
||||
bucketName,
|
||||
objectKey,
|
||||
requestType: 'objectPutLegalHold',
|
||||
versionId,
|
||||
getDeleteMarker: true,
|
||||
requestType: request.apiMethods || 'objectPutLegalHold',
|
||||
request,
|
||||
};
|
||||
|
||||
return async.waterfall([
|
||||
next => metadataValidateBucketAndObj(metadataValParams, log,
|
||||
next => metadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket, objectMD) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed',
|
||||
|
|
|
@ -87,6 +87,7 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
|
|||
const uploadId = request.query.uploadId;
|
||||
const mpuBucketName = `${constants.mpuBucketPrefix}${bucketName}`;
|
||||
const objectKey = request.objectKey;
|
||||
const originalIdentityImpDenies = request.actionImplicitDenies;
|
||||
|
||||
return async.waterfall([
|
||||
// Get the destination bucket.
|
||||
|
@ -109,7 +110,8 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
|
|||
// For validating the request at the destinationBucket level the
|
||||
// `requestType` is the general 'objectPut'.
|
||||
const requestType = 'objectPut';
|
||||
if (!isBucketAuthorized(destinationBucket, requestType, canonicalID, authInfo, log, request)) {
|
||||
if (!isBucketAuthorized(destinationBucket, request.apiMethods || requestType, canonicalID, authInfo,
|
||||
request.actionImplicitDenies, log, request)) {
|
||||
log.debug('access denied for user on bucket', { requestType });
|
||||
return next(errors.AccessDenied, destinationBucket);
|
||||
}
|
||||
|
@ -139,24 +141,24 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
|
|||
// Get the MPU shadow bucket.
|
||||
(destinationBucket, cipherBundle, next) =>
|
||||
metadata.getBucket(mpuBucketName, log,
|
||||
(err, mpuBucket) => {
|
||||
if (err && err.is.NoSuchBucket) {
|
||||
return next(errors.NoSuchUpload, destinationBucket);
|
||||
}
|
||||
if (err) {
|
||||
log.error('error getting the shadow mpu bucket', {
|
||||
error: err,
|
||||
method: 'objectPutPart::metadata.getBucket',
|
||||
});
|
||||
return next(err, destinationBucket);
|
||||
}
|
||||
let splitter = constants.splitter;
|
||||
// BACKWARD: Remove to remove the old splitter
|
||||
if (mpuBucket.getMdBucketModelVersion() < 2) {
|
||||
splitter = constants.oldSplitter;
|
||||
}
|
||||
return next(null, destinationBucket, cipherBundle, splitter);
|
||||
}),
|
||||
(err, mpuBucket) => {
|
||||
if (err && err.is.NoSuchBucket) {
|
||||
return next(errors.NoSuchUpload, destinationBucket);
|
||||
}
|
||||
if (err) {
|
||||
log.error('error getting the shadow mpu bucket', {
|
||||
error: err,
|
||||
method: 'objectPutPart::metadata.getBucket',
|
||||
});
|
||||
return next(err, destinationBucket);
|
||||
}
|
||||
let splitter = constants.splitter;
|
||||
// BACKWARD: Remove to remove the old splitter
|
||||
if (mpuBucket.getMdBucketModelVersion() < 2) {
|
||||
splitter = constants.oldSplitter;
|
||||
}
|
||||
return next(null, destinationBucket, cipherBundle, splitter);
|
||||
}),
|
||||
// Check authorization of the MPU shadow bucket.
|
||||
(destinationBucket, cipherBundle, splitter, next) => {
|
||||
const mpuOverviewKey = _getOverviewKey(splitter, objectKey,
|
||||
|
@ -187,7 +189,7 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
|
|||
// If data backend is backend that handles mpu (like real AWS),
|
||||
// no need to store part info in metadata
|
||||
(destinationBucket, objectLocationConstraint, cipherBundle,
|
||||
splitter, next) => {
|
||||
splitter, next) => {
|
||||
const mpuInfo = {
|
||||
destinationBucket,
|
||||
size,
|
||||
|
@ -196,24 +198,26 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
|
|||
partNumber,
|
||||
bucketName,
|
||||
};
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
delete request.actionImplicitDenies;
|
||||
writeContinue(request, request._response);
|
||||
return data.putPart(request, mpuInfo, streamingV4Params,
|
||||
objectLocationConstraint, locationConstraintCheck, log,
|
||||
(err, partInfo, updatedObjectLC) => {
|
||||
if (err) {
|
||||
return next(err, destinationBucket);
|
||||
}
|
||||
// if data backend handles mpu, skip to end of waterfall
|
||||
if (partInfo && partInfo.dataStoreType === 'aws_s3') {
|
||||
return next(skipError, destinationBucket,
|
||||
partInfo.dataStoreETag);
|
||||
}
|
||||
// partInfo will be null if data backend is not external
|
||||
// if the object location constraint undefined because
|
||||
// mpu was initiated in legacy version, update it
|
||||
return next(null, destinationBucket, updatedObjectLC,
|
||||
cipherBundle, splitter, partInfo);
|
||||
});
|
||||
objectLocationConstraint, locationConstraintCheck, log,
|
||||
(err, partInfo, updatedObjectLC) => {
|
||||
if (err) {
|
||||
return next(err, destinationBucket);
|
||||
}
|
||||
// if data backend handles mpu, skip to end of waterfall
|
||||
if (partInfo && partInfo.dataStoreType === 'aws_s3') {
|
||||
return next(skipError, destinationBucket,
|
||||
partInfo.dataStoreETag);
|
||||
}
|
||||
// partInfo will be null if data backend is not external
|
||||
// if the object location constraint undefined because
|
||||
// mpu was initiated in legacy version, update it
|
||||
return next(null, destinationBucket, updatedObjectLC,
|
||||
cipherBundle, splitter, partInfo);
|
||||
});
|
||||
},
|
||||
// Get any pre-existing part.
|
||||
(destinationBucket, objectLocationConstraint, cipherBundle,
|
||||
|
@ -249,14 +253,14 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
|
|||
},
|
||||
// Store in data backend.
|
||||
(destinationBucket, objectLocationConstraint, cipherBundle,
|
||||
partKey, prevObjectSize, oldLocations, partInfo, splitter, next) => {
|
||||
partKey, prevObjectSize, oldLocations, partInfo, splitter, next) => {
|
||||
// NOTE: set oldLocations to null so we do not batchDelete for now
|
||||
if (partInfo && partInfo.dataStoreType === 'azure') {
|
||||
// skip to storing metadata
|
||||
return next(null, destinationBucket, partInfo,
|
||||
partInfo.dataStoreETag,
|
||||
cipherBundle, partKey, prevObjectSize, null,
|
||||
objectLocationConstraint, splitter);
|
||||
partInfo.dataStoreETag,
|
||||
cipherBundle, partKey, prevObjectSize, null,
|
||||
objectLocationConstraint, splitter);
|
||||
}
|
||||
const objectContext = {
|
||||
bucketName,
|
||||
|
@ -282,7 +286,7 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
|
|||
// Store data locations in metadata and delete any overwritten
|
||||
// data if completeMPU hasn't been initiated yet.
|
||||
(destinationBucket, dataGetInfo, hexDigest, cipherBundle, partKey,
|
||||
prevObjectSize, oldLocations, objectLocationConstraint, splitter, next) => {
|
||||
prevObjectSize, oldLocations, objectLocationConstraint, splitter, next) => {
|
||||
// Use an array to be consistent with objectPutCopyPart where there
|
||||
// could be multiple locations.
|
||||
const partLocations = [dataGetInfo];
|
||||
|
@ -317,7 +321,7 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
|
|||
});
|
||||
},
|
||||
(partLocations, oldLocations, objectLocationConstraint, destinationBucket,
|
||||
hexDigest, prevObjectSize, splitter, next) => {
|
||||
hexDigest, prevObjectSize, splitter, next) => {
|
||||
if (!oldLocations) {
|
||||
return next(null, oldLocations, objectLocationConstraint,
|
||||
destinationBucket, hexDigest, prevObjectSize);
|
||||
|
@ -378,6 +382,8 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
|
|||
], (err, destinationBucket, hexDigest, prevObjectSize) => {
|
||||
const corsHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, destinationBucket);
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
request.actionImplicitDenies = originalIdentityImpDenies;
|
||||
if (err) {
|
||||
if (err === skipError) {
|
||||
return cb(null, hexDigest, corsHeaders);
|
||||
|
|
|
@ -41,45 +41,58 @@ function objectPutRetention(authInfo, request, log, callback) {
|
|||
authInfo,
|
||||
bucketName,
|
||||
objectKey,
|
||||
requestType: 'objectPutRetention',
|
||||
versionId: reqVersionId,
|
||||
getDeleteMarker: true,
|
||||
requestType: request.apiMethods || 'objectPutRetention',
|
||||
request,
|
||||
};
|
||||
|
||||
return async.waterfall([
|
||||
next => metadataValidateBucketAndObj(metadataValParams, log,
|
||||
(err, bucket, objectMD) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed',
|
||||
{ method: 'objectPutRetention', error: err });
|
||||
return next(err);
|
||||
}
|
||||
if (!objectMD) {
|
||||
const err = reqVersionId ? errors.NoSuchVersion :
|
||||
errors.NoSuchKey;
|
||||
log.trace('error no object metadata found',
|
||||
{ method: 'objectPutRetention', error: err });
|
||||
return next(err, bucket);
|
||||
}
|
||||
if (objectMD.isDeleteMarker) {
|
||||
log.trace('version is a delete marker',
|
||||
{ method: 'objectPutRetention' });
|
||||
return next(errors.MethodNotAllowed, bucket);
|
||||
}
|
||||
if (!bucket.isObjectLockEnabled()) {
|
||||
log.trace('object lock not enabled on bucket',
|
||||
{ method: 'objectPutRetention' });
|
||||
return next(errors.InvalidRequest.customizeDescription(
|
||||
'Bucket is missing Object Lock Configuration'
|
||||
), bucket);
|
||||
}
|
||||
return next(null, bucket, objectMD);
|
||||
}),
|
||||
(bucket, objectMD, next) => {
|
||||
next => {
|
||||
log.trace('parsing retention information');
|
||||
parseRetentionXml(request.post, log,
|
||||
(err, retentionInfo) => next(err, bucket, retentionInfo, objectMD));
|
||||
(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.actionImplicitDenies, log,
|
||||
(err, bucket, objectMD) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed',
|
||||
{ method: 'objectPutRetention', error: err });
|
||||
return next(err);
|
||||
}
|
||||
if (!objectMD) {
|
||||
const err = reqVersionId ? errors.NoSuchVersion :
|
||||
errors.NoSuchKey;
|
||||
log.trace('error no object metadata found',
|
||||
{ method: 'objectPutRetention', error: err });
|
||||
return next(err, bucket);
|
||||
}
|
||||
if (objectMD.isDeleteMarker) {
|
||||
log.trace('version is a delete marker',
|
||||
{ method: 'objectPutRetention' });
|
||||
// FIXME we should return a `x-amz-delete-marker: true` header,
|
||||
// see S3C-7592
|
||||
return next(errors.MethodNotAllowed, bucket);
|
||||
}
|
||||
if (!bucket.isObjectLockEnabled()) {
|
||||
log.trace('object lock not enabled on bucket',
|
||||
{ method: 'objectPutRetention' });
|
||||
return next(errors.InvalidRequest.customizeDescription(
|
||||
'Bucket is missing Object Lock Configuration'
|
||||
), bucket);
|
||||
}
|
||||
return next(null, bucket, retentionInfo, objectMD);
|
||||
}),
|
||||
(bucket, retentionInfo, objectMD, next) => {
|
||||
const hasGovernanceBypass = hasGovernanceBypassHeader(request.headers);
|
||||
if (hasGovernanceBypass && authInfo.isRequesterAnIAMUser()) {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
const async = require('async');
|
||||
const { errors, s3middleware } = require('arsenal');
|
||||
|
||||
const { decodeVersionId, getVersionIdResHeader } =
|
||||
require('./apiUtils/object/versioning');
|
||||
const { decodeVersionId, getVersionIdResHeader } = require('./apiUtils/object/versioning');
|
||||
|
||||
const { metadataValidateBucketAndObj } = require('../metadata/metadataUtils');
|
||||
const { pushMetric } = require('../utapi/utilities');
|
||||
|
@ -10,6 +9,7 @@ const getReplicationInfo = require('./apiUtils/object/getReplicationInfo');
|
|||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
const metadata = require('../metadata/wrapper');
|
||||
const { data } = require('../data/wrapper');
|
||||
|
||||
const { parseTagXml } = s3middleware.tagging;
|
||||
const REPLICATION_ACTION = 'PUT_TAGGING';
|
||||
|
||||
|
@ -24,8 +24,8 @@ const REPLICATION_ACTION = 'PUT_TAGGING';
|
|||
function objectPutTagging(authInfo, request, log, callback) {
|
||||
log.debug('processing request', { method: 'objectPutTagging' });
|
||||
|
||||
const bucketName = request.bucketName;
|
||||
const objectKey = request.objectKey;
|
||||
const { bucketName } = request;
|
||||
const { objectKey } = request;
|
||||
|
||||
const decodedVidResult = decodeVersionId(request.query);
|
||||
if (decodedVidResult instanceof Error) {
|
||||
|
@ -41,13 +41,14 @@ function objectPutTagging(authInfo, request, log, callback) {
|
|||
authInfo,
|
||||
bucketName,
|
||||
objectKey,
|
||||
requestType: 'objectPutTagging',
|
||||
versionId: reqVersionId,
|
||||
getDeleteMarker: true,
|
||||
requestType: request.apiMethods || 'objectPutTagging',
|
||||
request,
|
||||
};
|
||||
|
||||
return async.waterfall([
|
||||
next => metadataValidateBucketAndObj(metadataValParams, log,
|
||||
next => metadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
|
||||
(err, bucket, objectMD) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed',
|
||||
|
@ -70,8 +71,7 @@ function objectPutTagging(authInfo, request, log, callback) {
|
|||
}),
|
||||
(bucket, objectMD, next) => {
|
||||
log.trace('parsing tag(s)');
|
||||
parseTagXml(request.post, log, (err, tags) =>
|
||||
next(err, bucket, tags, objectMD));
|
||||
parseTagXml(request.post, log, (err, tags) => next(err, bucket, tags, objectMD));
|
||||
},
|
||||
(bucket, tags, objectMD, next) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
|
@ -88,13 +88,11 @@ function objectPutTagging(authInfo, request, log, callback) {
|
|||
// eslint-disable-next-line no-param-reassign
|
||||
objectMD.originOp = 's3:ObjectTagging:Put';
|
||||
metadata.putObjectMD(bucket.getName(), objectKey, objectMD, params,
|
||||
log, err =>
|
||||
next(err, bucket, objectMD));
|
||||
log, err => next(err, bucket, objectMD));
|
||||
},
|
||||
(bucket, objectMD, next) =>
|
||||
// if external backend handles tagging
|
||||
data.objectTagging('Put', objectKey, bucket, objectMD,
|
||||
log, err => next(err, bucket, objectMD)),
|
||||
// if external backend handles tagging
|
||||
(bucket, objectMD, next) => data.objectTagging('Put', objectKey, bucket, objectMD,
|
||||
log, err => next(err, bucket, objectMD)),
|
||||
], (err, bucket, objectMD) => {
|
||||
const additionalResHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
|
@ -110,8 +108,7 @@ function objectPutTagging(authInfo, request, log, callback) {
|
|||
location: objectMD ? objectMD.dataStoreName : undefined,
|
||||
});
|
||||
const verCfg = bucket.getVersioningConfiguration();
|
||||
additionalResHeaders['x-amz-version-id'] =
|
||||
getVersionIdResHeader(verCfg, objectMD);
|
||||
additionalResHeaders['x-amz-version-id'] = getVersionIdResHeader(verCfg, objectMD);
|
||||
}
|
||||
return callback(err, additionalResHeaders);
|
||||
});
|
||||
|
|
|
@ -21,12 +21,13 @@ const { pushMetric } = require('../utapi/utilities');
|
|||
* @param {string} objectKey - object key from request (or as translated in
|
||||
* websiteGet)
|
||||
* @param {object} corsHeaders - CORS-related response headers
|
||||
* @param {object} request - normalized request object
|
||||
* @param {object} log - Werelogs instance
|
||||
* @param {function} callback - callback to function in route
|
||||
* @return {undefined}
|
||||
*/
|
||||
function _errorActions(err, errorDocument, routingRules,
|
||||
bucket, objectKey, corsHeaders, log, callback) {
|
||||
bucket, objectKey, corsHeaders, request, log, callback) {
|
||||
const bucketName = bucket.getName();
|
||||
const errRoutingRule = findRoutingRule(routingRules,
|
||||
objectKey, err.code);
|
||||
|
@ -46,8 +47,8 @@ function _errorActions(err, errorDocument, routingRules,
|
|||
}
|
||||
// return the default error message if the object is private
|
||||
// rather than sending a stored error file
|
||||
if (!isObjAuthorized(bucket, errObjMD, 'objectGet',
|
||||
constants.publicId, null, log)) {
|
||||
if (!isObjAuthorized(bucket, errObjMD, request.apiMethods || 'objectGet',
|
||||
constants.publicId, null, request.actionImplicitDenies, log)) {
|
||||
log.trace('errorObj not authorized', { error: err });
|
||||
return callback(err, true, null, corsHeaders);
|
||||
}
|
||||
|
@ -143,8 +144,8 @@ function websiteGet(request, log, callback) {
|
|||
log.trace('error retrieving object metadata',
|
||||
{ error: err });
|
||||
let returnErr = err;
|
||||
const bucketAuthorized = isBucketAuthorized(bucket,
|
||||
'bucketGet', constants.publicId, null, log, request);
|
||||
const bucketAuthorized = isBucketAuthorized(bucket, request.apiMethods || 'bucketGet',
|
||||
constants.publicId, null, request.actionImplicitDenies, log, request);
|
||||
// if index object does not exist and bucket is private AWS
|
||||
// returns 403 - AccessDenied error.
|
||||
if (err.is.NoSuchKey && !bucketAuthorized) {
|
||||
|
@ -152,16 +153,16 @@ function websiteGet(request, log, callback) {
|
|||
}
|
||||
return _errorActions(returnErr,
|
||||
websiteConfig.getErrorDocument(), routingRules,
|
||||
bucket, reqObjectKey, corsHeaders, log,
|
||||
bucket, reqObjectKey, corsHeaders, request, log,
|
||||
callback);
|
||||
}
|
||||
if (!isObjAuthorized(bucket, objMD, 'objectGet',
|
||||
constants.publicId, null, log, request)) {
|
||||
if (!isObjAuthorized(bucket, objMD, request.apiMethods || 'objectGet',
|
||||
constants.publicId, null, request.actionImplicitDenies, log, request)) {
|
||||
const err = errors.AccessDenied;
|
||||
log.trace('request not authorized', { error: err });
|
||||
return _errorActions(err, websiteConfig.getErrorDocument(),
|
||||
routingRules, bucket,
|
||||
reqObjectKey, corsHeaders, log, callback);
|
||||
reqObjectKey, corsHeaders, request, log, callback);
|
||||
}
|
||||
|
||||
const headerValResult = validateHeaders(request.headers,
|
||||
|
@ -171,7 +172,7 @@ function websiteGet(request, log, callback) {
|
|||
log.trace('header validation error', { error: err });
|
||||
return _errorActions(err, websiteConfig.getErrorDocument(),
|
||||
routingRules, bucket, reqObjectKey,
|
||||
corsHeaders, log, callback);
|
||||
corsHeaders, request, log, callback);
|
||||
}
|
||||
// check if object to serve has website redirect header
|
||||
// Note: AWS prioritizes website configuration rules over
|
||||
|
|
|
@ -103,8 +103,8 @@ function websiteHead(request, log, callback) {
|
|||
log.trace('error retrieving object metadata',
|
||||
{ error: err });
|
||||
let returnErr = err;
|
||||
const bucketAuthorized = isBucketAuthorized(bucket,
|
||||
'bucketGet', constants.publicId, null, log, request);
|
||||
const bucketAuthorized = isBucketAuthorized(bucket, request.apiMethods || 'bucketGet',
|
||||
constants.publicId, null, request.actionImplicitDenies, log, request);
|
||||
// if index object does not exist and bucket is private AWS
|
||||
// returns 403 - AccessDenied error.
|
||||
if (err.is.NoSuchKey && !bucketAuthorized) {
|
||||
|
@ -113,8 +113,8 @@ function websiteHead(request, log, callback) {
|
|||
return _errorActions(returnErr, routingRules,
|
||||
reqObjectKey, corsHeaders, log, callback);
|
||||
}
|
||||
if (!isObjAuthorized(bucket, objMD, 'objectGet',
|
||||
constants.publicId, null, log, request)) {
|
||||
if (!isObjAuthorized(bucket, objMD, request.apiMethods || 'objectGet',
|
||||
constants.publicId, null, request.actionImplicitDenies, log, request)) {
|
||||
const err = errors.AccessDenied;
|
||||
log.trace('request not authorized', { error: err });
|
||||
return _errorActions(err, routingRules, reqObjectKey,
|
||||
|
|
|
@ -42,7 +42,6 @@ function getNullVersion(objMD, bucketName, objectKey, log, cb) {
|
|||
* NOTE: If the value of `versionId` param is 'null', this function returns the
|
||||
* master version objMD. The null version object md must be retrieved in a
|
||||
* separate step using the master object md: see getNullVersion().
|
||||
* @param {string} requestType - type of request
|
||||
* @param {string} bucketName - name of bucket
|
||||
* @param {string} objectKey - name of object key
|
||||
* @param {string} [versionId] - version of object to retrieve
|
||||
|
@ -50,7 +49,7 @@ function getNullVersion(objMD, bucketName, objectKey, log, cb) {
|
|||
* @param {function} cb - callback
|
||||
* @return {undefined} - and call callback with err, bucket md and object md
|
||||
*/
|
||||
function metadataGetBucketAndObject(requestType, bucketName, objectKey,
|
||||
function metadataGetBucketAndObject(bucketName, objectKey,
|
||||
versionId, log, cb) {
|
||||
const options = {
|
||||
// if attempting to get 'null' version, must retrieve null version id
|
||||
|
@ -73,13 +72,6 @@ function metadataGetBucketAndObject(requestType, bucketName, objectKey,
|
|||
});
|
||||
return cb(errors.NoSuchBucket);
|
||||
}
|
||||
if (bucketShield(bucket, requestType)) {
|
||||
log.debug('bucket is shielded from request', {
|
||||
requestType,
|
||||
method: 'metadataGetBucketAndObject',
|
||||
});
|
||||
return cb(errors.NoSuchBucket);
|
||||
}
|
||||
log.trace('found bucket in metadata');
|
||||
return cb(null, bucket, obj);
|
||||
});
|
||||
|
@ -118,6 +110,54 @@ function metadataGetObject(bucketName, objectKey, versionId, log, cb) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that a bucket is accessible and authorized to the user,
|
||||
* return a specific error code otherwise
|
||||
*
|
||||
* @param {BucketInfo} bucket - bucket info
|
||||
* @param {object} params - function parameters
|
||||
* @param {AuthInfo} params.authInfo - AuthInfo class instance, requester's info
|
||||
* @param {string} params.requestType - type of request
|
||||
* @param {string} [params.preciseRequestType] - precise type of request
|
||||
* @param {object} params.request - http request object
|
||||
* @param {object} actionImplicitDenies - authorization results showing if an action is implictly denied
|
||||
* @param {RequestLogger} log - request logger
|
||||
* @return {ArsenalError|null} returns a validation error, or null if validation OK
|
||||
* The following errors may be returned:
|
||||
* - NoSuchBucket: bucket is shielded
|
||||
* - MethodNotAllowed: requester is not bucket owner and asking for a
|
||||
* bucket policy operation
|
||||
* - AccessDenied: bucket is not authorized
|
||||
*/
|
||||
function validateBucket(bucket, params, actionImplicitDenies, log) {
|
||||
const { authInfo, preciseRequestType, request } = params;
|
||||
let requestType = params.requestType;
|
||||
if (bucketShield(bucket, requestType)) {
|
||||
log.debug('bucket is shielded from request', {
|
||||
requestType,
|
||||
method: 'validateBucket',
|
||||
});
|
||||
return errors.NoSuchBucket;
|
||||
}
|
||||
// if requester is not bucket owner, bucket policy actions should be denied with
|
||||
// MethodNotAllowed error
|
||||
const onlyOwnerAllowed = ['bucketDeletePolicy', 'bucketGetPolicy', 'bucketPutPolicy'];
|
||||
const canonicalID = authInfo.getCanonicalID();
|
||||
if (!Array.isArray(requestType)) {
|
||||
requestType = [requestType];
|
||||
}
|
||||
if (bucket.getOwner() !== canonicalID && requestType.some(type => onlyOwnerAllowed.includes(type))) {
|
||||
return errors.MethodNotAllowed;
|
||||
}
|
||||
if (!isBucketAuthorized(bucket, (preciseRequestType || requestType), canonicalID,
|
||||
authInfo, actionImplicitDenies, log, request)) {
|
||||
log.debug('access denied for user on bucket', { requestType });
|
||||
return errors.AccessDenied;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/** metadataValidateBucketAndObj - retrieve bucket and object md from metadata
|
||||
* and check if user is authorized to access them.
|
||||
* @param {object} params - function parameters
|
||||
|
@ -127,41 +167,60 @@ function metadataGetObject(bucketName, objectKey, versionId, log, cb) {
|
|||
* @param {string} [params.versionId] - version id if getting specific version
|
||||
* @param {string} params.requestType - type of request
|
||||
* @param {object} params.request - http request object
|
||||
* @param {boolean} actionImplicitDenies - identity authorization results
|
||||
* @param {RequestLogger} log - request logger
|
||||
* @param {function} callback - callback
|
||||
* @return {undefined} - and call callback with params err, bucket md
|
||||
*/
|
||||
function metadataValidateBucketAndObj(params, log, callback) {
|
||||
const { authInfo, bucketName, objectKey, versionId, requestType, preciseRequestType, request } = params;
|
||||
const canonicalID = authInfo.getCanonicalID();
|
||||
function metadataValidateBucketAndObj(params, actionImplicitDenies, log, callback) {
|
||||
const { authInfo, bucketName, objectKey, versionId, getDeleteMarker, request } = params;
|
||||
let requestType = params.requestType;
|
||||
if (!Array.isArray(requestType)) {
|
||||
requestType = [requestType];
|
||||
}
|
||||
async.waterfall([
|
||||
function getBucketAndObjectMD(next) {
|
||||
return metadataGetBucketAndObject(requestType, bucketName,
|
||||
objectKey, versionId, log, next);
|
||||
},
|
||||
function checkBucketAuth(bucket, objMD, next) {
|
||||
// if requester is not bucket owner, bucket policy actions should be denied with
|
||||
// MethodNotAllowed error
|
||||
const onlyOwnerAllowed = ['bucketDeletePolicy', 'bucketGetPolicy', 'bucketPutPolicy'];
|
||||
if (bucket.getOwner() !== canonicalID && onlyOwnerAllowed.includes(requestType)) {
|
||||
return next(errors.MethodNotAllowed, bucket);
|
||||
next => {
|
||||
// versionId may be 'null', which asks metadata to fetch the null key specifically
|
||||
const getOptions = { versionId };
|
||||
if (getDeleteMarker) {
|
||||
getOptions.getDeleteMarker = true;
|
||||
}
|
||||
if (!isBucketAuthorized(bucket, (preciseRequestType || requestType), canonicalID,
|
||||
authInfo, log, request)) {
|
||||
log.debug('access denied for user on bucket', { requestType });
|
||||
return next(errors.AccessDenied, bucket);
|
||||
}
|
||||
return next(null, bucket, objMD);
|
||||
return metadata.getBucketAndObjectMD(bucketName, objectKey, getOptions, log, (err, getResult) => {
|
||||
if (err) {
|
||||
// if some implicit actionImplicitDenies, return AccessDenied
|
||||
// before leaking any state information
|
||||
if (actionImplicitDenies && Object.values(actionImplicitDenies).some(v => v === true)) {
|
||||
return next(errors.AccessDenied);
|
||||
}
|
||||
return next(err);
|
||||
}
|
||||
return next(null, getResult);
|
||||
});
|
||||
},
|
||||
function handleNullVersionGet(bucket, objMD, next) {
|
||||
(getResult, next) => {
|
||||
const bucket = getResult.bucket ?
|
||||
BucketInfo.deSerialize(getResult.bucket) : undefined;
|
||||
if (!bucket) {
|
||||
log.debug('bucketAttrs is undefined', {
|
||||
bucket: bucketName,
|
||||
method: 'metadataValidateBucketAndObj',
|
||||
});
|
||||
return next(errors.NoSuchBucket);
|
||||
}
|
||||
const validationError = validateBucket(bucket, params, actionImplicitDenies, log);
|
||||
if (validationError) {
|
||||
return next(validationError, bucket);
|
||||
}
|
||||
if (objMD && versionId === 'null') {
|
||||
return getNullVersion(objMD, bucketName, objectKey, log,
|
||||
(err, nullVer) => next(err, bucket, nullVer));
|
||||
}
|
||||
return next(null, bucket, objMD);
|
||||
},
|
||||
function checkObjectAuth(bucket, objMD, next) {
|
||||
if (!isObjAuthorized(bucket, objMD, requestType, canonicalID, authInfo, log, request)) {
|
||||
(bucket, objMD, next) => {
|
||||
const canonicalID = authInfo.getCanonicalID();
|
||||
if (!isObjAuthorized(bucket, objMD, requestType, canonicalID, authInfo, actionImplicitDenies,
|
||||
log, request)) {
|
||||
log.debug('access denied for user on object', { requestType });
|
||||
return next(errors.AccessDenied, bucket);
|
||||
}
|
||||
|
@ -209,34 +268,25 @@ function metadataGetBucket(requestType, bucketName, log, cb) {
|
|||
* @param {string} params.bucketName - name of bucket
|
||||
* @param {string} params.requestType - type of request
|
||||
* @param {string} params.request - http request object
|
||||
* @param {boolean} actionImplicitDenies - identity authorization results
|
||||
* @param {RequestLogger} log - request logger
|
||||
* @param {function} callback - callback
|
||||
* @return {undefined} - and call callback with params err, bucket md
|
||||
*/
|
||||
function metadataValidateBucket(params, log, callback) {
|
||||
const { authInfo, bucketName, requestType, preciseRequestType, request } = params;
|
||||
const canonicalID = authInfo.getCanonicalID();
|
||||
return metadataGetBucket(requestType, bucketName, log, (err, bucket) => {
|
||||
function metadataValidateBucket(params, actionImplicitDenies, log, callback) {
|
||||
const { bucketName } = params;
|
||||
return metadata.getBucket(bucketName, log, (err, bucket) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
// if requester is not bucket owner, bucket policy actions should be denied with
|
||||
// MethodNotAllowed error
|
||||
const onlyOwnerAllowed = ['bucketDeletePolicy', 'bucketGetPolicy', 'bucketPutPolicy'];
|
||||
if (bucket.getOwner() !== canonicalID && onlyOwnerAllowed.includes(requestType)) {
|
||||
return callback(errors.MethodNotAllowed, bucket);
|
||||
}
|
||||
// still return bucket for cors headers
|
||||
if (!isBucketAuthorized(bucket, (preciseRequestType || requestType), canonicalID, authInfo, log, request)) {
|
||||
log.debug('access denied for user on bucket', { requestType });
|
||||
return callback(errors.AccessDenied, bucket);
|
||||
}
|
||||
return callback(null, bucket);
|
||||
const validationError = validateBucket(bucket, params, actionImplicitDenies, log);
|
||||
return callback(validationError, bucket);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
metadataGetObject,
|
||||
validateBucket,
|
||||
metadataValidateBucketAndObj,
|
||||
metadataValidateBucket,
|
||||
};
|
||||
|
|
|
@ -1149,6 +1149,8 @@ function routeBackbeat(clientIP, request, response, log) {
|
|||
// Attach the apiMethod method to the request, so it can used by monitoring in the server
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
request.apiMethod = 'routeBackbeat';
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
request.actionImplicitDenies = false;
|
||||
|
||||
log.debug('routing request', {
|
||||
method: 'routeBackbeat',
|
||||
|
@ -1270,10 +1272,10 @@ function routeBackbeat(clientIP, request, response, log) {
|
|||
objectKey: request.objectKey,
|
||||
authInfo: userInfo,
|
||||
versionId,
|
||||
requestType: 'ReplicateObject',
|
||||
requestType: request.apiMethods || 'ReplicateObject',
|
||||
request,
|
||||
};
|
||||
return metadataValidateBucketAndObj(mdValParams, log, next);
|
||||
return metadataValidateBucketAndObj(mdValParams, request.actionImplicitDenies, log, next);
|
||||
},
|
||||
(bucketInfo, objMd, next) => {
|
||||
if (useMultipleBackend) {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"homepage": "https://github.com/scality/S3#readme",
|
||||
"dependencies": {
|
||||
"@hapi/joi": "^17.1.0",
|
||||
"arsenal": "git+https://github.com/scality/arsenal#7.10.47",
|
||||
"arsenal": "git+https://github.com/scality/arsenal#1d74f512c86ca34ee7e662acdc213f18c86326b0",
|
||||
"async": "~2.5.0",
|
||||
"aws-sdk": "2.905.0",
|
||||
"azure-storage": "^2.1.0",
|
||||
|
|
|
@ -175,7 +175,6 @@ describe('KMIP backed server-side encryption', () => {
|
|||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow creating mpu with SSE header ' +
|
||||
'in encrypted bucket', done => {
|
||||
async.waterfall([
|
||||
|
|
Binary file not shown.
|
@ -54,6 +54,7 @@ const bucketPutRequest = {
|
|||
url: '/',
|
||||
post: '',
|
||||
parsedHost: 'localhost',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
const awsETag = 'be747eb4b75517bf6b3cf7c5fbb62f3a';
|
||||
|
@ -73,6 +74,7 @@ const completeBody = '<CompleteMultipartUpload>' +
|
|||
const basicParams = {
|
||||
bucketName,
|
||||
namespace,
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
function getObjectGetRequest(objectKey) {
|
||||
|
@ -270,6 +272,7 @@ function mpuSetup(location, key, cb) {
|
|||
'x-amz-meta-scal-location-constraint': location },
|
||||
url: `/${key}?uploads`,
|
||||
parsedHost: 'localhost',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
initiateMultipartUpload(authInfo, initiateRequest, log,
|
||||
(err, result) => {
|
||||
|
@ -317,7 +320,6 @@ function abortMultipleMpus(backendsInfo, callback) {
|
|||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
describe('Multipart Upload API with AWS Backend', function mpuTestSuite() {
|
||||
this.timeout(60000);
|
||||
|
||||
|
@ -342,6 +344,7 @@ describe('Multipart Upload API with AWS Backend', function mpuTestSuite() {
|
|||
'x-amz-meta-scal-location-constraint': `${awsLocation}` },
|
||||
url: `/${objectKey}?uploads`,
|
||||
parsedHost: 'localhost',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
initiateMultipartUpload(authInfo, initiateRequest, log,
|
||||
|
@ -365,6 +368,7 @@ describe('Multipart Upload API with AWS Backend', function mpuTestSuite() {
|
|||
`${awsLocationMismatch}` },
|
||||
url: `/${objectKey}?uploads`,
|
||||
parsedHost: 'localhost',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
initiateMultipartUpload(authInfo, initiateRequest, log,
|
||||
|
@ -389,6 +393,7 @@ describe('Multipart Upload API with AWS Backend', function mpuTestSuite() {
|
|||
},
|
||||
url: `/${objectKey}?uploads`,
|
||||
parsedHost: 'localhost',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
initiateMultipartUpload(authInfo, initiateRequest, log,
|
||||
|
@ -612,6 +617,7 @@ describe('Multipart Upload API with AWS Backend', function mpuTestSuite() {
|
|||
'x-amz-meta-scal-location-constraint': awsLocation },
|
||||
url: `/${objectKey}?uploads`,
|
||||
parsedHost: 'localhost',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
initiateMultipartUpload(authInfo, initiateRequest, log,
|
||||
err => {
|
||||
|
@ -712,6 +718,7 @@ describe('Multipart Upload API with AWS Backend', function mpuTestSuite() {
|
|||
headers: { host: '/' },
|
||||
url: `/${bucketName}?uploads`,
|
||||
query: {},
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
listMultipartUploads(authInfo, listMpuParams, log,
|
||||
(err, mpuListXml) => {
|
||||
|
|
|
@ -71,7 +71,6 @@ function copySetup(params, cb) {
|
|||
callback),
|
||||
], err => cb(err));
|
||||
}
|
||||
|
||||
describe('ObjectCopy API with multiple backends', () => {
|
||||
before(() => {
|
||||
cleanup();
|
||||
|
|
|
@ -3,21 +3,19 @@ const async = require('async');
|
|||
const { parseString } = require('xml2js');
|
||||
const AWS = require('aws-sdk');
|
||||
|
||||
const { cleanup, DummyRequestLogger, makeAuthInfo }
|
||||
= require('../unit/helpers');
|
||||
const { metadata } = require('arsenal').storage.metadata.inMemory.metadata;
|
||||
const { cleanup, DummyRequestLogger, makeAuthInfo } = require('../unit/helpers');
|
||||
const { ds } = require('arsenal').storage.data.inMemory.datastore;
|
||||
const { bucketPut } = require('../../lib/api/bucketPut');
|
||||
const initiateMultipartUpload
|
||||
= require('../../lib/api/initiateMultipartUpload');
|
||||
const initiateMultipartUpload = require('../../lib/api/initiateMultipartUpload');
|
||||
const objectPut = require('../../lib/api/objectPut');
|
||||
const objectPutCopyPart = require('../../lib/api/objectPutCopyPart');
|
||||
const DummyRequest = require('../unit/DummyRequest');
|
||||
const { metadata } = require('arsenal').storage.metadata.inMemory.metadata;
|
||||
const constants = require('../../constants');
|
||||
|
||||
const s3 = new AWS.S3();
|
||||
|
||||
const splitter = constants.splitter;
|
||||
const { splitter } = constants;
|
||||
const log = new DummyRequestLogger();
|
||||
const canonicalID = 'accessKey1';
|
||||
const authInfo = makeAuthInfo(canonicalID);
|
||||
|
@ -56,14 +54,14 @@ function getAwsParamsBucketMismatch(destObjName, uploadId) {
|
|||
}
|
||||
|
||||
function copyPutPart(bucketLoc, mpuLoc, srcObjLoc, requestHost, cb,
|
||||
errorPutCopyPart) {
|
||||
errorPutCopyPart) {
|
||||
const keys = getSourceAndDestKeys();
|
||||
const { sourceObjName, destObjName } = keys;
|
||||
const post = bucketLoc ? '<?xml version="1.0" encoding="UTF-8"?>' +
|
||||
'<CreateBucketConfiguration ' +
|
||||
'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
`<LocationConstraint>${bucketLoc}</LocationConstraint>` +
|
||||
'</CreateBucketConfiguration>' : '';
|
||||
const post = bucketLoc ? '<?xml version="1.0" encoding="UTF-8"?>'
|
||||
+ '<CreateBucketConfiguration '
|
||||
+ 'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">'
|
||||
+ `<LocationConstraint>${bucketLoc}</LocationConstraint>`
|
||||
+ '</CreateBucketConfiguration>' : '';
|
||||
const bucketPutReq = new DummyRequest({
|
||||
bucketName,
|
||||
namespace,
|
||||
|
@ -80,10 +78,13 @@ errorPutCopyPart) {
|
|||
objectKey: destObjName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: `/${destObjName}?uploads`,
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
if (mpuLoc) {
|
||||
initiateReq.headers = { 'host': `${bucketName}.s3.amazonaws.com`,
|
||||
'x-amz-meta-scal-location-constraint': `${mpuLoc}` };
|
||||
initiateReq.headers = {
|
||||
'host': `${bucketName}.s3.amazonaws.com`,
|
||||
'x-amz-meta-scal-location-constraint': `${mpuLoc}`,
|
||||
};
|
||||
}
|
||||
if (requestHost) {
|
||||
initiateReq.parsedHost = requestHost;
|
||||
|
@ -94,10 +95,13 @@ errorPutCopyPart) {
|
|||
objectKey: sourceObjName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
if (srcObjLoc) {
|
||||
sourceObjPutParams.headers = { 'host': `${bucketName}.s3.amazonaws.com`,
|
||||
'x-amz-meta-scal-location-constraint': `${srcObjLoc}` };
|
||||
sourceObjPutParams.headers = {
|
||||
'host': `${bucketName}.s3.amazonaws.com`,
|
||||
'x-amz-meta-scal-location-constraint': `${srcObjLoc}`,
|
||||
};
|
||||
}
|
||||
const sourceObjPutReq = new DummyRequest(sourceObjPutParams, body);
|
||||
if (requestHost) {
|
||||
|
@ -112,8 +116,7 @@ errorPutCopyPart) {
|
|||
});
|
||||
},
|
||||
next => {
|
||||
objectPut(authInfo, sourceObjPutReq, undefined, log, err =>
|
||||
next(err));
|
||||
objectPut(authInfo, sourceObjPutReq, undefined, log, err => next(err));
|
||||
},
|
||||
next => {
|
||||
initiateMultipartUpload(authInfo, initiateReq, log, next);
|
||||
|
@ -130,8 +133,8 @@ errorPutCopyPart) {
|
|||
// Need to build request in here since do not have
|
||||
// uploadId until here
|
||||
assert.ifError(err, 'Error putting source object or initiate MPU');
|
||||
const testUploadId = json.InitiateMultipartUploadResult.
|
||||
UploadId[0];
|
||||
const testUploadId = json.InitiateMultipartUploadResult
|
||||
.UploadId[0];
|
||||
const copyPartParams = {
|
||||
bucketName,
|
||||
namespace,
|
||||
|
@ -172,137 +175,137 @@ function assertPartList(partList, uploadId) {
|
|||
}
|
||||
|
||||
describeSkipIfE2E('ObjectCopyPutPart API with multiple backends',
|
||||
function testSuite() {
|
||||
this.timeout(60000);
|
||||
function testSuite() {
|
||||
this.timeout(60000);
|
||||
|
||||
beforeEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
beforeEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('should copy part to mem based on mpu location', done => {
|
||||
copyPutPart(fileLocation, memLocation, null, 'localhost', () => {
|
||||
it('should copy part to mem based on mpu location', done => {
|
||||
copyPutPart(fileLocation, memLocation, null, 'localhost', () => {
|
||||
// object info is stored in ds beginning at index one,
|
||||
// so an array length of two means only one object
|
||||
// was stored in mem
|
||||
assert.strictEqual(ds.length, 2);
|
||||
assert.deepStrictEqual(ds[1].value, body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy part to file based on mpu location', done => {
|
||||
copyPutPart(memLocation, fileLocation, null, 'localhost', () => {
|
||||
assert.strictEqual(ds.length, 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy part to AWS based on mpu location', done => {
|
||||
copyPutPart(memLocation, awsLocation, null, 'localhost',
|
||||
(keys, uploadId) => {
|
||||
assert.strictEqual(ds.length, 2);
|
||||
const awsReq = getAwsParams(keys.destObjName, uploadId);
|
||||
s3.listParts(awsReq, (err, partList) => {
|
||||
assertPartList(partList, uploadId);
|
||||
s3.abortMultipartUpload(awsReq, err => {
|
||||
assert.equal(err, null, `Error aborting MPU: ${err}. ` +
|
||||
`You must abort MPU with upload ID ${uploadId} manually.`);
|
||||
done();
|
||||
});
|
||||
assert.strictEqual(ds.length, 2);
|
||||
assert.deepStrictEqual(ds[1].value, body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy part to mem from AWS based on mpu location', done => {
|
||||
copyPutPart(awsLocation, memLocation, null, 'localhost', () => {
|
||||
assert.strictEqual(ds.length, 2);
|
||||
assert.deepStrictEqual(ds[1].value, body);
|
||||
done();
|
||||
it('should copy part to file based on mpu location', done => {
|
||||
copyPutPart(memLocation, fileLocation, null, 'localhost', () => {
|
||||
assert.strictEqual(ds.length, 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy part to mem based on bucket location', done => {
|
||||
copyPutPart(memLocation, null, null, 'localhost', () => {
|
||||
it('should copy part to AWS based on mpu location', done => {
|
||||
copyPutPart(memLocation, awsLocation, null, 'localhost',
|
||||
(keys, uploadId) => {
|
||||
assert.strictEqual(ds.length, 2);
|
||||
const awsReq = getAwsParams(keys.destObjName, uploadId);
|
||||
s3.listParts(awsReq, (err, partList) => {
|
||||
assertPartList(partList, uploadId);
|
||||
s3.abortMultipartUpload(awsReq, err => {
|
||||
assert.equal(err, null, `Error aborting MPU: ${err}. `
|
||||
+ `You must abort MPU with upload ID ${uploadId} manually.`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy part to mem from AWS based on mpu location', done => {
|
||||
copyPutPart(awsLocation, memLocation, null, 'localhost', () => {
|
||||
assert.strictEqual(ds.length, 2);
|
||||
assert.deepStrictEqual(ds[1].value, body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy part to mem based on bucket location', done => {
|
||||
copyPutPart(memLocation, null, null, 'localhost', () => {
|
||||
// ds length should be three because both source
|
||||
// and copied objects should be in mem
|
||||
assert.strictEqual(ds.length, 3);
|
||||
assert.deepStrictEqual(ds[2].value, body);
|
||||
done();
|
||||
assert.strictEqual(ds.length, 3);
|
||||
assert.deepStrictEqual(ds[2].value, body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy part to file based on bucket location', done => {
|
||||
copyPutPart(fileLocation, null, null, 'localhost', () => {
|
||||
it('should copy part to file based on bucket location', done => {
|
||||
copyPutPart(fileLocation, null, null, 'localhost', () => {
|
||||
// ds should be empty because both source and
|
||||
// coped objects should be in file
|
||||
assert.deepStrictEqual(ds, []);
|
||||
done();
|
||||
assert.deepStrictEqual(ds, []);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy part to AWS based on bucket location', done => {
|
||||
copyPutPart(awsLocation, null, null, 'localhost', (keys, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
const awsReq = getAwsParams(keys.destObjName, uploadId);
|
||||
s3.listParts(awsReq, (err, partList) => {
|
||||
assertPartList(partList, uploadId);
|
||||
s3.abortMultipartUpload(awsReq, err => {
|
||||
assert.equal(err, null, `Error aborting MPU: ${err}. ` +
|
||||
`You must abort MPU with upload ID ${uploadId} manually.`);
|
||||
done();
|
||||
it('should copy part to AWS based on bucket location', done => {
|
||||
copyPutPart(awsLocation, null, null, 'localhost', (keys, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
const awsReq = getAwsParams(keys.destObjName, uploadId);
|
||||
s3.listParts(awsReq, (err, partList) => {
|
||||
assertPartList(partList, uploadId);
|
||||
s3.abortMultipartUpload(awsReq, err => {
|
||||
assert.equal(err, null, `Error aborting MPU: ${err}. `
|
||||
+ `You must abort MPU with upload ID ${uploadId} manually.`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy part an object on AWS location that has bucketMatch ' +
|
||||
'equals false to a mpu with a different AWS location', done => {
|
||||
copyPutPart(null, awsLocation, awsLocationMismatch, 'localhost',
|
||||
(keys, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
const awsReq = getAwsParams(keys.destObjName, uploadId);
|
||||
s3.listParts(awsReq, (err, partList) => {
|
||||
assertPartList(partList, uploadId);
|
||||
s3.abortMultipartUpload(awsReq, err => {
|
||||
assert.equal(err, null, `Error aborting MPU: ${err}. ` +
|
||||
`You must abort MPU with upload ID ${uploadId} manually.`);
|
||||
done();
|
||||
it('should copy part an object on AWS location that has bucketMatch '
|
||||
+ 'equals false to a mpu with a different AWS location', done => {
|
||||
copyPutPart(null, awsLocation, awsLocationMismatch, 'localhost',
|
||||
(keys, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
const awsReq = getAwsParams(keys.destObjName, uploadId);
|
||||
s3.listParts(awsReq, (err, partList) => {
|
||||
assertPartList(partList, uploadId);
|
||||
s3.abortMultipartUpload(awsReq, err => {
|
||||
assert.equal(err, null, `Error aborting MPU: ${err}. `
|
||||
+ `You must abort MPU with upload ID ${uploadId} manually.`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy part an object on AWS to a mpu with a different '
|
||||
+ 'AWS location that has bucketMatch equals false', done => {
|
||||
copyPutPart(null, awsLocationMismatch, awsLocation, 'localhost',
|
||||
(keys, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
const awsReq = getAwsParamsBucketMismatch(keys.destObjName,
|
||||
uploadId);
|
||||
s3.listParts(awsReq, (err, partList) => {
|
||||
assertPartList(partList, uploadId);
|
||||
s3.abortMultipartUpload(awsReq, err => {
|
||||
assert.equal(err, null, `Error aborting MPU: ${err}. `
|
||||
+ `You must abort MPU with upload ID ${uploadId} manually.`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return error 403 AccessDenied copying part to a '
|
||||
+ 'different AWS location without object READ access',
|
||||
done => {
|
||||
const errorPutCopyPart = { code: 'AccessDenied', statusCode: 403 };
|
||||
copyPutPart(null, awsLocation, awsLocation2, 'localhost', done,
|
||||
errorPutCopyPart);
|
||||
});
|
||||
|
||||
|
||||
it('should copy part to file based on request endpoint', done => {
|
||||
copyPutPart(null, null, memLocation, 'localhost', () => {
|
||||
assert.strictEqual(ds.length, 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy part an object on AWS to a mpu with a different ' +
|
||||
'AWS location that has bucketMatch equals false', done => {
|
||||
copyPutPart(null, awsLocationMismatch, awsLocation, 'localhost',
|
||||
(keys, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
const awsReq = getAwsParamsBucketMismatch(keys.destObjName,
|
||||
uploadId);
|
||||
s3.listParts(awsReq, (err, partList) => {
|
||||
assertPartList(partList, uploadId);
|
||||
s3.abortMultipartUpload(awsReq, err => {
|
||||
assert.equal(err, null, `Error aborting MPU: ${err}. ` +
|
||||
`You must abort MPU with upload ID ${uploadId} manually.`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return error 403 AccessDenied copying part to a ' +
|
||||
'different AWS location without object READ access',
|
||||
done => {
|
||||
const errorPutCopyPart = { code: 'AccessDenied', statusCode: 403 };
|
||||
copyPutPart(null, awsLocation, awsLocation2, 'localhost', done,
|
||||
errorPutCopyPart);
|
||||
});
|
||||
|
||||
|
||||
it('should copy part to file based on request endpoint', done => {
|
||||
copyPutPart(null, null, memLocation, 'localhost', () => {
|
||||
assert.strictEqual(ds.length, 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,20 +3,17 @@ const async = require('async');
|
|||
const crypto = require('crypto');
|
||||
const { parseString } = require('xml2js');
|
||||
const AWS = require('aws-sdk');
|
||||
const { metadata } = require('arsenal').storage.metadata.inMemory.metadata;
|
||||
const { config } = require('../../lib/Config');
|
||||
const { cleanup, DummyRequestLogger, makeAuthInfo }
|
||||
= require('../unit/helpers');
|
||||
const { cleanup, DummyRequestLogger, makeAuthInfo } = require('../unit/helpers');
|
||||
const { ds } = require('arsenal').storage.data.inMemory.datastore;
|
||||
const { bucketPut } = require('../../lib/api/bucketPut');
|
||||
const initiateMultipartUpload
|
||||
= require('../../lib/api/initiateMultipartUpload');
|
||||
const initiateMultipartUpload = require('../../lib/api/initiateMultipartUpload');
|
||||
const objectPutPart = require('../../lib/api/objectPutPart');
|
||||
const DummyRequest = require('../unit/DummyRequest');
|
||||
const { metadata } = require('arsenal').storage.metadata.inMemory.metadata;
|
||||
const mdWrapper = require('../../lib/metadata/wrapper');
|
||||
const constants = require('../../constants');
|
||||
const { getRealAwsConfig } =
|
||||
require('../functional/aws-node-sdk/test/support/awsConfig');
|
||||
const { getRealAwsConfig } = require('../functional/aws-node-sdk/test/support/awsConfig');
|
||||
|
||||
const memLocation = 'scality-internal-mem';
|
||||
const fileLocation = 'scality-internal-file';
|
||||
|
@ -25,7 +22,7 @@ const awsLocationMismatch = 'awsbackendmismatch';
|
|||
const awsConfig = getRealAwsConfig(awsLocation);
|
||||
const s3 = new AWS.S3(awsConfig);
|
||||
|
||||
const splitter = constants.splitter;
|
||||
const { splitter } = constants;
|
||||
const log = new DummyRequestLogger();
|
||||
const canonicalID = 'accessKey1';
|
||||
const authInfo = makeAuthInfo(canonicalID);
|
||||
|
@ -47,13 +44,13 @@ function _getOverviewKey(objectKey, uploadId) {
|
|||
}
|
||||
|
||||
function putPart(bucketLoc, mpuLoc, requestHost, cb,
|
||||
errorDescription) {
|
||||
errorDescription) {
|
||||
const objectName = `objectName-${Date.now()}`;
|
||||
const post = bucketLoc ? '<?xml version="1.0" encoding="UTF-8"?>' +
|
||||
'<CreateBucketConfiguration ' +
|
||||
'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
`<LocationConstraint>${bucketLoc}</LocationConstraint>` +
|
||||
'</CreateBucketConfiguration>' : '';
|
||||
const post = bucketLoc ? '<?xml version="1.0" encoding="UTF-8"?>'
|
||||
+ '<CreateBucketConfiguration '
|
||||
+ 'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">'
|
||||
+ `<LocationConstraint>${bucketLoc}</LocationConstraint>`
|
||||
+ '</CreateBucketConfiguration>' : '';
|
||||
const bucketPutReq = {
|
||||
bucketName,
|
||||
namespace,
|
||||
|
@ -70,10 +67,13 @@ errorDescription) {
|
|||
objectKey: objectName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: `/${objectName}?uploads`,
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
if (mpuLoc) {
|
||||
initiateReq.headers = { 'host': `${bucketName}.s3.amazonaws.com`,
|
||||
'x-amz-meta-scal-location-constraint': `${mpuLoc}` };
|
||||
initiateReq.headers = {
|
||||
'host': `${bucketName}.s3.amazonaws.com`,
|
||||
'x-amz-meta-scal-location-constraint': `${mpuLoc}`,
|
||||
};
|
||||
}
|
||||
if (requestHost) {
|
||||
initiateReq.parsedHost = requestHost;
|
||||
|
@ -123,9 +123,9 @@ errorDescription) {
|
|||
const partReq = new DummyRequest(partReqParams, body1);
|
||||
return objectPutPart(authInfo, partReq, undefined, log, err => {
|
||||
assert.strictEqual(err, null);
|
||||
if (bucketLoc !== awsLocation && mpuLoc !== awsLocation &&
|
||||
bucketLoc !== awsLocationMismatch &&
|
||||
mpuLoc !== awsLocationMismatch) {
|
||||
if (bucketLoc !== awsLocation && mpuLoc !== awsLocation
|
||||
&& bucketLoc !== awsLocationMismatch
|
||||
&& mpuLoc !== awsLocationMismatch) {
|
||||
const keysInMPUkeyMap = [];
|
||||
metadata.keyMaps.get(mpuBucket).forEach((val, key) => {
|
||||
keysInMPUkeyMap.push(key);
|
||||
|
@ -138,7 +138,7 @@ errorDescription) {
|
|||
});
|
||||
const partKey = sortedKeyMap[1];
|
||||
const partETag = metadata.keyMaps.get(mpuBucket)
|
||||
.get(partKey)['content-md5'];
|
||||
.get(partKey)['content-md5'];
|
||||
assert.strictEqual(keysInMPUkeyMap.length, 2);
|
||||
assert.strictEqual(partETag, calculatedHash1);
|
||||
}
|
||||
|
@ -148,8 +148,8 @@ errorDescription) {
|
|||
}
|
||||
|
||||
function listAndAbort(uploadId, calculatedHash2, objectName, done) {
|
||||
const awsBucket = config.locationConstraints[awsLocation].
|
||||
details.bucketName;
|
||||
const awsBucket = config.locationConstraints[awsLocation]
|
||||
.details.bucketName;
|
||||
const params = {
|
||||
Bucket: awsBucket,
|
||||
Key: objectName,
|
||||
|
@ -162,167 +162,168 @@ function listAndAbort(uploadId, calculatedHash2, objectName, done) {
|
|||
assert.strictEqual(`"${calculatedHash2}"`, data.Parts[0].ETag);
|
||||
}
|
||||
s3.abortMultipartUpload(params, err => {
|
||||
assert.equal(err, null, `Error aborting MPU: ${err}. ` +
|
||||
`You must abort MPU with upload ID ${uploadId} manually.`);
|
||||
assert.equal(err, null, `Error aborting MPU: ${err}. `
|
||||
+ `You must abort MPU with upload ID ${uploadId} manually.`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describeSkipIfE2E('objectPutPart API with multiple backends',
|
||||
function testSuite() {
|
||||
this.timeout(5000);
|
||||
function testSuite() {
|
||||
this.timeout(5000);
|
||||
|
||||
beforeEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
beforeEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('should upload a part to file based on mpu location', done => {
|
||||
putPart(memLocation, fileLocation, 'localhost', () => {
|
||||
it('should upload a part to file based on mpu location', done => {
|
||||
putPart(memLocation, fileLocation, 'localhost', () => {
|
||||
// if ds is empty, the object is not in mem, which means it
|
||||
// must be in file because those are the only possibilities
|
||||
// for unit tests
|
||||
assert.deepStrictEqual(ds, []);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to mem based on mpu location', done => {
|
||||
putPart(fileLocation, memLocation, 'localhost', () => {
|
||||
assert.deepStrictEqual(ds[1].value, body1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to AWS based on mpu location', done => {
|
||||
putPart(fileLocation, awsLocation, 'localhost',
|
||||
(objectName, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
listAndAbort(uploadId, null, objectName, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should replace part if two parts uploaded with same part number to AWS',
|
||||
done => {
|
||||
putPart(fileLocation, awsLocation, 'localhost',
|
||||
(objectName, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
const partReqParams = {
|
||||
bucketName,
|
||||
namespace,
|
||||
objectKey: objectName,
|
||||
headers: { 'host': `${bucketName}.s3.amazonaws.com`,
|
||||
'x-amz-meta-scal-location-constraint': awsLocation },
|
||||
url: `/${objectName}?partNumber=1&uploadId=${uploadId}`,
|
||||
query: {
|
||||
partNumber: '1', uploadId,
|
||||
},
|
||||
};
|
||||
const partReq = new DummyRequest(partReqParams, body2);
|
||||
objectPutPart(authInfo, partReq, undefined, log, err => {
|
||||
assert.equal(err, null, `Error putting second part: ${err}`);
|
||||
listAndAbort(uploadId, calculatedHash2, objectName, done);
|
||||
assert.deepStrictEqual(ds, []);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should upload part based on mpu location even if part ' +
|
||||
'location constraint is specified ', done => {
|
||||
putPart(fileLocation, memLocation, 'localhost', () => {
|
||||
assert.deepStrictEqual(ds[1].value, body1);
|
||||
done();
|
||||
it('should put a part to mem based on mpu location', done => {
|
||||
putPart(fileLocation, memLocation, 'localhost', () => {
|
||||
assert.deepStrictEqual(ds[1].value, body1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to file based on bucket location', done => {
|
||||
putPart(fileLocation, null, 'localhost', () => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to mem based on bucket location', done => {
|
||||
putPart(memLocation, null, 'localhost', () => {
|
||||
assert.deepStrictEqual(ds[1].value, body1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to AWS based on bucket location', done => {
|
||||
putPart(awsLocation, null, 'localhost',
|
||||
(objectName, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
listAndAbort(uploadId, null, objectName, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to AWS based on bucket location with bucketMatch ' +
|
||||
'set to true', done => {
|
||||
putPart(null, awsLocation, 'localhost',
|
||||
(objectName, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
listAndAbort(uploadId, null, objectName, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to AWS based on bucket location with bucketMatch ' +
|
||||
'set to false', done => {
|
||||
putPart(null, awsLocationMismatch, 'localhost',
|
||||
(objectName, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
listAndAbort(uploadId, null, `${bucketName}/${objectName}`, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to file based on request endpoint', done => {
|
||||
putPart(null, null, 'localhost', () => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should store a part even if the MPU was initiated on legacy version',
|
||||
done => {
|
||||
putPart('scality-internal-mem', null, 'localhost',
|
||||
(objectKey, uploadId) => {
|
||||
const mputOverviewKey = _getOverviewKey(objectKey, uploadId);
|
||||
mdWrapper.getObjectMD(mpuBucket, mputOverviewKey, {}, log,
|
||||
(err, res) => {
|
||||
// remove location constraint to mimic legacy behvior
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
res.controllingLocationConstraint = undefined;
|
||||
const md5Hash = crypto.createHash('md5');
|
||||
const bufferBody = Buffer.from(body1);
|
||||
const calculatedHash = md5Hash.update(bufferBody).digest('hex');
|
||||
const partRequest = new DummyRequest({
|
||||
bucketName,
|
||||
namespace,
|
||||
objectKey,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: `/${objectKey}?partNumber=1&uploadId=${uploadId}`,
|
||||
query: { partNumber: '1', uploadId },
|
||||
calculatedHash,
|
||||
}, body1);
|
||||
objectPutPart(authInfo, partRequest, undefined, log, err => {
|
||||
assert.strictEqual(err, null);
|
||||
const keysInMPUkeyMap = [];
|
||||
metadata.keyMaps.get(mpuBucket).forEach((val, key) => {
|
||||
keysInMPUkeyMap.push(key);
|
||||
});
|
||||
const sortedKeyMap = keysInMPUkeyMap.sort(a => {
|
||||
if (a.slice(0, 8) === 'overview') {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
const partKey = sortedKeyMap[1];
|
||||
const partETag = metadata.keyMaps.get(mpuBucket)
|
||||
.get(partKey)['content-md5'];
|
||||
assert.strictEqual(keysInMPUkeyMap.length, 2);
|
||||
assert.strictEqual(partETag, calculatedHash);
|
||||
done();
|
||||
it('should put a part to AWS based on mpu location', done => {
|
||||
putPart(fileLocation, awsLocation, 'localhost',
|
||||
(objectName, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
listAndAbort(uploadId, null, objectName, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should replace part if two parts uploaded with same part number to AWS',
|
||||
done => {
|
||||
putPart(fileLocation, awsLocation, 'localhost',
|
||||
(objectName, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
const partReqParams = {
|
||||
bucketName,
|
||||
namespace,
|
||||
objectKey: objectName,
|
||||
headers: {
|
||||
'host': `${bucketName}.s3.amazonaws.com`,
|
||||
'x-amz-meta-scal-location-constraint': awsLocation,
|
||||
},
|
||||
url: `/${objectName}?partNumber=1&uploadId=${uploadId}`,
|
||||
query: {
|
||||
partNumber: '1', uploadId,
|
||||
},
|
||||
};
|
||||
const partReq = new DummyRequest(partReqParams, body2);
|
||||
objectPutPart(authInfo, partReq, undefined, log, err => {
|
||||
assert.equal(err, null, `Error putting second part: ${err}`);
|
||||
listAndAbort(uploadId, calculatedHash2, objectName, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should upload part based on mpu location even if part '
|
||||
+ 'location constraint is specified ', done => {
|
||||
putPart(fileLocation, memLocation, 'localhost', () => {
|
||||
assert.deepStrictEqual(ds[1].value, body1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to file based on bucket location', done => {
|
||||
putPart(fileLocation, null, 'localhost', () => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to mem based on bucket location', done => {
|
||||
putPart(memLocation, null, 'localhost', () => {
|
||||
assert.deepStrictEqual(ds[1].value, body1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to AWS based on bucket location', done => {
|
||||
putPart(awsLocation, null, 'localhost',
|
||||
(objectName, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
listAndAbort(uploadId, null, objectName, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to AWS based on bucket location with bucketMatch '
|
||||
+ 'set to true', done => {
|
||||
putPart(null, awsLocation, 'localhost',
|
||||
(objectName, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
listAndAbort(uploadId, null, objectName, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to AWS based on bucket location with bucketMatch '
|
||||
+ 'set to false', done => {
|
||||
putPart(null, awsLocationMismatch, 'localhost',
|
||||
(objectName, uploadId) => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
listAndAbort(uploadId, null, `${bucketName}/${objectName}`, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should put a part to file based on request endpoint', done => {
|
||||
putPart(null, null, 'localhost', () => {
|
||||
assert.deepStrictEqual(ds, []);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should store a part even if the MPU was initiated on legacy version',
|
||||
done => {
|
||||
putPart('scality-internal-mem', null, 'localhost',
|
||||
(objectKey, uploadId) => {
|
||||
const mputOverviewKey = _getOverviewKey(objectKey, uploadId);
|
||||
mdWrapper.getObjectMD(mpuBucket, mputOverviewKey, {}, log,
|
||||
(err, res) => {
|
||||
// remove location constraint to mimic legacy behvior
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
res.controllingLocationConstraint = undefined;
|
||||
const md5Hash = crypto.createHash('md5');
|
||||
const bufferBody = Buffer.from(body1);
|
||||
const calculatedHash = md5Hash.update(bufferBody).digest('hex');
|
||||
const partRequest = new DummyRequest({
|
||||
bucketName,
|
||||
namespace,
|
||||
objectKey,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: `/${objectKey}?partNumber=1&uploadId=${uploadId}`,
|
||||
query: { partNumber: '1', uploadId },
|
||||
calculatedHash,
|
||||
}, body1);
|
||||
objectPutPart(authInfo, partRequest, undefined, log, err => {
|
||||
assert.strictEqual(err, null);
|
||||
const keysInMPUkeyMap = [];
|
||||
metadata.keyMaps.get(mpuBucket).forEach((val, key) => {
|
||||
keysInMPUkeyMap.push(key);
|
||||
});
|
||||
const sortedKeyMap = keysInMPUkeyMap.sort(a => {
|
||||
if (a.slice(0, 8) === 'overview') {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
const partKey = sortedKeyMap[1];
|
||||
const partETag = metadata.keyMaps.get(mpuBucket)
|
||||
.get(partKey)['content-md5'];
|
||||
assert.strictEqual(keysInMPUkeyMap.length, 2);
|
||||
assert.strictEqual(partETag, calculatedHash);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,7 +16,7 @@ class DummyRequest extends http.IncomingMessage {
|
|||
this.parsedContentLength = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this.actionImplicitDenies = false;
|
||||
if (Array.isArray(msg)) {
|
||||
msg.forEach(part => {
|
||||
this.push(part);
|
||||
|
|
|
@ -24,6 +24,7 @@ const bucketPutReq = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
const taggingUtil = new TaggingConfigTester();
|
||||
|
|
|
@ -106,7 +106,7 @@ describe('bucket authorization for bucketGet, bucketHead, ' +
|
|||
}
|
||||
bucket.setCannedAcl(value.canned);
|
||||
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);
|
||||
done();
|
||||
});
|
||||
|
@ -128,13 +128,13 @@ describe('bucket authorization for bucketGetACL', () => {
|
|||
|
||||
it('should allow access to bucket owner', () => {
|
||||
const result = isBucketAuthorized(bucket, 'bucketGetACL',
|
||||
ownerCanonicalId, authInfo);
|
||||
ownerCanonicalId, authInfo, false);
|
||||
assert.strictEqual(result, true);
|
||||
});
|
||||
|
||||
it('should allow access to user in bucket owner account', () => {
|
||||
const result = isBucketAuthorized(bucket, 'bucketGetACL',
|
||||
ownerCanonicalId, userAuthInfo);
|
||||
ownerCanonicalId, userAuthInfo, false);
|
||||
assert.strictEqual(result, true);
|
||||
});
|
||||
|
||||
|
@ -157,7 +157,7 @@ describe('bucket authorization for bucketGetACL', () => {
|
|||
orders.forEach(value => {
|
||||
it(`should allow access to ${value.it}`, done => {
|
||||
const noAuthResult = isBucketAuthorized(bucket, 'bucketGetACL',
|
||||
value.id);
|
||||
value.id, null, false);
|
||||
assert.strictEqual(noAuthResult, false);
|
||||
if (value.aclParam) {
|
||||
bucket.setSpecificAcl(value.aclParam[1], value.aclParam[0]);
|
||||
|
@ -165,7 +165,7 @@ describe('bucket authorization for bucketGetACL', () => {
|
|||
bucket.setCannedAcl(value.canned);
|
||||
}
|
||||
const authorizedResult = isBucketAuthorized(bucket, 'bucketGetACL',
|
||||
value.id, value.auth);
|
||||
value.id, value.auth, false);
|
||||
assert.strictEqual(authorizedResult, true);
|
||||
done();
|
||||
});
|
||||
|
@ -187,13 +187,13 @@ describe('bucket authorization for bucketPutACL', () => {
|
|||
|
||||
it('should allow access to bucket owner', () => {
|
||||
const result = isBucketAuthorized(bucket, 'bucketPutACL',
|
||||
ownerCanonicalId, authInfo);
|
||||
ownerCanonicalId, authInfo, false);
|
||||
assert.strictEqual(result, true);
|
||||
});
|
||||
|
||||
it('should allow access to user in bucket owner account', () => {
|
||||
const result = isBucketAuthorized(bucket, 'bucketPutACL',
|
||||
ownerCanonicalId, userAuthInfo);
|
||||
ownerCanonicalId, userAuthInfo, false);
|
||||
assert.strictEqual(result, true);
|
||||
});
|
||||
|
||||
|
@ -202,11 +202,11 @@ describe('bucket authorization for bucketPutACL', () => {
|
|||
it('should allow access to account if ' +
|
||||
`account was granted ${value} right`, done => {
|
||||
const noAuthResult = isBucketAuthorized(bucket, 'bucketPutACL',
|
||||
accountToVet, altAcctAuthInfo);
|
||||
accountToVet, altAcctAuthInfo, false);
|
||||
assert.strictEqual(noAuthResult, false);
|
||||
bucket.setSpecificAcl(accountToVet, value);
|
||||
const authorizedResult = isBucketAuthorized(bucket, 'bucketPutACL',
|
||||
accountToVet, altAcctAuthInfo);
|
||||
accountToVet, altAcctAuthInfo, false);
|
||||
assert.strictEqual(authorizedResult, true);
|
||||
done();
|
||||
});
|
||||
|
@ -228,13 +228,13 @@ describe('bucket authorization for bucketOwnerAction', () => {
|
|||
|
||||
it('should allow access to bucket owner', () => {
|
||||
const result = isBucketAuthorized(bucket, 'bucketDeleteCors',
|
||||
ownerCanonicalId, authInfo);
|
||||
ownerCanonicalId, authInfo, false);
|
||||
assert.strictEqual(result, true);
|
||||
});
|
||||
|
||||
it('should allow access to user in bucket owner account', () => {
|
||||
const result = isBucketAuthorized(bucket, 'bucketDeleteCors',
|
||||
ownerCanonicalId, userAuthInfo);
|
||||
ownerCanonicalId, userAuthInfo, false);
|
||||
assert.strictEqual(result, true);
|
||||
});
|
||||
|
||||
|
@ -256,7 +256,7 @@ describe('bucket authorization for bucketOwnerAction', () => {
|
|||
}
|
||||
bucket.setCannedAcl(value.canned);
|
||||
const result = isBucketAuthorized(bucket, 'bucketDeleteCors',
|
||||
value.id, value.auth);
|
||||
value.id, value.auth, false);
|
||||
assert.strictEqual(result, false);
|
||||
done();
|
||||
});
|
||||
|
@ -278,13 +278,13 @@ describe('bucket authorization for bucketDelete', () => {
|
|||
|
||||
it('should allow access to bucket owner', () => {
|
||||
const result = isBucketAuthorized(bucket, 'bucketDelete',
|
||||
ownerCanonicalId, authInfo);
|
||||
ownerCanonicalId, authInfo, false);
|
||||
assert.strictEqual(result, true);
|
||||
});
|
||||
|
||||
it('should allow access to user in bucket owner account', () => {
|
||||
const result = isBucketAuthorized(bucket, 'bucketDelete',
|
||||
ownerCanonicalId, userAuthInfo);
|
||||
ownerCanonicalId, userAuthInfo, false);
|
||||
assert.strictEqual(result, true);
|
||||
});
|
||||
|
||||
|
@ -305,7 +305,7 @@ describe('bucket authorization for bucketDelete', () => {
|
|||
bucket.setSpecificAcl(value.aclParam[1], value.aclParam[0]);
|
||||
}
|
||||
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);
|
||||
done();
|
||||
});
|
||||
|
@ -329,13 +329,13 @@ describe('bucket authorization for objectDelete and objectPut', () => {
|
|||
|
||||
it('should allow access to bucket owner', () => {
|
||||
const results = requestTypes.map(type =>
|
||||
isBucketAuthorized(bucket, type, ownerCanonicalId, authInfo));
|
||||
isBucketAuthorized(bucket, type, ownerCanonicalId, authInfo, false));
|
||||
assert.deepStrictEqual(results, [true, true]);
|
||||
});
|
||||
|
||||
it('should allow access to user in bucket owner account', () => {
|
||||
const results = requestTypes.map(type =>
|
||||
isBucketAuthorized(bucket, type, ownerCanonicalId, userAuthInfo));
|
||||
isBucketAuthorized(bucket, type, ownerCanonicalId, userAuthInfo, false));
|
||||
assert.deepStrictEqual(results, [true, true]);
|
||||
});
|
||||
|
||||
|
@ -360,13 +360,13 @@ describe('bucket authorization for objectDelete and objectPut', () => {
|
|||
it(`should allow access to ${value.it}`, done => {
|
||||
bucket.setCannedAcl(value.canned);
|
||||
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);
|
||||
if (value.aclParam) {
|
||||
bucket.setSpecificAcl(value.aclParam[1], value.aclParam[0]);
|
||||
}
|
||||
const authResults = requestTypes.map(type =>
|
||||
isBucketAuthorized(bucket, type, accountToVet, altAcctAuthInfo));
|
||||
isBucketAuthorized(bucket, type, accountToVet, altAcctAuthInfo, false));
|
||||
assert.deepStrictEqual(authResults, [true, true]);
|
||||
done();
|
||||
});
|
||||
|
@ -378,10 +378,10 @@ describe('bucket authorization for objectPutACL and objectGetACL', () => {
|
|||
'are done at object level', done => {
|
||||
const requestTypes = ['objectPutACL', 'objectGetACL'];
|
||||
const results = requestTypes.map(type =>
|
||||
isBucketAuthorized(bucket, type, accountToVet, altAcctAuthInfo));
|
||||
isBucketAuthorized(bucket, type, accountToVet, altAcctAuthInfo, false));
|
||||
assert.deepStrictEqual(results, [true, true]);
|
||||
const publicUserResults = requestTypes.map(type =>
|
||||
isBucketAuthorized(bucket, type, constants.publicId));
|
||||
isBucketAuthorized(bucket, type, constants.publicId, null, false));
|
||||
assert.deepStrictEqual(publicUserResults, [true, true]);
|
||||
done();
|
||||
});
|
||||
|
|
|
@ -77,7 +77,6 @@ function createMPU(testRequest, initiateRequest, deleteOverviewMPUObj, cb) {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('bucketDelete API', () => {
|
||||
beforeEach(() => {
|
||||
cleanup();
|
||||
|
@ -88,6 +87,7 @@ describe('bucketDelete API', () => {
|
|||
namespace,
|
||||
headers: {},
|
||||
url: `/${bucketName}`,
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
const initiateRequest = {
|
||||
|
@ -96,6 +96,7 @@ describe('bucketDelete API', () => {
|
|||
objectKey: objectName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: `/${objectName}?uploads`,
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
it('should return an error if the bucket is not empty', done => {
|
||||
|
|
|
@ -19,12 +19,12 @@ const testBucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
const testBucketPutCorsRequest =
|
||||
corsUtil.createBucketCorsRequest('PUT', bucketName);
|
||||
const testBucketDeleteCorsRequest =
|
||||
corsUtil.createBucketCorsRequest('DELETE', bucketName);
|
||||
|
||||
describe('deleteBucketCors API', () => {
|
||||
beforeEach(done => {
|
||||
cleanup();
|
||||
|
|
|
@ -13,8 +13,8 @@ const bucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
describe('bucketDeleteEncryption API', () => {
|
||||
before(() => cleanup());
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ function _makeRequest(includeXml) {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
if (includeXml) {
|
||||
request.post = '<LifecycleConfiguration ' +
|
||||
|
@ -30,7 +31,6 @@ function _makeRequest(includeXml) {
|
|||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
describe('deleteBucketLifecycle API', () => {
|
||||
before(() => cleanup());
|
||||
beforeEach(done => bucketPut(authInfo, _makeRequest(), log, done));
|
||||
|
|
|
@ -19,6 +19,7 @@ function _makeRequest(includePolicy) {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
if (includePolicy) {
|
||||
const examplePolicy = {
|
||||
|
@ -36,7 +37,6 @@ function _makeRequest(includePolicy) {
|
|||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
describe('deleteBucketPolicy API', () => {
|
||||
before(() => cleanup());
|
||||
beforeEach(done => bucketPut(authInfo, _makeRequest(), log, done));
|
||||
|
|
|
@ -20,6 +20,7 @@ const testBucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
const testBucketDeleteWebsiteRequest = {
|
||||
bucketName,
|
||||
|
@ -28,10 +29,10 @@ const testBucketDeleteWebsiteRequest = {
|
|||
},
|
||||
url: '/?website',
|
||||
query: { website: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
const testBucketPutWebsiteRequest = Object.assign({ post: config.getXml() },
|
||||
testBucketDeleteWebsiteRequest);
|
||||
|
||||
describe('deleteBucketWebsite API', () => {
|
||||
beforeEach(done => {
|
||||
cleanup();
|
||||
|
|
|
@ -63,6 +63,7 @@ const baseGetRequest = {
|
|||
bucketName,
|
||||
namespace,
|
||||
headers: { host: '/' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
const baseUrl = `/${bucketName}`;
|
||||
|
||||
|
@ -173,7 +174,6 @@ const tests = [
|
|||
},
|
||||
},
|
||||
];
|
||||
|
||||
describe('bucketGet API', () => {
|
||||
beforeEach(() => {
|
||||
cleanup();
|
||||
|
|
|
@ -14,7 +14,6 @@ const authInfo = makeAuthInfo(accessKey);
|
|||
const canonicalID = authInfo.getCanonicalID();
|
||||
const namespace = 'default';
|
||||
const bucketName = 'bucketname';
|
||||
|
||||
describe('bucketGetACL API', () => {
|
||||
beforeEach(() => {
|
||||
cleanup();
|
||||
|
@ -25,6 +24,7 @@ describe('bucketGetACL API', () => {
|
|||
namespace,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
const testGetACLRequest = {
|
||||
bucketName,
|
||||
|
@ -32,6 +32,7 @@ describe('bucketGetACL API', () => {
|
|||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
it('should get a canned private ACL', done => {
|
||||
|
@ -44,6 +45,7 @@ describe('bucketGetACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
async.waterfall([
|
||||
|
@ -76,6 +78,7 @@ describe('bucketGetACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
async.waterfall([
|
||||
|
@ -119,6 +122,7 @@ describe('bucketGetACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
async.waterfall([
|
||||
|
@ -156,6 +160,7 @@ describe('bucketGetACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
async.waterfall([
|
||||
|
@ -194,6 +199,7 @@ describe('bucketGetACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
async.waterfall([
|
||||
|
@ -248,6 +254,7 @@ describe('bucketGetACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
const canonicalIDforSample1 =
|
||||
'79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be';
|
||||
|
@ -338,6 +345,7 @@ describe('bucketGetACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
async.waterfall([
|
||||
|
@ -377,6 +385,7 @@ describe('bucketGetACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
async.waterfall([
|
||||
|
|
|
@ -16,6 +16,7 @@ const testBucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
function _makeCorsRequest(xml) {
|
||||
|
@ -26,6 +27,7 @@ function _makeCorsRequest(xml) {
|
|||
},
|
||||
url: '/?cors',
|
||||
query: { cors: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
if (xml) {
|
||||
|
@ -55,7 +57,6 @@ function _comparePutGetXml(sampleXml, done) {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('getBucketCors API', () => {
|
||||
beforeEach(done => {
|
||||
cleanup();
|
||||
|
|
|
@ -17,8 +17,8 @@ const testBucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
describe('getBucketLifecycle API', () => {
|
||||
before(() => cleanup());
|
||||
beforeEach(done => bucketPut(authInfo, testBucketPutRequest, log, done));
|
||||
|
|
|
@ -16,6 +16,7 @@ const testBucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
const testGetLocationRequest = {
|
||||
|
@ -25,6 +26,7 @@ const testGetLocationRequest = {
|
|||
},
|
||||
url: '/?location',
|
||||
query: { location: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
const locationConstraints = config.locationConstraints;
|
||||
|
@ -37,7 +39,6 @@ function getBucketRequestObject(location) {
|
|||
'</CreateBucketConfiguration>' : undefined;
|
||||
return Object.assign({ post }, testBucketPutRequest);
|
||||
}
|
||||
|
||||
describe('getBucketLocation API', () => {
|
||||
Object.keys(locationConstraints).forEach(location => {
|
||||
if (location === 'us-east-1') {
|
||||
|
|
|
@ -15,6 +15,7 @@ const testBucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
function getNotificationRequest(bucketName, xml) {
|
||||
|
@ -23,6 +24,7 @@ function getNotificationRequest(bucketName, xml) {
|
|||
headers: {
|
||||
host: `${bucketName}.s3.amazonaws.com`,
|
||||
},
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
if (xml) {
|
||||
request.post = xml;
|
||||
|
@ -52,7 +54,6 @@ function getNotificationXml() {
|
|||
'</NotificationConfiguration>';
|
||||
}
|
||||
|
||||
|
||||
describe('getBucketNotification API', () => {
|
||||
before(cleanup);
|
||||
beforeEach(done => bucketPut(authInfo, testBucketPutRequest, log, done));
|
||||
|
|
|
@ -14,6 +14,7 @@ const bucketPutReq = {
|
|||
host: `${bucketName}.s3.amazonaws.com`,
|
||||
},
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
const testBucketPutReqWithObjLock = {
|
||||
|
@ -23,6 +24,7 @@ const testBucketPutReqWithObjLock = {
|
|||
'x-amz-bucket-object-lock-enabled': 'True',
|
||||
},
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
function getObjectLockConfigRequest(bucketName, xml) {
|
||||
|
@ -33,6 +35,7 @@ function getObjectLockConfigRequest(bucketName, xml) {
|
|||
'x-amz-bucket-object-lock-enabled': 'true',
|
||||
},
|
||||
url: '/?object-lock',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
if (xml) {
|
||||
request.post = xml;
|
||||
|
@ -65,7 +68,6 @@ function getObjectLockXml(mode, type, time) {
|
|||
xmlStr += xml.objLockConfigClose;
|
||||
return xmlStr;
|
||||
}
|
||||
|
||||
describe('bucketGetObjectLock API', () => {
|
||||
before(done => bucketPut(authInfo, bucketPutReq, log, done));
|
||||
after(cleanup);
|
||||
|
@ -79,7 +81,6 @@ describe('bucketGetObjectLock API', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('bucketGetObjectLock API', () => {
|
||||
before(cleanup);
|
||||
beforeEach(done => bucketPut(authInfo, testBucketPutReqWithObjLock, log, done));
|
||||
|
|
|
@ -16,6 +16,7 @@ const testBasicRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
const expectedBucketPolicy = {
|
||||
|
@ -34,8 +35,8 @@ const testPutPolicyRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
post: JSON.stringify(expectedBucketPolicy),
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
describe('getBucketPolicy API', () => {
|
||||
before(() => cleanup());
|
||||
beforeEach(done => bucketPut(authInfo, testBasicRequest, log, done));
|
||||
|
|
|
@ -53,7 +53,6 @@ function getReplicationConfig() {
|
|||
],
|
||||
};
|
||||
}
|
||||
|
||||
describe("'getReplicationConfigurationXML' function", () => {
|
||||
it('should return XML from the bucket replication configuration', done =>
|
||||
getAndCheckXML(getReplicationConfig(), done));
|
||||
|
|
|
@ -15,6 +15,7 @@ const testBucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
function _makeWebsiteRequest(xml) {
|
||||
|
@ -25,6 +26,7 @@ function _makeWebsiteRequest(xml) {
|
|||
},
|
||||
url: '/?website',
|
||||
query: { website: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
if (xml) {
|
||||
|
@ -53,7 +55,6 @@ function _comparePutGetXml(sampleXml, done) {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('getBucketWebsite API', () => {
|
||||
beforeEach(done => {
|
||||
cleanup();
|
||||
|
|
|
@ -14,6 +14,7 @@ const testRequest = {
|
|||
namespace,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
describe('bucketHead API', () => {
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -244,7 +244,7 @@ describe('bucket policy authorization', () => {
|
|||
describe('isBucketAuthorized with no policy set', () => {
|
||||
it('should allow access to bucket owner', done => {
|
||||
const allowed = isBucketAuthorized(bucket, 'bucketPut',
|
||||
bucketOwnerCanonicalId, null, log);
|
||||
bucketOwnerCanonicalId, null, false, log);
|
||||
assert.equal(allowed, true);
|
||||
done();
|
||||
});
|
||||
|
@ -252,7 +252,7 @@ describe('bucket policy authorization', () => {
|
|||
it('should deny access to non-bucket owner',
|
||||
done => {
|
||||
const allowed = isBucketAuthorized(bucket, 'bucketPut',
|
||||
altAcctCanonicalId, null, log);
|
||||
altAcctCanonicalId, null, false, log);
|
||||
assert.equal(allowed, false);
|
||||
done();
|
||||
});
|
||||
|
@ -268,7 +268,7 @@ describe('bucket policy authorization', () => {
|
|||
it('should allow access to non-bucket owner if principal is set to "*"',
|
||||
done => {
|
||||
const allowed = isBucketAuthorized(bucket, bucAction,
|
||||
altAcctCanonicalId, null, log);
|
||||
altAcctCanonicalId, null, false, log);
|
||||
assert.equal(allowed, true);
|
||||
done();
|
||||
});
|
||||
|
@ -276,7 +276,7 @@ describe('bucket policy authorization', () => {
|
|||
it('should allow access to public user if principal is set to "*"',
|
||||
done => {
|
||||
const allowed = isBucketAuthorized(bucket, bucAction,
|
||||
constants.publicId, null, log);
|
||||
constants.publicId, null, false, log);
|
||||
assert.equal(allowed, true);
|
||||
done();
|
||||
});
|
||||
|
@ -287,7 +287,7 @@ describe('bucket policy authorization', () => {
|
|||
newPolicy.Statement[0][t.keyToChange] = t.bucketValue;
|
||||
bucket.setBucketPolicy(newPolicy);
|
||||
const allowed = isBucketAuthorized(bucket, bucAction,
|
||||
t.bucketId, t.bucketAuthInfo, log);
|
||||
t.bucketId, t.bucketAuthInfo, false, log);
|
||||
assert.equal(allowed, t.expected);
|
||||
done();
|
||||
});
|
||||
|
@ -304,7 +304,7 @@ describe('bucket policy authorization', () => {
|
|||
};
|
||||
bucket.setBucketPolicy(newPolicy);
|
||||
const allowed = isBucketAuthorized(bucket, bucAction,
|
||||
altAcctCanonicalId, null, log);
|
||||
altAcctCanonicalId, null, false, log);
|
||||
assert.equal(allowed, false);
|
||||
done();
|
||||
});
|
||||
|
@ -312,7 +312,7 @@ describe('bucket policy authorization', () => {
|
|||
it('should deny access to non-bucket owner with an unsupported action type',
|
||||
done => {
|
||||
const allowed = isBucketAuthorized(bucket, 'unsupportedAction',
|
||||
altAcctCanonicalId, null, log);
|
||||
altAcctCanonicalId, null, false, log);
|
||||
assert.equal(allowed, false);
|
||||
done();
|
||||
});
|
||||
|
@ -325,7 +325,7 @@ describe('bucket policy authorization', () => {
|
|||
|
||||
it('should allow access to object owner', done => {
|
||||
const allowed = isObjAuthorized(bucket, object, objAction,
|
||||
objectOwnerCanonicalId, null, log);
|
||||
objectOwnerCanonicalId, null, false, log);
|
||||
assert.equal(allowed, true);
|
||||
done();
|
||||
});
|
||||
|
@ -333,7 +333,7 @@ describe('bucket policy authorization', () => {
|
|||
it('should deny access to non-object owner',
|
||||
done => {
|
||||
const allowed = isObjAuthorized(bucket, object, objAction,
|
||||
altAcctCanonicalId, null, log);
|
||||
altAcctCanonicalId, null, false, log);
|
||||
assert.equal(allowed, false);
|
||||
done();
|
||||
});
|
||||
|
@ -352,7 +352,7 @@ describe('bucket policy authorization', () => {
|
|||
it('should allow access to non-object owner if principal is set to "*"',
|
||||
done => {
|
||||
const allowed = isObjAuthorized(bucket, object, objAction,
|
||||
altAcctCanonicalId, null, log);
|
||||
altAcctCanonicalId, null, false, log);
|
||||
assert.equal(allowed, true);
|
||||
done();
|
||||
});
|
||||
|
@ -360,7 +360,7 @@ describe('bucket policy authorization', () => {
|
|||
it('should allow access to public user if principal is set to "*"',
|
||||
done => {
|
||||
const allowed = isObjAuthorized(bucket, object, objAction,
|
||||
constants.publicId, null, log);
|
||||
constants.publicId, null, false, log);
|
||||
assert.equal(allowed, true);
|
||||
done();
|
||||
});
|
||||
|
@ -371,7 +371,7 @@ describe('bucket policy authorization', () => {
|
|||
newPolicy.Statement[0][t.keyToChange] = t.objectValue;
|
||||
bucket.setBucketPolicy(newPolicy);
|
||||
const allowed = isObjAuthorized(bucket, object, objAction,
|
||||
t.objectId, t.objectAuthInfo, log);
|
||||
t.objectId, t.objectAuthInfo, false, log);
|
||||
assert.equal(allowed, t.expected);
|
||||
done();
|
||||
});
|
||||
|
@ -383,7 +383,7 @@ describe('bucket policy authorization', () => {
|
|||
newPolicy.Statement[0].Action = ['s3:GetObject'];
|
||||
bucket.setBucketPolicy(newPolicy);
|
||||
const allowed = isObjAuthorized(bucket, object, 'objectHead',
|
||||
altAcctCanonicalId, altAcctAuthInfo, log);
|
||||
altAcctCanonicalId, altAcctAuthInfo, false, log);
|
||||
assert.equal(allowed, true);
|
||||
done();
|
||||
});
|
||||
|
@ -393,7 +393,7 @@ describe('bucket policy authorization', () => {
|
|||
newPolicy.Statement[0].Action = ['s3:PutObject'];
|
||||
bucket.setBucketPolicy(newPolicy);
|
||||
const allowed = isObjAuthorized(bucket, object, 'objectHead',
|
||||
altAcctCanonicalId, altAcctAuthInfo, log);
|
||||
altAcctCanonicalId, altAcctAuthInfo, false, log);
|
||||
assert.equal(allowed, false);
|
||||
done();
|
||||
});
|
||||
|
@ -408,7 +408,7 @@ describe('bucket policy authorization', () => {
|
|||
};
|
||||
bucket.setBucketPolicy(newPolicy);
|
||||
const allowed = isObjAuthorized(bucket, object, objAction,
|
||||
altAcctCanonicalId, null, log);
|
||||
altAcctCanonicalId, null, false, log);
|
||||
assert.equal(allowed, false);
|
||||
done();
|
||||
});
|
||||
|
@ -416,7 +416,7 @@ describe('bucket policy authorization', () => {
|
|||
it('should deny access to non-object owner with an unsupported action type',
|
||||
done => {
|
||||
const allowed = isObjAuthorized(bucket, object, 'unsupportedAction',
|
||||
altAcctCanonicalId, null, log);
|
||||
altAcctCanonicalId, null, false, log);
|
||||
assert.equal(allowed, false);
|
||||
done();
|
||||
});
|
||||
|
|
|
@ -18,11 +18,10 @@ const testBucketPutRequest = {
|
|||
namespace,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
const canonicalIDforSample1 =
|
||||
'79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be';
|
||||
const canonicalIDforSample2 =
|
||||
'79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2bf';
|
||||
const canonicalIDforSample1 = '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be';
|
||||
const canonicalIDforSample2 = '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2bf';
|
||||
|
||||
const invalidIds = {
|
||||
'too short': 'id="invalid_id"',
|
||||
|
@ -42,11 +41,10 @@ describe('putBucketACL API', () => {
|
|||
afterEach(() => cleanup());
|
||||
|
||||
it('should parse a grantheader', () => {
|
||||
const grantRead =
|
||||
`uri=${constants.logId}, ` +
|
||||
'emailAddress="test@testing.com", ' +
|
||||
'emailAddress="test2@testly.com", ' +
|
||||
'id="sdfsdfsfwwiieohefs"';
|
||||
const grantRead = `uri=${constants.logId}, `
|
||||
+ 'emailAddress="test@testing.com", '
|
||||
+ 'emailAddress="test2@testly.com", '
|
||||
+ 'id="sdfsdfsfwwiieohefs"';
|
||||
const grantReadHeader = aclUtils.parseGrant(grantRead, 'read');
|
||||
const firstIdentifier = grantReadHeader[0].identifier;
|
||||
assert.strictEqual(firstIdentifier, constants.logId);
|
||||
|
@ -58,7 +56,7 @@ describe('putBucketACL API', () => {
|
|||
assert.strictEqual(fourthIdentifier, 'sdfsdfsfwwiieohefs');
|
||||
const fourthType = grantReadHeader[3].userIDType;
|
||||
assert.strictEqual(fourthType, 'id');
|
||||
const grantType = grantReadHeader[3].grantType;
|
||||
const { grantType } = grantReadHeader[3];
|
||||
assert.strictEqual(grantType, 'read');
|
||||
});
|
||||
|
||||
|
@ -72,6 +70,7 @@ describe('putBucketACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
|
@ -90,6 +89,7 @@ describe('putBucketACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
assert.strictEqual(err, undefined);
|
||||
|
@ -111,6 +111,7 @@ describe('putBucketACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
const testACLRequest2 = {
|
||||
bucketName,
|
||||
|
@ -121,6 +122,7 @@ describe('putBucketACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
assert.strictEqual(err, undefined);
|
||||
|
@ -130,7 +132,7 @@ describe('putBucketACL API', () => {
|
|||
assert.strictEqual(err, undefined);
|
||||
metadata.getBucket(bucketName, log, (err, md) => {
|
||||
assert.strictEqual(md.getAcl().Canned,
|
||||
'authenticated-read');
|
||||
'authenticated-read');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -138,8 +140,8 @@ describe('putBucketACL API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should set a canned private ACL ' +
|
||||
'followed by a log-delivery-write ACL', done => {
|
||||
it('should set a canned private ACL '
|
||||
+ 'followed by a log-delivery-write ACL', done => {
|
||||
const testACLRequest = {
|
||||
bucketName,
|
||||
namespace,
|
||||
|
@ -149,6 +151,7 @@ describe('putBucketACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
const testACLRequest2 = {
|
||||
bucketName,
|
||||
|
@ -159,6 +162,7 @@ describe('putBucketACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
|
@ -169,7 +173,7 @@ describe('putBucketACL API', () => {
|
|||
assert.strictEqual(err, undefined);
|
||||
metadata.getBucket(bucketName, log, (err, md) => {
|
||||
assert.strictEqual(md.getAcl().Canned,
|
||||
'log-delivery-write');
|
||||
'log-delivery-write');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -184,19 +188,20 @@ describe('putBucketACL API', () => {
|
|||
headers: {
|
||||
'host': `${bucketName}.s3.amazonaws.com`,
|
||||
'x-amz-grant-full-control':
|
||||
'emailaddress="sampleaccount1@sampling.com"' +
|
||||
',emailaddress="sampleaccount2@sampling.com"',
|
||||
'emailaddress="sampleaccount1@sampling.com"'
|
||||
+ ',emailaddress="sampleaccount2@sampling.com"',
|
||||
'x-amz-grant-read': `uri=${constants.logId}`,
|
||||
'x-amz-grant-write': `uri=${constants.publicId}`,
|
||||
'x-amz-grant-read-acp':
|
||||
'id=79a59df900b949e55d96a1e698fbacedfd6e09d98eac' +
|
||||
'f8f8d5218e7cd47ef2be',
|
||||
'id=79a59df900b949e55d96a1e698fbacedfd6e09d98eac'
|
||||
+ 'f8f8d5218e7cd47ef2be',
|
||||
'x-amz-grant-write-acp':
|
||||
'id=79a59df900b949e55d96a1e698fbacedfd6e09d98eac' +
|
||||
'f8f8d5218e7cd47ef2bf',
|
||||
'id=79a59df900b949e55d96a1e698fbacedfd6e09d98eac'
|
||||
+ 'f8f8d5218e7cd47ef2bf',
|
||||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
assert.strictEqual(err, undefined);
|
||||
|
@ -223,21 +228,22 @@ describe('putBucketACL API', () => {
|
|||
headers: {
|
||||
'host': `${bucketName}.s3.amazonaws.com`,
|
||||
'x-amz-grant-full-control':
|
||||
'emailaddress="sampleaccount1@sampling.com"' +
|
||||
',emailaddress="sampleaccount2@sampling.com"',
|
||||
'emailaddress="sampleaccount1@sampling.com"'
|
||||
+ ',emailaddress="sampleaccount2@sampling.com"',
|
||||
'x-amz-grant-read':
|
||||
'emailaddress="sampleaccount1@sampling.com"',
|
||||
'x-amz-grant-write':
|
||||
'emailaddress="sampleaccount1@sampling.com"',
|
||||
'x-amz-grant-read-acp':
|
||||
'id=79a59df900b949e55d96a1e698fbacedfd6e09d98eac' +
|
||||
'f8f8d5218e7cd47ef2be',
|
||||
'id=79a59df900b949e55d96a1e698fbacedfd6e09d98eac'
|
||||
+ 'f8f8d5218e7cd47ef2be',
|
||||
'x-amz-grant-write-acp':
|
||||
'id=79a59df900b949e55d96a1e698fbacedfd6e09d98eac' +
|
||||
'f8f8d5218e7cd47ef2bf',
|
||||
'id=79a59df900b949e55d96a1e698fbacedfd6e09d98eac'
|
||||
+ 'f8f8d5218e7cd47ef2bf',
|
||||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
assert.strictEqual(err, undefined);
|
||||
|
@ -260,8 +266,8 @@ describe('putBucketACL API', () => {
|
|||
});
|
||||
|
||||
Object.keys(invalidIds).forEach(idType => {
|
||||
it('should return an error if grantee canonical ID provided in ACL ' +
|
||||
`request invalid because ${idType}`, done => {
|
||||
it('should return an error if grantee canonical ID provided in ACL '
|
||||
+ `request invalid because ${idType}`, done => {
|
||||
const testACLRequest = {
|
||||
bucketName,
|
||||
namespace,
|
||||
|
@ -271,6 +277,7 @@ describe('putBucketACL API', () => {
|
|||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
return bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
assert.deepStrictEqual(err, errors.InvalidArgument);
|
||||
|
@ -279,19 +286,20 @@ describe('putBucketACL API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should return an error if invalid email ' +
|
||||
'provided in ACL header request', done => {
|
||||
it('should return an error if invalid email '
|
||||
+ 'provided in ACL header request', done => {
|
||||
const testACLRequest = {
|
||||
bucketName,
|
||||
namespace,
|
||||
headers: {
|
||||
'host': `${bucketName}.s3.amazonaws.com`,
|
||||
'x-amz-grant-full-control':
|
||||
'emailaddress="sampleaccount1@sampling.com"' +
|
||||
',emailaddress="nonexistentEmail@sampling.com"',
|
||||
'emailaddress="sampleaccount1@sampling.com"'
|
||||
+ ',emailaddress="nonexistentEmail@sampling.com"',
|
||||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
|
@ -305,52 +313,53 @@ describe('putBucketACL API', () => {
|
|||
bucketName,
|
||||
namespace,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
post: '<AccessControlPolicy xmlns=' +
|
||||
'"http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
'<Owner>' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Owner>' +
|
||||
'<AccessControlList>' +
|
||||
'<Grant>' +
|
||||
'<Grantee xsi:type="CanonicalUser">' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Grantee>' +
|
||||
'<Permission>FULL_CONTROL</Permission>' +
|
||||
'</Grant>' +
|
||||
'<Grant>' +
|
||||
'<Grantee xsi:type="Group">' +
|
||||
`<URI>${constants.publicId}</URI>` +
|
||||
'</Grantee>' +
|
||||
'<Permission>READ</Permission>' +
|
||||
'</Grant>' +
|
||||
'<Grant>' +
|
||||
'<Grantee xsi:type="Group">' +
|
||||
`<URI>${constants.logId}</URI>` +
|
||||
'</Grantee>' +
|
||||
'<Permission>WRITE</Permission>' +
|
||||
'</Grant>' +
|
||||
'<Grant>' +
|
||||
'<Grantee xsi:type="AmazonCustomerByEmail">' +
|
||||
'<EmailAddress>sampleaccount1@sampling.com' +
|
||||
'</EmailAddress>' +
|
||||
'</Grantee>' +
|
||||
'<Permission>WRITE_ACP</Permission>' +
|
||||
'</Grant>' +
|
||||
'<Grant>' +
|
||||
'<Grantee xsi:type="CanonicalUser">' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbacedfd' +
|
||||
'6e09d98eacf8f8d5218e7cd47ef2bf</ID>' +
|
||||
'</Grantee>' +
|
||||
'<Permission>READ_ACP</Permission>' +
|
||||
'</Grant>' +
|
||||
'</AccessControlList>' +
|
||||
'</AccessControlPolicy>',
|
||||
post: '<AccessControlPolicy xmlns='
|
||||
+ '"http://s3.amazonaws.com/doc/2006-03-01/">'
|
||||
+ '<Owner>'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Owner>'
|
||||
+ '<AccessControlList>'
|
||||
+ '<Grant>'
|
||||
+ '<Grantee xsi:type="CanonicalUser">'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>FULL_CONTROL</Permission>'
|
||||
+ '</Grant>'
|
||||
+ '<Grant>'
|
||||
+ '<Grantee xsi:type="Group">'
|
||||
+ `<URI>${constants.publicId}</URI>`
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>READ</Permission>'
|
||||
+ '</Grant>'
|
||||
+ '<Grant>'
|
||||
+ '<Grantee xsi:type="Group">'
|
||||
+ `<URI>${constants.logId}</URI>`
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>WRITE</Permission>'
|
||||
+ '</Grant>'
|
||||
+ '<Grant>'
|
||||
+ '<Grantee xsi:type="AmazonCustomerByEmail">'
|
||||
+ '<EmailAddress>sampleaccount1@sampling.com'
|
||||
+ '</EmailAddress>'
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>WRITE_ACP</Permission>'
|
||||
+ '</Grant>'
|
||||
+ '<Grant>'
|
||||
+ '<Grantee xsi:type="CanonicalUser">'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbacedfd'
|
||||
+ '6e09d98eacf8f8d5218e7cd47ef2bf</ID>'
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>READ_ACP</Permission>'
|
||||
+ '</Grant>'
|
||||
+ '</AccessControlList>'
|
||||
+ '</AccessControlPolicy>',
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
|
@ -362,7 +371,7 @@ describe('putBucketACL API', () => {
|
|||
assert.strictEqual(md.getAcl().READ[0], constants.publicId);
|
||||
assert.strictEqual(md.getAcl().WRITE[0], constants.logId);
|
||||
assert.strictEqual(md.getAcl().WRITE_ACP[0],
|
||||
canonicalIDforSample1);
|
||||
canonicalIDforSample1);
|
||||
assert.strictEqual(md.getAcl().READ_ACP[0],
|
||||
canonicalIDforSample2);
|
||||
done();
|
||||
|
@ -375,17 +384,18 @@ describe('putBucketACL API', () => {
|
|||
bucketName,
|
||||
namespace,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
post: '<AccessControlPolicy xmlns=' +
|
||||
'"http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
'<Owner>' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Owner>' +
|
||||
'<AccessControlList></AccessControlList>' +
|
||||
'</AccessControlPolicy>',
|
||||
post: '<AccessControlPolicy xmlns='
|
||||
+ '"http://s3.amazonaws.com/doc/2006-03-01/">'
|
||||
+ '<Owner>'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Owner>'
|
||||
+ '<AccessControlList></AccessControlList>'
|
||||
+ '</AccessControlPolicy>',
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
|
@ -403,64 +413,66 @@ describe('putBucketACL API', () => {
|
|||
});
|
||||
|
||||
it('should not be able to set ACLs without AccessControlList section',
|
||||
done => {
|
||||
const testACLRequest = {
|
||||
bucketName,
|
||||
namespace,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
post: '<AccessControlPolicy xmlns=' +
|
||||
'"http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
'<Owner>' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Owner>' +
|
||||
'</AccessControlPolicy>',
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
};
|
||||
done => {
|
||||
const testACLRequest = {
|
||||
bucketName,
|
||||
namespace,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
post: '<AccessControlPolicy xmlns='
|
||||
+ '"http://s3.amazonaws.com/doc/2006-03-01/">'
|
||||
+ '<Owner>'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Owner>'
|
||||
+ '</AccessControlPolicy>',
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
assert.deepStrictEqual(err, errors.MalformedACLError);
|
||||
done();
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
assert.deepStrictEqual(err, errors.MalformedACLError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an error if multiple AccessControlList section', done => {
|
||||
const testACLRequest = {
|
||||
bucketName,
|
||||
namespace,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
post: '<AccessControlPolicy xmlns=' +
|
||||
'"http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
'<Owner>' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Owner>' +
|
||||
'<AccessControlList>' +
|
||||
'<Grant>' +
|
||||
'<Grantee xsi:type="CanonicalUser">' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Grantee>' +
|
||||
'<Permission>FULL_CONTROL</Permission>' +
|
||||
'</Grant>' +
|
||||
'</AccessControlList>' +
|
||||
'<AccessControlList>' +
|
||||
'<Grant>' +
|
||||
'<Grantee xsi:type="CanonicalUser">' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Grantee>' +
|
||||
'<Permission>READ</Permission>' +
|
||||
'</Grant>' +
|
||||
'</AccessControlList>' +
|
||||
'</AccessControlPolicy>',
|
||||
post: '<AccessControlPolicy xmlns='
|
||||
+ '"http://s3.amazonaws.com/doc/2006-03-01/">'
|
||||
+ '<Owner>'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Owner>'
|
||||
+ '<AccessControlList>'
|
||||
+ '<Grant>'
|
||||
+ '<Grantee xsi:type="CanonicalUser">'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>FULL_CONTROL</Permission>'
|
||||
+ '</Grant>'
|
||||
+ '</AccessControlList>'
|
||||
+ '<AccessControlList>'
|
||||
+ '<Grant>'
|
||||
+ '<Grantee xsi:type="CanonicalUser">'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>READ</Permission>'
|
||||
+ '</Grant>'
|
||||
+ '</AccessControlList>'
|
||||
+ '</AccessControlPolicy>',
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
|
@ -469,30 +481,31 @@ describe('putBucketACL API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should return an error if invalid grantee user ID ' +
|
||||
'provided in ACL request body', done => {
|
||||
it('should return an error if invalid grantee user ID '
|
||||
+ 'provided in ACL request body', done => {
|
||||
const testACLRequest = {
|
||||
bucketName,
|
||||
namespace,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
post: '<AccessControlPolicy xmlns=' +
|
||||
'"http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
'<Owner>' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Owner>' +
|
||||
'<AccessControlList>' +
|
||||
'<Grant>' +
|
||||
'<Grantee xsi:type="CanonicalUser">' +
|
||||
'<ID>invalid_id</ID>' +
|
||||
'</Grantee>' +
|
||||
'<Permission>READ_ACP</Permission>' +
|
||||
'</Grant>' +
|
||||
'</AccessControlList>' +
|
||||
'</AccessControlPolicy>',
|
||||
post: '<AccessControlPolicy xmlns='
|
||||
+ '"http://s3.amazonaws.com/doc/2006-03-01/">'
|
||||
+ '<Owner>'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Owner>'
|
||||
+ '<AccessControlList>'
|
||||
+ '<Grant>'
|
||||
+ '<Grantee xsi:type="CanonicalUser">'
|
||||
+ '<ID>invalid_id</ID>'
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>READ_ACP</Permission>'
|
||||
+ '</Grant>'
|
||||
+ '</AccessControlList>'
|
||||
+ '</AccessControlPolicy>',
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
return bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
|
@ -501,30 +514,31 @@ describe('putBucketACL API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should return an error if invalid email ' +
|
||||
'address provided in ACLs set out in request body', done => {
|
||||
it('should return an error if invalid email '
|
||||
+ 'address provided in ACLs set out in request body', done => {
|
||||
const testACLRequest = {
|
||||
bucketName,
|
||||
namespace,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
post: '<AccessControlPolicy xmlns=' +
|
||||
'"http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
'<Owner>' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Owner>' +
|
||||
'<AccessControlList>' +
|
||||
'<Grant>' +
|
||||
'<Grantee xsi:type="AmazonCustomerByEmail">' +
|
||||
'<EmailAddress>xyz@amazon.com</EmailAddress>' +
|
||||
'</Grantee>' +
|
||||
'<Permission>WRITE_ACP</Permission>' +
|
||||
'</Grant>' +
|
||||
'</AccessControlList>' +
|
||||
'</AccessControlPolicy>',
|
||||
post: '<AccessControlPolicy xmlns='
|
||||
+ '"http://s3.amazonaws.com/doc/2006-03-01/">'
|
||||
+ '<Owner>'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Owner>'
|
||||
+ '<AccessControlList>'
|
||||
+ '<Grant>'
|
||||
+ '<Grantee xsi:type="AmazonCustomerByEmail">'
|
||||
+ '<EmailAddress>xyz@amazon.com</EmailAddress>'
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>WRITE_ACP</Permission>'
|
||||
+ '</Grant>'
|
||||
+ '</AccessControlList>'
|
||||
+ '</AccessControlPolicy>',
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
assert.deepStrictEqual(err, errors.UnresolvableGrantByEmailAddress);
|
||||
|
@ -542,24 +556,25 @@ describe('putBucketACL API', () => {
|
|||
* "Grant" which is part of the s3 xml scheme for ACLs
|
||||
* so an error should be returned
|
||||
*/
|
||||
post: '<AccessControlPolicy xmlns=' +
|
||||
'"http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
'<Owner>' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Owner>' +
|
||||
'<AccessControlList>' +
|
||||
'<PowerGrant>' +
|
||||
'<Grantee xsi:type="AmazonCustomerByEmail">' +
|
||||
'<EmailAddress>xyz@amazon.com</EmailAddress>' +
|
||||
'</Grantee>' +
|
||||
'<Permission>WRITE_ACP</Permission>' +
|
||||
'</PowerGrant>' +
|
||||
'</AccessControlList>' +
|
||||
'</AccessControlPolicy>',
|
||||
post: '<AccessControlPolicy xmlns='
|
||||
+ '"http://s3.amazonaws.com/doc/2006-03-01/">'
|
||||
+ '<Owner>'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Owner>'
|
||||
+ '<AccessControlList>'
|
||||
+ '<PowerGrant>'
|
||||
+ '<Grantee xsi:type="AmazonCustomerByEmail">'
|
||||
+ '<EmailAddress>xyz@amazon.com</EmailAddress>'
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>WRITE_ACP</Permission>'
|
||||
+ '</PowerGrant>'
|
||||
+ '</AccessControlList>'
|
||||
+ '</AccessControlPolicy>',
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
|
@ -579,32 +594,33 @@ describe('putBucketACL API', () => {
|
|||
* "Grant" which is part of the s3 xml scheme for ACLs
|
||||
* so an error should be returned
|
||||
*/
|
||||
post: '<AccessControlPolicy xmlns=' +
|
||||
'"http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
'<Owner>' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Owner>' +
|
||||
'<AccessControlList>' +
|
||||
'<Grant>' +
|
||||
'<Grantee xsi:type="CanonicalUser">' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Grantee>' +
|
||||
'<Permission>FULL_CONTROL</Permission>' +
|
||||
'</Grant>' +
|
||||
'<PowerGrant>' +
|
||||
'<Grantee xsi:type="AmazonCustomerByEmail">' +
|
||||
'<EmailAddress>xyz@amazon.com</EmailAddress>' +
|
||||
'</Grantee>' +
|
||||
'<Permission>WRITE_ACP</Permission>' +
|
||||
'</PowerGrant>' +
|
||||
'</AccessControlList>' +
|
||||
'</AccessControlPolicy>',
|
||||
post: '<AccessControlPolicy xmlns='
|
||||
+ '"http://s3.amazonaws.com/doc/2006-03-01/">'
|
||||
+ '<Owner>'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Owner>'
|
||||
+ '<AccessControlList>'
|
||||
+ '<Grant>'
|
||||
+ '<Grantee xsi:type="CanonicalUser">'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>FULL_CONTROL</Permission>'
|
||||
+ '</Grant>'
|
||||
+ '<PowerGrant>'
|
||||
+ '<Grantee xsi:type="AmazonCustomerByEmail">'
|
||||
+ '<EmailAddress>xyz@amazon.com</EmailAddress>'
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>WRITE_ACP</Permission>'
|
||||
+ '</PowerGrant>'
|
||||
+ '</AccessControlList>'
|
||||
+ '</AccessControlPolicy>',
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
|
@ -622,24 +638,25 @@ describe('putBucketACL API', () => {
|
|||
// so an error should be returned
|
||||
post: {
|
||||
'<AccessControlPolicy xmlns':
|
||||
'"http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
'<Owner>' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'<Owner>' +
|
||||
'<AccessControlList>' +
|
||||
'<Grant>' +
|
||||
'<Grantee xsi:type="AmazonCustomerByEmail">' +
|
||||
'<EmailAddress>xyz@amazon.com</EmailAddress>' +
|
||||
'<Grantee>' +
|
||||
'<Permission>WRITE_ACP</Permission>' +
|
||||
'<Grant>' +
|
||||
'<AccessControlList>' +
|
||||
'<AccessControlPolicy>',
|
||||
'"http://s3.amazonaws.com/doc/2006-03-01/">'
|
||||
+ '<Owner>'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '<Owner>'
|
||||
+ '<AccessControlList>'
|
||||
+ '<Grant>'
|
||||
+ '<Grantee xsi:type="AmazonCustomerByEmail">'
|
||||
+ '<EmailAddress>xyz@amazon.com</EmailAddress>'
|
||||
+ '<Grantee>'
|
||||
+ '<Permission>WRITE_ACP</Permission>'
|
||||
+ '<Grant>'
|
||||
+ '<AccessControlList>'
|
||||
+ '<AccessControlPolicy>',
|
||||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
|
@ -648,32 +665,33 @@ describe('putBucketACL API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should return an error if invalid group ' +
|
||||
'uri provided in ACLs set out in request body', done => {
|
||||
it('should return an error if invalid group '
|
||||
+ 'uri provided in ACLs set out in request body', done => {
|
||||
const testACLRequest = {
|
||||
bucketName,
|
||||
namespace,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
// URI in grant below is not valid group URI for s3
|
||||
post: '<AccessControlPolicy xmlns=' +
|
||||
'"http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
'<Owner>' +
|
||||
'<ID>79a59df900b949e55d96a1e698fbaced' +
|
||||
'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>' +
|
||||
'<DisplayName>OwnerDisplayName</DisplayName>' +
|
||||
'</Owner>' +
|
||||
'<AccessControlList>' +
|
||||
'<Grant>' +
|
||||
'<Grantee xsi:type="Group">' +
|
||||
'<URI>http://acs.amazonaws.com/groups/' +
|
||||
'global/NOTAVALIDGROUP</URI>' +
|
||||
'</Grantee>' +
|
||||
'<Permission>READ</Permission>' +
|
||||
'</Grant>' +
|
||||
'</AccessControlList>' +
|
||||
'</AccessControlPolicy>',
|
||||
post: '<AccessControlPolicy xmlns='
|
||||
+ '"http://s3.amazonaws.com/doc/2006-03-01/">'
|
||||
+ '<Owner>'
|
||||
+ '<ID>79a59df900b949e55d96a1e698fbaced'
|
||||
+ 'fd6e09d98eacf8f8d5218e7cd47ef2be</ID>'
|
||||
+ '<DisplayName>OwnerDisplayName</DisplayName>'
|
||||
+ '</Owner>'
|
||||
+ '<AccessControlList>'
|
||||
+ '<Grant>'
|
||||
+ '<Grantee xsi:type="Group">'
|
||||
+ '<URI>http://acs.amazonaws.com/groups/'
|
||||
+ 'global/NOTAVALIDGROUP</URI>'
|
||||
+ '</Grantee>'
|
||||
+ '<Permission>READ</Permission>'
|
||||
+ '</Grant>'
|
||||
+ '</AccessControlList>'
|
||||
+ '</AccessControlPolicy>',
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
|
@ -682,19 +700,20 @@ describe('putBucketACL API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should return an error if invalid group uri' +
|
||||
'provided in ACL header request', done => {
|
||||
it('should return an error if invalid group uri'
|
||||
+ 'provided in ACL header request', done => {
|
||||
const testACLRequest = {
|
||||
bucketName,
|
||||
namespace,
|
||||
headers: {
|
||||
'host': `${bucketName}.s3.amazonaws.com`,
|
||||
'x-amz-grant-full-control':
|
||||
'uri="http://acs.amazonaws.com/groups/' +
|
||||
'global/NOTAVALIDGROUP"',
|
||||
'uri="http://acs.amazonaws.com/groups/'
|
||||
+ 'global/NOTAVALIDGROUP"',
|
||||
},
|
||||
url: '/?acl',
|
||||
query: { acl: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
bucketPutACL(authInfo, testACLRequest, log, err => {
|
||||
|
|
|
@ -3,13 +3,13 @@ const { errors } = require('arsenal');
|
|||
|
||||
const { bucketPut } = require('../../../lib/api/bucketPut');
|
||||
const bucketPutCors = require('../../../lib/api/bucketPutCors');
|
||||
const { _validator, parseCorsXml }
|
||||
= require('../../../lib/api/apiUtils/bucket/bucketCors');
|
||||
const { cleanup,
|
||||
const { _validator, parseCorsXml } = require('../../../lib/api/apiUtils/bucket/bucketCors');
|
||||
const {
|
||||
cleanup,
|
||||
DummyRequestLogger,
|
||||
makeAuthInfo,
|
||||
CorsConfigTester }
|
||||
= require('../helpers');
|
||||
CorsConfigTester,
|
||||
} = require('../helpers');
|
||||
const metadata = require('../../../lib/metadata/wrapper');
|
||||
|
||||
const log = new DummyRequestLogger();
|
||||
|
@ -19,6 +19,7 @@ const testBucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
function _testPutBucketCors(authInfo, request, log, errCode, cb) {
|
||||
|
@ -30,13 +31,13 @@ function _testPutBucketCors(authInfo, request, log, errCode, cb) {
|
|||
}
|
||||
|
||||
function _generateSampleXml(value) {
|
||||
const xml = '<CORSConfiguration>' +
|
||||
'<CORSRule>' +
|
||||
'<AllowedMethod>PUT</AllowedMethod>' +
|
||||
'<AllowedOrigin>www.example.com</AllowedOrigin>' +
|
||||
`${value}` +
|
||||
'</CORSRule>' +
|
||||
'</CORSConfiguration>';
|
||||
const xml = '<CORSConfiguration>'
|
||||
+ '<CORSRule>'
|
||||
+ '<AllowedMethod>PUT</AllowedMethod>'
|
||||
+ '<AllowedOrigin>www.example.com</AllowedOrigin>'
|
||||
+ `${value}`
|
||||
+ '</CORSRule>'
|
||||
+ '</CORSConfiguration>';
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
@ -125,8 +126,8 @@ describe('PUT bucket cors :: helper validation functions ', () => {
|
|||
|
||||
it('should return MalformedXML if more than one ID per rule', done => {
|
||||
const testValue = 'testid';
|
||||
const xml = _generateSampleXml(`<ID>${testValue}</ID>` +
|
||||
`<ID>${testValue}</ID>`);
|
||||
const xml = _generateSampleXml(`<ID>${testValue}</ID>`
|
||||
+ `<ID>${testValue}</ID>`);
|
||||
parseCorsXml(xml, log, err => {
|
||||
assert(err, 'Expected error but found none');
|
||||
assert.deepStrictEqual(err, errors.MalformedXML);
|
||||
|
@ -157,8 +158,8 @@ describe('PUT bucket cors :: helper validation functions ', () => {
|
|||
describe('validateMaxAgeSeconds ', () => {
|
||||
it('should validate successfully for valid value', done => {
|
||||
const testValue = 60;
|
||||
const xml = _generateSampleXml(`<MaxAgeSeconds>${testValue}` +
|
||||
'</MaxAgeSeconds>');
|
||||
const xml = _generateSampleXml(`<MaxAgeSeconds>${testValue}`
|
||||
+ '</MaxAgeSeconds>');
|
||||
parseCorsXml(xml, log, (err, result) => {
|
||||
assert.strictEqual(err, null, `Found unexpected err ${err}`);
|
||||
assert.strictEqual(typeof result[0].maxAgeSeconds, 'number');
|
||||
|
@ -167,12 +168,13 @@ describe('PUT bucket cors :: helper validation functions ', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should return MalformedXML if more than one MaxAgeSeconds ' +
|
||||
'per rule', done => {
|
||||
it('should return MalformedXML if more than one MaxAgeSeconds '
|
||||
+ 'per rule', done => {
|
||||
const testValue = '60';
|
||||
const xml = _generateSampleXml(
|
||||
`<MaxAgeSeconds>${testValue}</MaxAgeSeconds>` +
|
||||
`<MaxAgeSeconds>${testValue}</MaxAgeSeconds>`);
|
||||
`<MaxAgeSeconds>${testValue}</MaxAgeSeconds>`
|
||||
+ `<MaxAgeSeconds>${testValue}</MaxAgeSeconds>`,
|
||||
);
|
||||
parseCorsXml(xml, log, err => {
|
||||
assert(err, 'Expected error but found none');
|
||||
assert.deepStrictEqual(err, errors.MalformedXML);
|
||||
|
@ -182,8 +184,8 @@ describe('PUT bucket cors :: helper validation functions ', () => {
|
|||
|
||||
it('should validate & return undefined if empty value', done => {
|
||||
const testValue = '';
|
||||
const xml = _generateSampleXml(`<MaxAgeSeconds>${testValue}` +
|
||||
'</MaxAgeSeconds>');
|
||||
const xml = _generateSampleXml(`<MaxAgeSeconds>${testValue}`
|
||||
+ '</MaxAgeSeconds>');
|
||||
parseCorsXml(xml, log, (err, result) => {
|
||||
assert.strictEqual(err, null, `Found unexpected err ${err}`);
|
||||
assert.strictEqual(result[0].MaxAgeSeconds, undefined);
|
||||
|
|
|
@ -14,6 +14,7 @@ const bucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
describe('bucketPutEncryption API', () => {
|
||||
|
@ -32,25 +33,27 @@ describe('bucketPutEncryption API', () => {
|
|||
|
||||
it('should reject a config with no Rule', done => {
|
||||
bucketPutEncryption(authInfo, templateRequest(bucketName,
|
||||
{ post: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
{
|
||||
post: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
</ServerSideEncryptionConfiguration>`,
|
||||
}), log, err => {
|
||||
assert.strictEqual(err.is.MalformedXML, true);
|
||||
done();
|
||||
});
|
||||
}), log, err => {
|
||||
assert.strictEqual(err.is.MalformedXML, true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject a config with no ApplyServerSideEncryptionByDefault section', done => {
|
||||
bucketPutEncryption(authInfo, templateRequest(bucketName,
|
||||
{ post: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
{
|
||||
post: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Rule></Rule>
|
||||
</ServerSideEncryptionConfiguration>`,
|
||||
}), log, err => {
|
||||
assert.strictEqual(err.is.MalformedXML, true);
|
||||
done();
|
||||
});
|
||||
}), log, err => {
|
||||
assert.strictEqual(err.is.MalformedXML, true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject a config with no SSEAlgorithm', done => {
|
||||
|
@ -155,33 +158,32 @@ describe('bucketPutEncryption API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should update SSEAlgorithm if existing SSEAlgorithm is AES256, ' +
|
||||
'new SSEAlgorithm is aws:kms and no KMSMasterKeyID is provided',
|
||||
done => {
|
||||
const post = templateSSEConfig({ algorithm: 'AES256' });
|
||||
bucketPutEncryption(authInfo, templateRequest(bucketName, { post }), log, err => {
|
||||
it('should update SSEAlgorithm if existing SSEAlgorithm is AES256, '
|
||||
+ 'new SSEAlgorithm is aws:kms and no KMSMasterKeyID is provided',
|
||||
done => {
|
||||
const post = templateSSEConfig({ algorithm: 'AES256' });
|
||||
bucketPutEncryption(authInfo, templateRequest(bucketName, { post }), log, err => {
|
||||
assert.ifError(err);
|
||||
return getSSEConfig(bucketName, log, (err, sseInfo) => {
|
||||
assert.ifError(err);
|
||||
return getSSEConfig(bucketName, log, (err, sseInfo) => {
|
||||
assert.ifError(err);
|
||||
const { masterKeyId } = sseInfo;
|
||||
const newConf = templateSSEConfig({ algorithm: 'aws:kms' });
|
||||
return bucketPutEncryption(authInfo, templateRequest(bucketName, { post: newConf }), log,
|
||||
err => {
|
||||
assert.ifError(err);
|
||||
return getSSEConfig(bucketName, log, (err, updatedSSEInfo) => {
|
||||
assert.deepStrictEqual(updatedSSEInfo, {
|
||||
mandatory: true,
|
||||
algorithm: 'aws:kms',
|
||||
cryptoScheme: 1,
|
||||
masterKeyId,
|
||||
});
|
||||
done();
|
||||
const { masterKeyId } = sseInfo;
|
||||
const newConf = templateSSEConfig({ algorithm: 'aws:kms' });
|
||||
return bucketPutEncryption(authInfo, templateRequest(bucketName, { post: newConf }), log,
|
||||
err => {
|
||||
assert.ifError(err);
|
||||
return getSSEConfig(bucketName, log, (err, updatedSSEInfo) => {
|
||||
assert.deepStrictEqual(updatedSSEInfo, {
|
||||
mandatory: true,
|
||||
algorithm: 'aws:kms',
|
||||
cryptoScheme: 1,
|
||||
masterKeyId,
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should update SSEAlgorithm to aws:kms and set KMSMasterKeyID', done => {
|
||||
const post = templateSSEConfig({ algorithm: 'AES256' });
|
||||
|
|
|
@ -17,6 +17,7 @@ const testBucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
const expectedLifecycleConfig = {
|
||||
|
|
|
@ -15,6 +15,7 @@ const bucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
const expectedNotifConfig = {
|
||||
|
@ -52,6 +53,7 @@ function getNotifRequest(empty) {
|
|||
host: `${bucketName}.s3.amazonaws.com`,
|
||||
},
|
||||
post: notifXml,
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
return putNotifConfigRequest;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ const bucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
const objectLockXml = '<ObjectLockConfiguration ' +
|
||||
|
@ -30,6 +31,7 @@ const putObjLockRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
post: objectLockXml,
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
const expectedObjectLockConfig = {
|
||||
|
|
|
@ -15,6 +15,7 @@ const testBucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
let expectedBucketPolicy = {};
|
||||
|
@ -25,6 +26,7 @@ function getPolicyRequest(policy) {
|
|||
host: `${bucketName}.s3.amazonaws.com`,
|
||||
},
|
||||
post: JSON.stringify(policy),
|
||||
actionImplicitDenies: 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 =
|
||||
{ StringEquals: { 's3:x-amz-acl': ['public-read'] } };
|
||||
bucketPutPolicy(authInfo, getPolicyRequest(expectedBucketPolicy), log,
|
||||
|
|
|
@ -19,6 +19,7 @@ const testBucketPutRequest = {
|
|||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
|
||||
function _getPutWebsiteRequest(xml) {
|
||||
|
@ -29,6 +30,7 @@ function _getPutWebsiteRequest(xml) {
|
|||
},
|
||||
url: '/?website',
|
||||
query: { website: '' },
|
||||
actionImplicitDenies: false,
|
||||
};
|
||||
request.post = xml;
|
||||
return request;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue