diff --git a/lib/api/objectPutRetention.js b/lib/api/objectPutRetention.js index 578af167db..814df67edd 100644 --- a/lib/api/objectPutRetention.js +++ b/lib/api/objectPutRetention.js @@ -1,15 +1,17 @@ const async = require('async'); -const { errors, s3middleware } = require('arsenal'); +const { errors, s3middleware, auth, policies } = require('arsenal'); +const vault = require('../auth/vault'); const { decodeVersionId, getVersionIdResHeader } = require('./apiUtils/object/versioning'); -const { ObjectLockInfo, checkUserGovernanceBypass, hasGovernanceBypassHeader } = +const { validateObjectLockUpdate, objectLockRequiresBypass } = require('./apiUtils/object/objectLockHelpers'); const { metadataValidateBucketAndObj } = require('../metadata/metadataUtils'); const { pushMetric } = require('../utapi/utilities'); const getReplicationInfo = require('./apiUtils/object/getReplicationInfo'); const collectCorsHeaders = require('../utilities/collectCorsHeaders'); const metadata = require('../metadata/wrapper'); +const { config } = require('../Config'); const { parseRetentionXml } = s3middleware.retention; const REPLICATION_ACTION = 'PUT_RETENTION'; @@ -81,31 +83,54 @@ function objectPutRetention(authInfo, request, log, callback) { (err, retentionInfo) => next(err, bucket, retentionInfo, objectMD)); }, (bucket, retentionInfo, objectMD, next) => { - const hasGovernanceBypass = hasGovernanceBypassHeader(request.headers); - if (hasGovernanceBypass && authInfo.isRequesterAnIAMUser()) { - return checkUserGovernanceBypass(request, authInfo, bucket, objectKey, log, err => { - if (err) { - if (err.is.AccessDenied) { - log.debug('user does not have BypassGovernanceRetention and object is locked'); + if (objectLockRequiresBypass(objectMD, retentionInfo) && authInfo.isRequesterAnIAMUser()) { + log.trace('object in GOVERNANCE mode and is user, checking for attached policies', + { method: 'objectPutRetention' }); + const authParams = auth.server.extractParams(request, log, 's3', + request.query); + const ip = policies.requestUtils.getClientIp(request, config); + const requestContextParams = { + constantParams: { + headers: request.headers, + query: request.query, + generalResource: bucketName, + specificResource: { key: objectKey }, + requesterIp: ip, + sslEnabled: request.connection.encrypted, + apiMethod: 'bypassGovernanceRetention', + awsService: 's3', + locationConstraint: bucket.getLocationConstraint(), + requesterInfo: authInfo, + signatureVersion: authParams.params.data.signatureVersion, + authType: authParams.params.data.authType, + signatureAge: authParams.params.data.signatureAge, + }, + }; + return vault.checkPolicies(requestContextParams, + authInfo.getArn(), log, (err, authorizationResults) => { + if (err) { + return next(err); + } + if (authorizationResults[0].isAllowed !== true) { + log.trace('authorization check failed for user', + { + 'method': 'objectPutRetention', + 's3:BypassGovernanceRetention': false, + }); + return next(errors.AccessDenied); } - return next(err, bucket); - } - return next(null, bucket, retentionInfo, hasGovernanceBypass, objectMD); - }); + return next(null, bucket, retentionInfo, objectMD); + }); } - return next(null, bucket, retentionInfo, hasGovernanceBypass, objectMD); + return next(null, bucket, retentionInfo, objectMD); }, - (bucket, retentionInfo, hasGovernanceBypass, objectMD, next) => { - const objLockInfo = new ObjectLockInfo({ - mode: objectMD.retentionMode, - date: objectMD.retentionDate, - legalHold: objectMD.legalHold, - }); - - if (!objLockInfo.canModifyPolicy(retentionInfo, hasGovernanceBypass)) { - return next(errors.AccessDenied, bucket); + (bucket, retentionInfo, objectMD, next) => { + const bypassHeader = request.headers['x-amz-bypass-governance-retention'] || ''; + const bypassGovernance = bypassHeader.toLowerCase() === 'true'; + const validationError = validateObjectLockUpdate(objectMD, retentionInfo, bypassGovernance); + if (validationError) { + return next(validationError, bucket, objectMD); } - return next(null, bucket, retentionInfo, objectMD); }, (bucket, retentionInfo, objectMD, next) => {