Compare commits

...

5 Commits

Author SHA1 Message Date
williamlardier 70e9228c4f
testing 2023-07-18 13:22:48 +02:00
williamlardier cf230e7c07
test bb 2023-07-11 11:44:35 +02:00
williamlardier 7ccf386da5
wip 2023-07-11 11:05:45 +02:00
williamlardier d596264f01
wip 2023-07-11 10:11:07 +02:00
williamlardier 9a29d19a5a
Handle implcit deny results from IAM.
The API is started if IAM returns Allow or an implicit Deny.
In these cases, we add a new boolean to the request that
serves as a context when checking the Bucket/Object ACL or
the Bucket policies. Then, we implement the same authorization
logic as AWS, where an implicit deny from IAM and an Allow from
the Bucket Policy should allow the request.
2023-07-10 10:24:24 +02:00
58 changed files with 284 additions and 113 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,22 @@ 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;
// Specific APIs are not checking any ACLs or Bucket Policies.
if (apiMethod === 'bucketPut' || apiMethod === 'serviceGet' && isImplicitDeny) {
return errors.AccessDenied;
}
return { returnTagCount, isImplicitDeny };
}
return async.waterfall([
@ -237,7 +247,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

@ -1,5 +1,6 @@
const { evaluators, actionMaps, RequestContext } = require('arsenal').policies;
const { evaluators, actionMaps, RequestContext, requestUtils } = require('arsenal').policies;
const constants = require('../../../../constants');
const { config } = require('../../../Config');
const { allAuthedUsersId, bucketOwnerActions, logId, publicId,
assumedRoleArnResourceType, backbeatLifecycleSessionName } = constants;
@ -198,6 +199,20 @@ function _checkBucketPolicyResources(request, resource, log) {
return evaluators.isResourceApplicable(requestContext, resource, log);
}
function _checkBucketPolicyConditions(request, conditions, log) {
const ip = request ? requestUtils.getClientIp(request, config) : undefined;
if (!conditions) {
return true;
}
// build request context from the request!
const requestContext = new RequestContext(request.headers, request.query,
request.bucketName, request.objectKey, ip,
request.connection.encrypted, request.resourceType, 's3', null, null,
null, null, null, null, null, null, null, null, null,
request.objectLockRetentionDays);
return evaluators.meetConditions(requestContext, conditions, log);
}
function _getAccountId(arn) {
// account or user arn is of format 'arn:aws:iam::<12-digit-acct-id>:etc...
return arn.substr(13, 12);
@ -255,12 +270,13 @@ function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, l
const principalMatch = _checkPrincipals(canonicalID, arn, s.Principal);
const actionMatch = _checkBucketPolicyActions(requestType, s.Action, log);
const resourceMatch = _checkBucketPolicyResources(request, s.Resource, log);
const conditionsMatch = _checkBucketPolicyConditions(request, s.Condition, log);
if (principalMatch && actionMatch && resourceMatch && s.Effect === 'Deny') {
if (principalMatch && actionMatch && resourceMatch && conditionsMatch && s.Effect === 'Deny') {
// explicit deny trumps any allows, so return immediately
return 'explicitDeny';
}
if (principalMatch && actionMatch && resourceMatch && s.Effect === 'Allow') {
if (principalMatch && actionMatch && resourceMatch && conditionsMatch && s.Effect === 'Allow') {
permission = 'allow';
}
copiedStatement = copiedStatement.splice(1);
@ -268,7 +284,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 +307,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, isImplicitIdentityDeny, log, request));
}
let requesterIsNotUser = true;
let arn = null;
@ -314,7 +343,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 +352,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

@ -30,7 +30,7 @@ async function bucketDeleteTagging(authInfo, request, log, callback) {
};
try {
bucket = await metadataValidateBucketPromise(metadataValParams, log);
bucket = await metadataValidateBucketPromise(metadataValParams, request.isImplicitIdentityDeny, log);
bucket.setTags([]);
// eslint-disable-next-line no-unused-expressions
await updateBucketPromise(bucket.getName(), bucket, log);

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

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

View File

@ -58,7 +58,7 @@ function bucketGetVersioning(authInfo, request, log, callback) {
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

@ -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

@ -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"');
}
@ -70,7 +69,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,22 @@ function objectPutRetention(authInfo, request, log, callback) {
};
return async.waterfall([
next => metadataValidateBucketAndObj(metadataValParams, log,
next => {
log.trace('parsing retention information');
parseRetentionXml(request.post, log,
(err, retentionInfo) => {
if (err) {
log.trace('error parsing retention information',
{ error: err });
return next(err);
}
const remainingDays = Math.ceil(
(new Date(retentionInfo.date) - Date.now()) / (1000 * 3600 * 24));
metadataValParams.request.objectLockRetentionDays = remainingDays;
return next(null, retentionInfo);
});
},
(retentionInfo, next) => metadataValidateBucketAndObj(metadataValParams, request.isImplicitIdentityDeny, log,
(err, bucket, objectMD) => {
if (err) {
log.trace('request authorization failed',
@ -77,13 +92,8 @@ function objectPutRetention(authInfo, request, log, callback) {
'Bucket is missing Object Lock Configuration'
), bucket);
}
return next(null, bucket, objectMD);
return next(null, bucket, retentionInfo, objectMD);
}),
(bucket, objectMD, next) => {
log.trace('parsing retention information');
parseRetentionXml(request.post, log,
(err, retentionInfo) => next(err, bucket, retentionInfo, objectMD));
},
(bucket, retentionInfo, objectMD, next) => {
const hasGovernanceBypass = hasGovernanceBypassHeader(request.headers);
if (hasGovernanceBypass && authInfo.isRequesterAnIAMUser()) {

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, request.isImplicitIdentityDeny, log, next);
},
(bucketInfo, objMd, next) => {
if (useMultipleBackend) {

View File

@ -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.70.4",
"arsenal": "git+https://github.com/scality/arsenal#f4894a6d6ebb36ba1a559de4811180cb942d55a7",
"async": "~2.5.0",
"aws-sdk": "2.905.0",
"azure-storage": "^2.1.0",

View File

@ -68,6 +68,7 @@ describe('GET object retention', () => {
afterEach(() => {
process.stdout.write('Removing object lock\n');
console.log(changeLockPromise, bucketName, objectName, versionId, changeObjectLock)
return changeLockPromise([{ bucket: bucketName, key: objectName, versionId }], '')
.then(() => {
process.stdout.write('Emptying and deleting buckets\n');
@ -81,7 +82,7 @@ describe('GET object retention', () => {
});
});
it('should return AccessDenied putting retention with another account',
it.only('should return AccessDenied putting retention with another account',
done => {
otherAccountS3.getObjectRetention({
Bucket: bucketName,

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

@ -76,7 +76,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,

View File

@ -456,7 +456,7 @@ describe('without object metadata', () => {
bucket.setCannedAcl(value.canned);
const results = requestTypes.map(type =>
isObjAuthorized(bucket, null, type, value.id, authInfoUser, log));
isObjAuthorized(bucket, null, type, value.id, authInfoUser, true, log));
assert.deepStrictEqual(results, value.response);
done();
});

View File

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

View File

@ -10,6 +10,7 @@ const versionIdUtils = versioning.VersionID;
const log = new DummyRequestLogger();
function changeObjectLock(objects, newConfig, cb) {
console.log('>W>>>', metadata, objects, newConfig, metadata.setup, metadata.client)
async.each(objects, (object, next) => {
const { bucket, key, versionId } = object;
metadataGetObject(bucket, key, versionIdUtils.decode(versionId), log, (err, objMD) => {

132
yarn.lock
View File

@ -129,6 +129,11 @@
resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz#5981a8db18b56ba38ef0efb7d995b12aa7b51918"
integrity sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==
"@socket.io/component-emitter@~3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
"@tootallnate/once@1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
@ -139,6 +144,23 @@
resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.15.tgz#26d4768fdda0e466f18d6c9918ca28cc89a4e1fe"
integrity sha512-PAmPfzvFA31mRoqZyTVsgJMsvbynR429UTTxhmfsUCrWGh3/fxOrzqBtaTPJsn4UtzTv4Vb0+/O7CARWb69N4g==
"@types/cookie@^0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d"
integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==
"@types/cors@^2.8.12":
version "2.8.13"
resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.13.tgz#b8ade22ba455a1b8cb3b5d3f35910fd204f84f94"
integrity sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==
dependencies:
"@types/node" "*"
"@types/node@*", "@types/node@>=10.0.0":
version "20.4.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.1.tgz#a6033a8718653c50ac4962977e14d0f984d9527d"
integrity sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==
"@types/utf8@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@types/utf8/-/utf8-3.0.1.tgz#bf081663d4fff05ee63b41f377a35f8b189f7e5b"
@ -426,9 +448,9 @@ arraybuffer.slice@~0.0.7:
optionalDependencies:
ioctl "^2.0.2"
"arsenal@git+https://github.com/scality/arsenal#7.70.4":
version "7.70.4"
resolved "git+https://github.com/scality/arsenal#c4cc5a2c3dfa4a8d6d565c4029ec05cbb0bf1a3e"
"arsenal@git+https://github.com/scality/arsenal#f4894a6d6ebb36ba1a559de4811180cb942d55a7":
version "7.70.6"
resolved "git+https://github.com/scality/arsenal#f4894a6d6ebb36ba1a559de4811180cb942d55a7"
dependencies:
"@types/async" "^3.2.12"
"@types/utf8" "^3.0.1"
@ -456,8 +478,8 @@ arraybuffer.slice@~0.0.7:
node-forge "^0.7.1"
prom-client "14.2.0"
simple-glob "^0.2"
socket.io "~2.3.0"
socket.io-client "~2.3.0"
socket.io "~4.6.1"
socket.io-client "~4.6.1"
sproxydclient "github:scality/sproxydclient#8.0.4"
utf8 "2.1.2"
uuid "^3.0.1"
@ -712,7 +734,7 @@ base64-js@^1.0.2, base64-js@^1.3.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
base64id@2.0.0:
base64id@2.0.0, base64id@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6"
integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==
@ -1150,6 +1172,11 @@ cookie@0.5.0:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
cookie@~0.4.1:
version "0.4.2"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
core-js@^2.4.0:
version "2.6.12"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
@ -1165,6 +1192,14 @@ core-util-is@~1.0.0:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
cors@~2.8.5:
version "2.8.5"
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"
cron-parser@^2.11.0, cron-parser@^2.15.0, cron-parser@^2.18.0:
version "2.18.0"
resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.18.0.tgz#de1bb0ad528c815548371993f81a54e5a089edcf"
@ -1228,7 +1263,7 @@ debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.6.8, debug@~2.6.9:
dependencies:
ms "2.0.0"
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3:
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@~4.3.1, debug@~4.3.2:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@ -1423,6 +1458,17 @@ engine.io-client@~3.4.0:
xmlhttprequest-ssl "~1.5.4"
yeast "0.1.2"
engine.io-client@~6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.4.0.tgz#88cd3082609ca86d7d3c12f0e746d12db4f47c91"
integrity sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.1"
engine.io-parser "~5.0.3"
ws "~8.11.0"
xmlhttprequest-ssl "~2.0.0"
engine.io-parser@~2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.1.tgz#57ce5611d9370ee94f99641b589f94c97e4f5da7"
@ -1434,6 +1480,11 @@ engine.io-parser@~2.2.0:
blob "0.0.5"
has-binary2 "~1.0.2"
engine.io-parser@~5.0.3:
version "5.0.7"
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.7.tgz#ed5eae76c71f398284c578ab6deafd3ba7e4e4f6"
integrity sha512-P+jDFbvK6lE3n1OL+q9KuzdOFWkkZ/cMV9gol/SbVfpyqfvrfrFTOFJ6fQm2VC3PZHlU3QPhVwmbsCnauHF2MQ==
engine.io@~3.4.0:
version "3.4.2"
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.4.2.tgz#8fc84ee00388e3e228645e0a7d3dfaeed5bd122c"
@ -1446,6 +1497,22 @@ engine.io@~3.4.0:
engine.io-parser "~2.2.0"
ws "^7.1.2"
engine.io@~6.4.2:
version "6.4.2"
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.4.2.tgz#ffeaf68f69b1364b0286badddf15ff633476473f"
integrity sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==
dependencies:
"@types/cookie" "^0.4.1"
"@types/cors" "^2.8.12"
"@types/node" ">=10.0.0"
accepts "~1.3.4"
base64id "2.0.0"
cookie "~0.4.1"
cors "~2.8.5"
debug "~4.3.1"
engine.io-parser "~5.0.3"
ws "~8.11.0"
entities@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
@ -3839,7 +3906,7 @@ oauth-sign@~0.9.0:
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
object-assign@^4.0.1, object-assign@^4.1.0:
object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
@ -4585,6 +4652,13 @@ socket.io-adapter@~1.1.0:
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9"
integrity sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==
socket.io-adapter@~2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12"
integrity sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==
dependencies:
ws "~8.11.0"
socket.io-client@2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.0.tgz#14d5ba2e00b9bcd145ae443ab96b3f86cbcc1bb4"
@ -4622,6 +4696,16 @@ socket.io-client@~2.3.0:
socket.io-parser "~3.3.0"
to-array "0.1.4"
socket.io-client@~4.6.1:
version "4.6.2"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.6.2.tgz#2bfde952e74625d54e622718a7cb1d591ee62fd6"
integrity sha512-OwWrMbbA8wSqhBAR0yoPK6EdQLERQAYjXb3A0zLpgxfM1ZGLKoxHx8gVmCHA6pcclRX5oA/zvQf7bghAS11jRA==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.2"
engine.io-client "~6.4.0"
socket.io-parser "~4.2.4"
socket.io-parser@~3.3.0:
version "3.3.2"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.2.tgz#ef872009d0adcf704f2fbe830191a14752ad50b6"
@ -4640,6 +4724,14 @@ socket.io-parser@~3.4.0:
debug "~4.1.0"
isarray "2.0.1"
socket.io-parser@~4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83"
integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.1"
socket.io@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.3.0.tgz#cd762ed6a4faeca59bc1f3e243c0969311eb73fb"
@ -4652,6 +4744,18 @@ socket.io@~2.3.0:
socket.io-client "2.3.0"
socket.io-parser "~3.4.0"
socket.io@~4.6.1:
version "4.6.2"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.6.2.tgz#d597db077d4df9cbbdfaa7a9ed8ccc3d49439786"
integrity sha512-Vp+lSks5k0dewYTfwgPT9UeGGd+ht7sCpB7p0e83VgO4X/AHYWhXITMrNk/pg8syY2bpx23ptClCQuHhqi2BgQ==
dependencies:
accepts "~1.3.4"
base64id "~2.0.0"
debug "~4.3.2"
engine.io "~6.4.2"
socket.io-adapter "~2.5.2"
socket.io-parser "~4.2.4"
socks-proxy-agent@^6.0.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce"
@ -5251,7 +5355,7 @@ validator@^13.0.0, validator@^13.6.0, validator@^13.7.0:
resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857"
integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==
vary@~1.1.2:
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
@ -5414,6 +5518,11 @@ ws@~6.1.0:
dependencies:
async-limiter "~1.0.0"
ws@~8.11.0:
version "8.11.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
xml2js@0.4.19:
version "0.4.19"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
@ -5457,6 +5566,11 @@ xmlhttprequest-ssl@~1.5.4:
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
integrity sha512-/bFPLUgJrfGUL10AIv4Y7/CUt6so9CLtB/oFxQSHseSDNNCdC6vwwKEqwLN6wNPBg9YWXAiMu8jkf6RPRS/75Q==
xmlhttprequest-ssl@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"