Compare commits

...

4 Commits

Author SHA1 Message Date
williamlardier f6ab312c46
fixups 2023-06-20 16:10:01 +02:00
williamlardier 89805212c9
fixup for backbeat route 2023-06-20 15:51:35 +02:00
williamlardier 503dc74f4b
update function calls 2023-06-20 15:50:51 +02:00
williamlardier 65b409fff2
Handle implcit deny results from IAM.
The API is started if IAM returns Allow or an implicit Deny.
In these cases, we add a new boolean to the request that
serves as a context when checking the Bucket/Object ACL or
the Bucket policies. Then, we implement the same authorization
logic as AWS, where an implicit deny from IAM and an Allow from
the Bucket Policy should allow the request.
2023-06-20 15:48:04 +02:00
50 changed files with 114 additions and 88 deletions

View File

@ -139,9 +139,10 @@ const api = {
function checkAuthResults(authResults) {
let returnTagCount = true;
let isImplicitDeny = 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;
}
@ -153,13 +154,18 @@ const api = {
}
} else {
for (let i = 0; i < authResults.length; i++) {
if (!authResults[i].isAllowed) {
if (!authResults[i].isAllowed && !authResults[i].isImplicit) {
// Any explicit deny rejects the current APÏ 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 = false;
}
}
}
return returnTagCount;
return { returnTagCount, isImplicitDeny };
}
return async.waterfall([
@ -237,7 +243,8 @@ const api = {
if (checkedResults instanceof Error) {
return callback(checkedResults);
}
returnTagCount = checkedResults;
returnTagCount = checkedResults.returnTagCount;
request.isImplicitIdentityDeny = checkedResults.isImplicitDeny;
}
if (apiMethod === 'objectPut' || apiMethod === 'objectPutPart') {
request._response = response;

View File

@ -8,7 +8,7 @@ const { allAuthedUsersId, bucketOwnerActions, logId, publicId,
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, requesterIsNotUser) {
if (bucket.getOwner() === canonicalID) {
return true;
}
@ -268,7 +268,18 @@ function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, l
return permission;
}
function isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request) {
function isIdentityAndResourceAuthorized(isImplicitIdentityDeny, resourceAuthzResult) {
// If resourceAuthzResult is true, the the IAM result is either Allow or
// implicit Deny (nothing applicable): in this case, we allow the request.
if (resourceAuthzResult === true) {
return true;
}
// If resourceAuthzResult is false, we only return true if IAM result is
// Allow. Otherwise we default to an implicit Deny.
return isImplicitIdentityDeny === false;
}
function isBucketAuthorized(bucket, requestType, canonicalID, authInfo, isImplicitIdentityDeny, log, request) {
// Check to see if user is authorized to perform a
// particular action on bucket based on ACLs.
// TODO: Add IAM checks
@ -280,32 +291,34 @@ function isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, req
}
// if the bucket owner is an account, users should not have default access
if ((bucket.getOwner() === canonicalID) && requesterIsNotUser) {
return true;
return isIdentityAndResourceAuthorized(isImplicitIdentityDeny, true);
}
const aclPermission = checkBucketAcls(bucket, requestType, canonicalID);
const aclPermission = checkBucketAcls(bucket, requestType, canonicalID, requesterIsNotUser);
const bucketPolicy = bucket.getBucketPolicy();
if (!bucketPolicy) {
return aclPermission;
return isIdentityAndResourceAuthorized(isImplicitIdentityDeny, aclPermission);
}
const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType,
canonicalID, arn, bucket.getOwner(), log, request);
if (bucketPolicyPermission === 'explicitDeny') {
return false;
}
return (aclPermission || (bucketPolicyPermission === 'allow'));
return isIdentityAndResourceAuthorized(isImplicitIdentityDeny,
(aclPermission || (bucketPolicyPermission === 'allow')));
}
function isObjAuthorized(bucket, objectMD, requestType, canonicalID, authInfo, log, request) {
function isObjAuthorized(bucket, objectMD, requestType, canonicalID, authInfo, isImplicitIdentityDeny, 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;
return isIdentityAndResourceAuthorized(isImplicitIdentityDeny, true);
}
// check bucket has read access
// 'bucketGet' covers listObjects and listMultipartUploads, bucket read actions
return isBucketAuthorized(bucket, 'bucketGet', canonicalID, authInfo, log, request);
return isIdentityAndResourceAuthorized(isImplicitIdentityDeny,
isBucketAuthorized(bucket, 'bucketGet', canonicalID, authInfo, log, isImplicitIdentityDeny, request));
}
let requesterIsNotUser = true;
let arn = null;
@ -314,7 +327,7 @@ function isObjAuthorized(bucket, objectMD, requestType, canonicalID, authInfo, l
arn = authInfo.getArn();
}
if (objectMD['owner-id'] === canonicalID && requesterIsNotUser) {
return true;
return isIdentityAndResourceAuthorized(isImplicitIdentityDeny, true);
}
// account is authorized if:
// - requesttype is included in bucketOwnerActions and
@ -323,20 +336,21 @@ function isObjAuthorized(bucket, objectMD, requestType, canonicalID, authInfo, l
if (bucketOwnerActions.includes(requestType)
&& (bucketOwner === canonicalID)
&& requesterIsNotUser) {
return true;
return isIdentityAndResourceAuthorized(isImplicitIdentityDeny, true);
}
const aclPermission = checkObjectAcls(bucket, objectMD, requestType,
canonicalID);
const bucketPolicy = bucket.getBucketPolicy();
if (!bucketPolicy) {
return aclPermission;
return isIdentityAndResourceAuthorized(isImplicitIdentityDeny, aclPermission);
}
const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType,
canonicalID, arn, bucket.getOwner(), log, request);
if (bucketPolicyPermission === 'explicitDeny') {
return false;
}
return (aclPermission || (bucketPolicyPermission === 'allow'));
return isIdentityAndResourceAuthorized(isImplicitIdentityDeny,
(aclPermission || (bucketPolicyPermission === 'allow')));
}
function _checkResource(resource, bucketArn) {

View File

@ -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.isImplicitIdentityDeny : true;
async.waterfall([
function checkDestBucketVal(next) {
metadataValidateBucketAndObj(metadataValParams, log,
metadataValidateBucketAndObj(metadataValParams, authzIdentityResult, log,
(err, destinationBucket) => {
if (err) {
return next(err, destinationBucket);

View File

@ -34,7 +34,7 @@ function bucketDelete(authInfo, request, log, cb) {
request,
};
return metadataValidateBucket(metadataValParams, log,
return metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucketMD) => {
const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucketMD);

View File

@ -26,7 +26,7 @@ function bucketDeleteEncryption(authInfo, request, log, callback) {
};
return async.waterfall([
next => metadataValidateBucket(metadataValParams, log, next),
next => metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, log, next),
(bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => next(err, bucket)),
(bucket, next) => {
const sseConfig = bucket.getServerSideEncryption();

View File

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

View File

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

View File

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

View File

@ -351,7 +351,7 @@ function bucketGet(authInfo, request, log, callback) {
listParams.marker = params.marker;
}
metadataValidateBucket(metadataValParams, log, (err, bucket) => {
metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket);
if (err) {

View File

@ -55,7 +55,7 @@ function bucketGetACL(authInfo, request, log, callback) {
},
};
metadataValidateBucket(metadataValParams, log, (err, bucket) => {
metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket);
if (err) {

View File

@ -27,7 +27,7 @@ function bucketGetEncryption(authInfo, request, log, callback) {
};
return async.waterfall([
next => metadataValidateBucket(metadataValParams, log, next),
next => metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, 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

View File

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

View File

@ -41,7 +41,7 @@ function bucketGetNotification(authInfo, request, log, callback) {
request,
};
return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
return metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
if (err) {
log.debug('error processing request', {

View File

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

View File

@ -21,7 +21,7 @@ function bucketGetPolicy(authInfo, request, log, callback) {
request,
};
return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
return metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
if (err) {
log.debug('error processing request', {

View File

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

View File

@ -58,7 +58,7 @@ function bucketGetVersioning(authInfo, request, log, callback) {
request,
};
metadataValidateBucket(metadataValParams, log, (err, bucket) => {
metadataValidateBucket(metadataValParams, log, request.isImplicitIdentityDeny, (err, bucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket);
if (err) {

View File

@ -22,7 +22,7 @@ function bucketHead(authInfo, request, log, callback) {
requestType: 'bucketHead',
request,
};
metadataValidateBucket(metadataValParams, log, (err, bucket) => {
metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket);
if (err) {

View File

@ -105,7 +105,7 @@ function bucketPutACL(authInfo, request, log, callback) {
return async.waterfall([
function waterfall1(next) {
metadataValidateBucket(metadataValParams, log,
metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket) => {
if (err) {
log.trace('request authorization failed', {

View File

@ -28,7 +28,7 @@ function bucketPutEncryption(authInfo, request, log, callback) {
};
return async.waterfall([
next => metadataValidateBucket(metadataValParams, log, next),
next => metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, log, next),
(bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => next(err, bucket)),
(bucket, next) => {
log.trace('parsing encryption config', { method: 'bucketPutEncryption' });

View File

@ -43,7 +43,7 @@ function bucketPutLifecycle(authInfo, request, log, callback) {
return next(null, configObj);
});
},
(lcConfig, next) => metadataValidateBucket(metadataValParams, log,
(lcConfig, next) => metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket) => {
if (err) {
return next(err, bucket);

View File

@ -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.isImplicitIdentityDeny, log,
(err, bucket) => next(err, bucket, notifConfig)),
(bucket, notifConfig, next) => {
bucket.setNotificationConfiguration(notifConfig);

View File

@ -41,7 +41,7 @@ function bucketPutObjectLock(authInfo, request, log, callback) {
return next(configObj.error || null, configObj);
});
},
(objectLockConfig, next) => metadataValidateBucket(metadataValParams,
(objectLockConfig, next) => metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny,
log, (err, bucket) => {
if (err) {
return next(err, bucket);

View File

@ -70,7 +70,7 @@ function bucketPutPolicy(authInfo, request, log, callback) {
return next(null, bucketPolicy);
});
},
(bucketPolicy, next) => metadataValidateBucket(metadataValParams, log,
(bucketPolicy, next) => metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket) => {
if (err) {
return next(err, bucket);

View File

@ -37,7 +37,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.isImplicitIdentityDeny, log, (err, bucket) => {
if (err) {
return next(err);
}

View File

@ -42,7 +42,7 @@ function bucketPutTagging(authInfo, request, log, callback) {
};
let bucket = null;
return waterfall([
next => metadataValidateBucket(metadataValParams, log,
next => metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, log,
(err, b) => {
bucket = b;
return next(err);

View File

@ -94,7 +94,7 @@ function bucketPutVersioning(authInfo, request, log, callback) {
return waterfall([
next => _parseXML(request, log, next),
next => metadataValidateBucket(metadataValParams, log,
next => metadataValidateBucket(metadataValParams, request.isImplicitIdentityDeny, 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

View File

@ -120,7 +120,7 @@ function completeMultipartUpload(authInfo, request, log, callback) {
// at the destinationBucket level are same as objectPut
requestType: 'objectPut',
};
metadataValidateBucketAndObj(metadataValParams, log, next);
metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log, next);
},
function validateMultipart(destBucket, objMD, next) {
if (objMD) {

View File

@ -262,7 +262,7 @@ function initiateMultipartUpload(authInfo, request, log, callback) {
}
async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log,
next => metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log,
(error, destinationBucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, destinationBucket);

View File

@ -105,7 +105,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.isImplicitIdentityDeny, log,
(err, bucket) => next(err, bucket));
},
function getMPUBucket(bucket, next) {

View File

@ -115,7 +115,7 @@ function listParts(authInfo, request, log, callback) {
async.waterfall([
function checkDestBucketVal(next) {
metadataValidateBucketAndObj(metadataValParams, log,
metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log,
(err, destinationBucket) => {
if (err) {
return next(err, destinationBucket, null);

View File

@ -249,7 +249,7 @@ function objectCopy(authInfo, request, sourceBucket,
}
return async.waterfall([
function checkDestAuth(next) {
return metadataValidateBucketAndObj(valPutParams, log,
return metadataValidateBucketAndObj(valPutParams, request.isImplicitIdentityDeny, log,
(err, destBucketMD, destObjMD) => {
if (err) {
log.debug('error validating put part of request',
@ -267,7 +267,7 @@ function objectCopy(authInfo, request, sourceBucket,
});
},
function checkSourceAuthorization(destBucketMD, destObjMD, next) {
return metadataValidateBucketAndObj(valGetParams, log,
return metadataValidateBucketAndObj(valGetParams, request.isImplicitIdentityDeny, log,
(err, sourceBucketMD, sourceObjMD) => {
if (err) {
log.debug('error validating get part of request',

View File

@ -59,7 +59,7 @@ function objectDelete(authInfo, request, log, cb) {
const canonicalID = authInfo.getCanonicalID();
return async.waterfall([
function validateBucketAndObj(next) {
return metadataValidateBucketAndObj(valParams, log,
return metadataValidateBucketAndObj(valParams, request.isImplicitIdentityDeny, log,
(err, bucketMD, objMD) => {
if (err) {
return next(err, bucketMD);

View File

@ -49,7 +49,7 @@ function objectDeleteTagging(authInfo, request, log, callback) {
};
return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log,
next => metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket, objectMD) => {
if (err) {
log.trace('request authorization failed',

View File

@ -50,7 +50,7 @@ function objectGet(authInfo, request, returnTagCount, log, callback) {
request,
};
return metadataValidateBucketAndObj(mdValParams, log,
return metadataValidateBucketAndObj(mdValParams, request.isImplicitIdentityDeny, log,
(err, bucket, objMD) => {
const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket);

View File

@ -74,7 +74,7 @@ function objectGetACL(authInfo, request, log, callback) {
return async.waterfall([
function validateBucketAndObj(next) {
return metadataValidateBucketAndObj(metadataValParams, log,
return metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket, objectMD) => {
if (err) {
log.trace('request authorization failed',

View File

@ -45,7 +45,7 @@ function objectGetLegalHold(authInfo, request, log, callback) {
};
return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log,
next => metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket, objectMD) => {
if (err) {
log.trace('request authorization failed',

View File

@ -45,7 +45,7 @@ function objectGetRetention(authInfo, request, log, callback) {
};
return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log,
next => metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket, objectMD) => {
if (err) {
log.trace('request authorization failed',

View File

@ -46,7 +46,7 @@ function objectGetTagging(authInfo, request, log, callback) {
};
return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log,
next => metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket, objectMD) => {
if (err) {
log.trace('request authorization failed',

View File

@ -50,7 +50,7 @@ function objectHead(authInfo, request, log, callback) {
request,
};
return metadataValidateBucketAndObj(mdValParams, log,
return metadataValidateBucketAndObj(mdValParams, request.isImplicitIdentityDeny, log,
(err, bucket, objMD) => {
const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket);

View File

@ -71,7 +71,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.isImplicitIdentityDeny, log,
(err, bucket, objMD) => {
const responseHeaders = collectCorsHeaders(headers.origin,
method, bucket);

View File

@ -112,7 +112,7 @@ function objectPutACL(authInfo, request, log, cb) {
return async.waterfall([
function validateBucketAndObj(next) {
return metadataValidateBucketAndObj(metadataValParams, log,
return metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket, objectMD) => {
if (err) {
return next(err);

View File

@ -93,7 +93,7 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
return async.waterfall([
function checkDestAuth(next) {
return metadataValidateBucketAndObj(valPutParams, log,
return metadataValidateBucketAndObj(valPutParams, request.isImplicitIdentityDeny, log,
(err, destBucketMD) => {
if (err) {
log.debug('error validating authorization for ' +
@ -112,7 +112,7 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
});
},
function checkSourceAuthorization(destBucketMD, next) {
return metadataValidateBucketAndObj(valGetParams, log,
return metadataValidateBucketAndObj(valGetParams, request.isImplicitIdentityDeny, log,
(err, sourceBucketMD, sourceObjMD) => {
if (err) {
log.debug('error validating get part of request',

View File

@ -48,7 +48,7 @@ function objectPutLegalHold(authInfo, request, log, callback) {
};
return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log,
next => metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket, objectMD) => {
if (err) {
log.trace('request authorization failed',

View File

@ -49,7 +49,7 @@ function objectPutRetention(authInfo, request, log, callback) {
};
return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log,
next => metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket, objectMD) => {
if (err) {
log.trace('request authorization failed',

View File

@ -50,7 +50,7 @@ function objectPutTagging(authInfo, request, log, callback) {
};
return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log,
next => metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket, objectMD) => {
if (err) {
log.trace('request authorization failed',

View File

@ -94,6 +94,7 @@ function metadataGetObject(bucketName, objectKey, versionId, log, cb) {
* @param {string} params.requestType - type of request
* @param {string} [params.preciseRequestType] - precise type of request
* @param {object} params.request - http request object
* @param {object} isImplicitIdentityDeny - identity authorization results
* @param {RequestLogger} log - request logger
* @return {ArsenalError|null} returns a validation error, or null if validation OK
* The following errors may be returned:
@ -102,7 +103,7 @@ function metadataGetObject(bucketName, objectKey, versionId, log, cb) {
* bucket policy operation
* - AccessDenied: bucket is not authorized
*/
function validateBucket(bucket, params, log) {
function validateBucket(bucket, params, isImplicitIdentityDeny, log) {
const { authInfo, requestType, preciseRequestType, request } = params;
if (bucketShield(bucket, requestType)) {
log.debug('bucket is shielded from request', {
@ -119,7 +120,7 @@ function validateBucket(bucket, params, log) {
return errors.MethodNotAllowed;
}
if (!isBucketAuthorized(bucket, (preciseRequestType || requestType), canonicalID,
authInfo, log, request)) {
authInfo, isImplicitIdentityDeny, log, request)) {
log.debug('access denied for user on bucket', { requestType });
return errors.AccessDenied;
}
@ -135,11 +136,12 @@ function validateBucket(bucket, params, log) {
* @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} isImplicitIdentityDeny - 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) {
function metadataValidateBucketAndObj(params, isImplicitIdentityDeny, log, callback) {
const { authInfo, bucketName, objectKey, versionId, getDeleteMarker,
requestType, request } = params;
async.waterfall([
@ -161,7 +163,7 @@ function metadataValidateBucketAndObj(params, log, callback) {
});
return next(errors.NoSuchBucket);
}
const validationError = validateBucket(bucket, params, log);
const validationError = validateBucket(bucket, params, isImplicitIdentityDeny, log);
if (validationError) {
return next(validationError, bucket);
}
@ -174,7 +176,8 @@ function metadataValidateBucketAndObj(params, log, callback) {
},
(bucket, objMD, next) => {
const canonicalID = authInfo.getCanonicalID();
if (!isObjAuthorized(bucket, objMD, requestType, canonicalID, authInfo, log, request)) {
if (!isObjAuthorized(bucket, objMD, requestType, canonicalID, authInfo, isImplicitIdentityDeny,
log, request)) {
log.debug('access denied for user on object', { requestType });
return next(errors.AccessDenied, bucket);
}
@ -196,18 +199,19 @@ function metadataValidateBucketAndObj(params, log, callback) {
* @param {string} params.bucketName - name of bucket
* @param {string} params.requestType - type of request
* @param {string} params.request - http request object
* @param {boolean} isImplicitIdentityDeny - 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) {
function metadataValidateBucket(params, isImplicitIdentityDeny, log, callback) {
const { bucketName } = params;
return metadata.getBucket(bucketName, log, (err, bucket) => {
if (err) {
log.debug('metadata getbucket failed', { error: err });
return callback(err);
}
const validationError = validateBucket(bucket, params, log);
const validationError = validateBucket(bucket, params, isImplicitIdentityDeny, log);
return callback(validationError, bucket);
});
}

View File

@ -1271,7 +1271,7 @@ function routeBackbeat(clientIP, request, response, log) {
requestType: 'ReplicateObject',
request,
};
return metadataValidateBucketAndObj(mdValParams, log, next);
return metadataValidateBucketAndObj(mdValParams, false, log, next);
},
(bucketInfo, objMd, next) => {
if (useMultipleBackend) {

View File

@ -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, true, 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, true, 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, true, 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, true, 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, true, 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, true, 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, true, 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, true, 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, true, 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, true, 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, true, 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, true, 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, true, 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, true, 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, true, 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, true, log);
assert.equal(allowed, false);
done();
});

View File

@ -48,7 +48,7 @@ describe('validateBucket', () => {
authInfo: otherAuthInfo,
requestType: 'bucketGet',
request: null,
}, log);
}, true, log);
assert(validationResult);
assert(validationResult.is.AccessDenied);
});