mirror of
https://github.com/versity/versitygw.git
synced 2026-05-01 17:55:42 +00:00
fix: check PutObjectTagging/LegalHold/Retention permissions on PutObject,CopyObject and CreateMultipartUpload
Fixes #1986 When a client includes tagging, legal hold, or retention headers in a PutObject, CopyObject or CreateMultipartUpload request, the corresponding bucket policy permissions must be verified in addition to s3:PutObject: `X-Amz-Tagging` - `s3:PutObjectTagging` `X-Amz-Object-Lock-Legal-Hold` - `s3:PutObjectLegalHold` `X-Amz-Object-Lock-Mode` - `s3:PutObjectRetention` Previously, only s3:PutObject was checked, allowing users to set tagging, legal hold, and retention without having the required permissions. Now each action permission is check, if user tries to add them. For CopyObject these permissions are checked on destination object.
This commit is contained in:
@@ -61,7 +61,7 @@ func VerifyObjectCopyAccess(ctx context.Context, be backend.Backend, copySource
|
||||
Acc: opts.Acc,
|
||||
Bucket: srcBucket,
|
||||
Object: srcObject,
|
||||
Action: GetObjectAction,
|
||||
Actions: []Action{GetObjectAction},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -76,7 +76,7 @@ type AccessOptions struct {
|
||||
Acc Account
|
||||
Bucket string
|
||||
Object string
|
||||
Action Action
|
||||
Actions []Action
|
||||
Readonly bool
|
||||
IsPublicRequest bool
|
||||
DisableACL bool
|
||||
@@ -105,7 +105,7 @@ func VerifyAccess(ctx context.Context, be backend.Backend, opts AccessOptions) e
|
||||
return policyErr
|
||||
}
|
||||
} else {
|
||||
return VerifyBucketPolicy(policy, opts.Acc.Access, opts.Bucket, opts.Object, opts.Action)
|
||||
return VerifyBucketPolicy(policy, opts.Acc.Access, opts.Bucket, opts.Object, opts.Actions...)
|
||||
}
|
||||
|
||||
if err := verifyACL(opts.Acl, opts.Acc.Access, opts.AclPermission, opts.DisableACL); err != nil {
|
||||
|
||||
@@ -234,7 +234,11 @@ func ValidatePolicyDocument(policyBin []byte, bucket string, iam IAMService) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyBucketPolicy(policy []byte, access, bucket, object string, action Action) error {
|
||||
func VerifyBucketPolicy(policy []byte, access, bucket, object string, actions ...Action) error {
|
||||
if len(actions) == 0 {
|
||||
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||
}
|
||||
|
||||
var bucketPolicy BucketPolicy
|
||||
if err := json.Unmarshal(policy, &bucketPolicy); err != nil {
|
||||
return fmt.Errorf("failed to parse the bucket policy: %w", err)
|
||||
@@ -245,8 +249,10 @@ func VerifyBucketPolicy(policy []byte, access, bucket, object string, action Act
|
||||
resource += "/" + object
|
||||
}
|
||||
|
||||
if !bucketPolicy.isAllowed(access, action, resource) {
|
||||
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||
for _, action := range actions {
|
||||
if !bucketPolicy.isAllowed(access, action, resource) {
|
||||
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -37,7 +37,7 @@ func (c S3ApiController) DeleteBucketTagging(ctx *fiber.Ctx) (*Response, error)
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketTaggingAction,
|
||||
Actions: []auth.Action{auth.PutBucketTaggingAction},
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -72,7 +72,7 @@ func (c S3ApiController) DeleteBucketOwnershipControls(ctx *fiber.Ctx) (*Respons
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketOwnershipControlsAction,
|
||||
Actions: []auth.Action{auth.PutBucketOwnershipControlsAction},
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -106,7 +106,7 @@ func (c S3ApiController) DeleteBucketPolicy(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.DeleteBucketPolicyAction,
|
||||
Actions: []auth.Action{auth.DeleteBucketPolicyAction},
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -141,7 +141,7 @@ func (c S3ApiController) DeleteBucketCors(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketCorsAction,
|
||||
Actions: []auth.Action{auth.PutBucketCorsAction},
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -177,7 +177,7 @@ func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.DeleteBucketAction,
|
||||
Actions: []auth.Action{auth.DeleteBucketAction},
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
|
||||
@@ -39,7 +39,7 @@ func (c S3ApiController) GetBucketTagging(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketTaggingAction,
|
||||
Actions: []auth.Action{auth.GetBucketTaggingAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -92,7 +92,7 @@ func (c S3ApiController) GetBucketOwnershipControls(ctx *fiber.Ctx) (*Response,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketOwnershipControlsAction,
|
||||
Actions: []auth.Action{auth.GetBucketOwnershipControlsAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -133,7 +133,7 @@ func (c S3ApiController) GetBucketVersioning(ctx *fiber.Ctx) (*Response, error)
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketVersioningAction,
|
||||
Actions: []auth.Action{auth.GetBucketVersioningAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -176,7 +176,7 @@ func (c S3ApiController) GetBucketCors(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketCorsAction,
|
||||
Actions: []auth.Action{auth.GetBucketCorsAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -220,7 +220,7 @@ func (c S3ApiController) GetBucketPolicy(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketPolicyAction,
|
||||
Actions: []auth.Action{auth.GetBucketPolicyAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -255,7 +255,7 @@ func (c S3ApiController) GetBucketPolicyStatus(ctx *fiber.Ctx) (*Response, error
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketPolicyStatusAction,
|
||||
Actions: []auth.Action{auth.GetBucketPolicyStatusAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -317,7 +317,7 @@ func (c S3ApiController) ListObjectVersions(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketVersionsAction,
|
||||
Actions: []auth.Action{auth.ListBucketVersionsAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -371,7 +371,7 @@ func (c S3ApiController) GetObjectLockConfiguration(ctx *fiber.Ctx) (*Response,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketObjectLockConfigurationAction,
|
||||
Actions: []auth.Action{auth.GetBucketObjectLockConfigurationAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -417,7 +417,7 @@ func (c S3ApiController) GetBucketAcl(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketAclAction,
|
||||
Actions: []auth.Action{auth.GetBucketAclAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -469,7 +469,7 @@ func (c S3ApiController) ListMultipartUploads(ctx *fiber.Ctx) (*Response, error)
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketMultipartUploadsAction,
|
||||
Actions: []auth.Action{auth.ListBucketMultipartUploadsAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -531,7 +531,7 @@ func (c S3ApiController) ListObjectsV2(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketAction,
|
||||
Actions: []auth.Action{auth.ListBucketAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -604,7 +604,7 @@ func (c S3ApiController) ListObjects(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketAction,
|
||||
Actions: []auth.Action{auth.ListBucketAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -667,7 +667,7 @@ func (c S3ApiController) GetBucketLocation(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketLocationAction,
|
||||
Actions: []auth.Action{auth.GetBucketLocationAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
|
||||
@@ -40,7 +40,7 @@ func (c S3ApiController) HeadBucket(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketAction,
|
||||
Actions: []auth.Action{auth.ListBucketAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
|
||||
@@ -48,7 +48,7 @@ func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.DeleteObjectAction,
|
||||
Actions: []auth.Action{auth.DeleteObjectAction},
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -130,7 +130,7 @@ func (c S3ApiController) POSTObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutObjectAction,
|
||||
Actions: []auth.Action{auth.PutObjectAction},
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
|
||||
@@ -45,7 +45,7 @@ func (c S3ApiController) PutBucketTagging(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketTaggingAction,
|
||||
Actions: []auth.Action{auth.PutBucketTaggingAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -88,7 +88,7 @@ func (c S3ApiController) PutBucketOwnershipControls(ctx *fiber.Ctx) (*Response,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketOwnershipControlsAction,
|
||||
Actions: []auth.Action{auth.PutBucketOwnershipControlsAction},
|
||||
DisableACL: c.disableACL,
|
||||
}); err != nil {
|
||||
return &Response{
|
||||
@@ -143,7 +143,7 @@ func (c S3ApiController) PutBucketVersioning(ctx *fiber.Ctx) (*Response, error)
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketVersioningAction,
|
||||
Actions: []auth.Action{auth.PutBucketVersioningAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -198,7 +198,7 @@ func (c S3ApiController) PutObjectLockConfiguration(ctx *fiber.Ctx) (*Response,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketObjectLockConfigurationAction,
|
||||
Actions: []auth.Action{auth.PutBucketObjectLockConfigurationAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
}); err != nil {
|
||||
@@ -240,7 +240,7 @@ func (c S3ApiController) PutBucketCors(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketCorsAction,
|
||||
Actions: []auth.Action{auth.PutBucketCorsAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -296,7 +296,7 @@ func (c S3ApiController) PutBucketPolicy(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketPolicyAction,
|
||||
Actions: []auth.Action{auth.PutBucketPolicyAction},
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -349,7 +349,7 @@ func (c S3ApiController) PutBucketAcl(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketAclAction,
|
||||
Actions: []auth.Action{auth.PutBucketAclAction},
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -50,7 +50,7 @@ func (c S3ApiController) DeleteObjectTagging(ctx *fiber.Ctx) (*Response, error)
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: action,
|
||||
Actions: []auth.Action{action},
|
||||
IsPublicRequest: isBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -94,7 +94,7 @@ func (c S3ApiController) AbortMultipartUpload(ctx *fiber.Ctx) (*Response, error)
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.AbortMultipartUploadAction,
|
||||
Actions: []auth.Action{auth.AbortMultipartUploadAction},
|
||||
IsPublicRequest: isBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -149,7 +149,7 @@ func (c S3ApiController) DeleteObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: action,
|
||||
Actions: []auth.Action{action},
|
||||
IsPublicRequest: isBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
|
||||
@@ -53,7 +53,7 @@ func (c S3ApiController) GetObjectTagging(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: action,
|
||||
Actions: []auth.Action{action},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -111,7 +111,7 @@ func (c S3ApiController) GetObjectRetention(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectRetentionAction,
|
||||
Actions: []auth.Action{auth.GetObjectRetentionAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -159,7 +159,7 @@ func (c S3ApiController) GetObjectLegalHold(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectLegalHoldAction,
|
||||
Actions: []auth.Action{auth.GetObjectLegalHoldAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -197,7 +197,7 @@ func (c S3ApiController) GetObjectAcl(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAclAction,
|
||||
Actions: []auth.Action{auth.GetObjectAclAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -240,7 +240,7 @@ func (c S3ApiController) ListParts(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.ListMultipartUploadPartsAction,
|
||||
Actions: []auth.Action{auth.ListMultipartUploadPartsAction},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -312,7 +312,7 @@ func (c S3ApiController) GetObjectAttributes(ctx *fiber.Ctx) (*Response, error)
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: action,
|
||||
Actions: []auth.Action{action},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -437,7 +437,7 @@ func (c S3ApiController) GetObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: action,
|
||||
Actions: []auth.Action{action},
|
||||
IsPublicRequest: isPublicBucketRequest,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
|
||||
@@ -85,7 +85,7 @@ func (c S3ApiController) HeadObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: action,
|
||||
Actions: []auth.Action{action},
|
||||
IsPublicRequest: isPublicBucket,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
|
||||
@@ -48,7 +48,7 @@ func (c S3ApiController) RestoreObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.RestoreObjectAction,
|
||||
Actions: []auth.Action{auth.RestoreObjectAction},
|
||||
IsPublicRequest: isBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -100,7 +100,7 @@ func (c S3ApiController) SelectObjectContent(ctx *fiber.Ctx) (*Response, error)
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAction,
|
||||
Actions: []auth.Action{auth.GetObjectAction},
|
||||
IsPublicRequest: isBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -154,11 +154,26 @@ func (c S3ApiController) CreateMultipartUpload(ctx *fiber.Ctx) (*Response, error
|
||||
contentEncoding := ctx.Get("Content-Encoding")
|
||||
tagging := ctx.Get("X-Amz-Tagging")
|
||||
expires := ctx.Get("Expires")
|
||||
legalHoldHdr := ctx.Get("X-Amz-Object-Lock-Legal-Hold")
|
||||
lockModeHdr := ctx.Get("X-Amz-Object-Lock-Mode")
|
||||
objLockDate := ctx.Get("X-Amz-Object-Lock-Retain-Until-Date")
|
||||
|
||||
// context locals
|
||||
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
|
||||
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
actions := []auth.Action{auth.PutObjectAction}
|
||||
if tagging != "" {
|
||||
actions = append(actions, auth.PutObjectTaggingAction)
|
||||
}
|
||||
if legalHoldHdr != "" {
|
||||
actions = append(actions, auth.PutObjectLegalHoldAction)
|
||||
}
|
||||
if lockModeHdr != "" || objLockDate != "" {
|
||||
actions = append(actions, auth.PutObjectRetentionAction)
|
||||
}
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
@@ -168,7 +183,7 @@ func (c S3ApiController) CreateMultipartUpload(ctx *fiber.Ctx) (*Response, error
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
Actions: actions,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -261,7 +276,7 @@ func (c S3ApiController) CompleteMultipartUpload(ctx *fiber.Ctx) (*Response, err
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
Actions: []auth.Action{auth.PutObjectAction},
|
||||
IsPublicRequest: isBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
|
||||
@@ -55,7 +55,7 @@ func (c S3ApiController) PutObjectTagging(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: action,
|
||||
Actions: []auth.Action{action},
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -106,7 +106,7 @@ func (c S3ApiController) PutObjectRetention(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectRetentionAction,
|
||||
Actions: []auth.Action{auth.PutObjectRetentionAction},
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -173,7 +173,7 @@ func (c S3ApiController) PutObjectLegalHold(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectLegalHoldAction,
|
||||
Actions: []auth.Action{auth.PutObjectLegalHoldAction},
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -243,7 +243,7 @@ func (c S3ApiController) UploadPart(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
Actions: []auth.Action{auth.PutObjectAction},
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -359,7 +359,7 @@ func (c S3ApiController) UploadPartCopy(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
Actions: []auth.Action{auth.PutObjectAction},
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
@@ -443,7 +443,7 @@ func (c S3ApiController) PutObjectAcl(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAclAction,
|
||||
Actions: []auth.Action{auth.PutObjectAclAction},
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -486,6 +486,9 @@ func (c S3ApiController) CopyObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
expires := ctx.Get("Expires")
|
||||
tagging := ctx.Get("x-amz-tagging")
|
||||
storageClass := ctx.Get("X-Amz-Storage-Class")
|
||||
legalHoldHdr := ctx.Get("X-Amz-Object-Lock-Legal-Hold")
|
||||
lockModeHdr := ctx.Get("X-Amz-Object-Lock-Mode")
|
||||
objLockDate := ctx.Get("X-Amz-Object-Lock-Retain-Until-Date")
|
||||
// context locals
|
||||
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
|
||||
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)
|
||||
@@ -500,6 +503,17 @@ func (c S3ApiController) CopyObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
}, err
|
||||
}
|
||||
|
||||
actions := []auth.Action{auth.PutObjectAction}
|
||||
if tagging != "" {
|
||||
actions = append(actions, auth.PutObjectTaggingAction)
|
||||
}
|
||||
if legalHoldHdr != "" {
|
||||
actions = append(actions, auth.PutObjectLegalHoldAction)
|
||||
}
|
||||
if lockModeHdr != "" || objLockDate != "" {
|
||||
actions = append(actions, auth.PutObjectRetentionAction)
|
||||
}
|
||||
|
||||
err = auth.VerifyObjectCopyAccess(ctx.Context(), c.be, copySource,
|
||||
auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
@@ -508,7 +522,7 @@ func (c S3ApiController) CopyObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
Actions: actions,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -642,6 +656,9 @@ func (c S3ApiController) PutObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
cacheControl := ctx.Get("Cache-Control")
|
||||
expires := ctx.Get("Expires")
|
||||
tagging := ctx.Get("x-amz-tagging")
|
||||
legalHoldHdr := ctx.Get("X-Amz-Object-Lock-Legal-Hold")
|
||||
lockModeHdr := ctx.Get("X-Amz-Object-Lock-Mode")
|
||||
objLockDate := ctx.Get("X-Amz-Object-Lock-Retain-Until-Date")
|
||||
// context locals
|
||||
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
|
||||
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)
|
||||
@@ -660,6 +677,17 @@ func (c S3ApiController) PutObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
contentLengthStr = decodedLength
|
||||
}
|
||||
|
||||
actions := []auth.Action{auth.PutObjectAction}
|
||||
if tagging != "" {
|
||||
actions = append(actions, auth.PutObjectTaggingAction)
|
||||
}
|
||||
if legalHoldHdr != "" {
|
||||
actions = append(actions, auth.PutObjectLegalHoldAction)
|
||||
}
|
||||
if lockModeHdr != "" || objLockDate != "" {
|
||||
actions = append(actions, auth.PutObjectRetentionAction)
|
||||
}
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
@@ -669,7 +697,7 @@ func (c S3ApiController) PutObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
Actions: actions,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
DisableACL: c.disableACL,
|
||||
})
|
||||
|
||||
@@ -17,6 +17,7 @@ package integration
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
@@ -453,3 +454,557 @@ func AccessControl_copy_object_with_starting_slash_for_user(s *S3Conf) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func AccessControl_PutObject_with_tagging_policy(s *S3Conf) error {
|
||||
testName := "AccessControl_PutObject_with_tagging_policy"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
testuser := getUser("user")
|
||||
if err := createUsers(s, []user{testuser}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objectResource := fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket)
|
||||
|
||||
// Error path: user has s3:PutObject but not s3:PutObjectTagging
|
||||
policy := genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:PutObject"`, objectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userClient := s.getUserClient(testuser)
|
||||
tagging := "key=value"
|
||||
_, err := putObjectWithData(0, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
Tagging: &tagging,
|
||||
}, userClient)
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Happy path: user has s3:PutObject and s3:PutObjectTagging
|
||||
policy = genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `["s3:PutObject","s3:PutObjectTagging"]`, objectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = putObjectWithData(0, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
Tagging: &tagging,
|
||||
}, userClient)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func AccessControl_PutObject_with_legal_hold_policy(s *S3Conf) error {
|
||||
testName := "AccessControl_PutObject_with_legal_hold_policy"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
testuser := getUser("user")
|
||||
if err := createUsers(s, []user{testuser}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objectResource := fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket)
|
||||
|
||||
// Error path: user has s3:PutObject but not s3:PutObjectLegalHold
|
||||
policy := genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:PutObject"`, objectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userClient := s.getUserClient(testuser)
|
||||
_, err := putObjectWithData(0, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatusOn,
|
||||
}, userClient)
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Happy path: user has s3:PutObject and s3:PutObjectLegalHold
|
||||
policy = genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `["s3:PutObject","s3:PutObjectLegalHold"]`, objectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = putObjectWithData(0, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatusOn,
|
||||
}, userClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cleanupLockedObjects(s3client, bucket, []objToDelete{{key: obj, removeLegalHold: true}})
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
func AccessControl_PutObject_with_retention_policy(s *S3Conf) error {
|
||||
testName := "AccessControl_PutObject_with_retention_policy"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
testuser := getUser("user")
|
||||
if err := createUsers(s, []user{testuser}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objectResource := fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket)
|
||||
date := time.Now().Add(time.Hour)
|
||||
|
||||
// Error path: user has s3:PutObject but not s3:PutObjectRetention
|
||||
policy := genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:PutObject"`, objectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userClient := s.getUserClient(testuser)
|
||||
_, err := putObjectWithData(0, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
ObjectLockMode: types.ObjectLockModeGovernance,
|
||||
ObjectLockRetainUntilDate: &date,
|
||||
}, userClient)
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Happy path: user has s3:PutObject and s3:PutObjectRetention
|
||||
policy = genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `["s3:PutObject","s3:PutObjectRetention"]`, objectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = putObjectWithData(0, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
ObjectLockMode: types.ObjectLockModeGovernance,
|
||||
ObjectLockRetainUntilDate: &date,
|
||||
}, userClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cleanupLockedObjects(s3client, bucket, []objToDelete{{key: obj}})
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
func AccessControl_CreateMultipartUpload_with_tagging_policy(s *S3Conf) error {
|
||||
testName := "AccessControl_CreateMultipartUpload_with_tagging_policy"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
testuser := getUser("user")
|
||||
if err := createUsers(s, []user{testuser}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objectResource := fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket)
|
||||
|
||||
// Error path: user has s3:PutObject but not s3:PutObjectTagging
|
||||
policy := genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:PutObject"`, objectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userClient := s.getUserClient(testuser)
|
||||
tagging := "key=value"
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := userClient.CreateMultipartUpload(ctx, &s3.CreateMultipartUploadInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
Tagging: &tagging,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Happy path: user has s3:PutObject and s3:PutObjectTagging
|
||||
policy = genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `["s3:PutObject","s3:PutObjectTagging"]`, objectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = userClient.CreateMultipartUpload(ctx, &s3.CreateMultipartUploadInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
Tagging: &tagging,
|
||||
})
|
||||
cancel()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func AccessControl_CreateMultipartUpload_with_legal_hold_policy(s *S3Conf) error {
|
||||
testName := "AccessControl_CreateMultipartUpload_with_legal_hold_policy"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
testuser := getUser("user")
|
||||
if err := createUsers(s, []user{testuser}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objectResource := fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket)
|
||||
|
||||
// Error path: user has s3:PutObject but not s3:PutObjectLegalHold
|
||||
policy := genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:PutObject"`, objectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userClient := s.getUserClient(testuser)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := userClient.CreateMultipartUpload(ctx, &s3.CreateMultipartUploadInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatusOn,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Happy path: user has s3:PutObject and s3:PutObjectLegalHold
|
||||
policy = genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `["s3:PutObject","s3:PutObjectLegalHold"]`, objectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = userClient.CreateMultipartUpload(ctx, &s3.CreateMultipartUploadInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatusOn,
|
||||
})
|
||||
cancel()
|
||||
return err
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
func AccessControl_CreateMultipartUpload_with_retention_policy(s *S3Conf) error {
|
||||
testName := "AccessControl_CreateMultipartUpload_with_retention_policy"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
testuser := getUser("user")
|
||||
if err := createUsers(s, []user{testuser}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objectResource := fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket)
|
||||
date := time.Now().Add(time.Hour)
|
||||
|
||||
// Error path: user has s3:PutObject but not s3:PutObjectRetention
|
||||
policy := genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:PutObject"`, objectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userClient := s.getUserClient(testuser)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := userClient.CreateMultipartUpload(ctx, &s3.CreateMultipartUploadInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
ObjectLockMode: types.ObjectLockModeGovernance,
|
||||
ObjectLockRetainUntilDate: &date,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Happy path: user has s3:PutObject and s3:PutObjectRetention
|
||||
policy = genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `["s3:PutObject","s3:PutObjectRetention"]`, objectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = userClient.CreateMultipartUpload(ctx, &s3.CreateMultipartUploadInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
ObjectLockMode: types.ObjectLockModeGovernance,
|
||||
ObjectLockRetainUntilDate: &date,
|
||||
})
|
||||
cancel()
|
||||
return err
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
func AccessControl_CopyObject_with_tagging_policy(s *S3Conf) error {
|
||||
testName := "AccessControl_CopyObject_with_tagging_policy"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
testuser := getUser("user")
|
||||
if err := createUsers(s, []user{testuser}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srcObj, dstObj := "source-object", "dst-object"
|
||||
_, err := putObjectWithData(0, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &srcObj,
|
||||
}, s3client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dstObjectResource := fmt.Sprintf(`"arn:aws:s3:::%s/%s"`, bucket, dstObj)
|
||||
srcObjectResource := fmt.Sprintf(`"arn:aws:s3:::%s/%s"`, bucket, srcObj)
|
||||
|
||||
// Error path: user has s3:PutObject, s3:GetObject but not s3:PutObjectTagging
|
||||
policy := fmt.Sprintf(`{
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "%s",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": %s
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "%s",
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": %s
|
||||
}
|
||||
]
|
||||
}`, testuser.access, srcObjectResource, testuser.access, dstObjectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userClient := s.getUserClient(testuser)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = userClient.CopyObject(ctx, &s3.CopyObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &dstObj,
|
||||
CopySource: getPtr(fmt.Sprintf("%s/%s", bucket, srcObj)),
|
||||
Tagging: getPtr("key=value"),
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Happy path: user has s3:GetObject, s3:PutObject and s3:PutObjectTagging
|
||||
policy = fmt.Sprintf(`{
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "%s",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": %s
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "%s",
|
||||
"Action": ["s3:PutObject","s3:PutObjectTagging"],
|
||||
"Resource": %s
|
||||
}
|
||||
]
|
||||
}`, testuser.access, srcObjectResource, testuser.access, dstObjectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = userClient.CopyObject(ctx, &s3.CopyObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &dstObj,
|
||||
CopySource: getPtr(fmt.Sprintf("%s/%s", bucket, srcObj)),
|
||||
Tagging: getPtr("key=value"),
|
||||
})
|
||||
cancel()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func AccessControl_CopyObject_with_legal_hold_policy(s *S3Conf) error {
|
||||
testName := "AccessControl_CopyObject_with_legal_hold_policy"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
testuser := getUser("user")
|
||||
if err := createUsers(s, []user{testuser}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srcObj, dstObj := "source-object", "dst-object"
|
||||
_, err := putObjectWithData(0, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &srcObj,
|
||||
}, s3client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dstObjectResource := fmt.Sprintf(`"arn:aws:s3:::%s/%s"`, bucket, dstObj)
|
||||
srcObjectResource := fmt.Sprintf(`"arn:aws:s3:::%s/%s"`, bucket, srcObj)
|
||||
|
||||
// Error path: user has s3:PutObject, s3:GetObject but not s3:PutObjectLegalHold
|
||||
policy := fmt.Sprintf(`{
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "%s",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": %s
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "%s",
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": %s
|
||||
}
|
||||
]
|
||||
}`, testuser.access, srcObjectResource, testuser.access, dstObjectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userClient := s.getUserClient(testuser)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = userClient.CopyObject(ctx, &s3.CopyObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &dstObj,
|
||||
CopySource: getPtr(fmt.Sprintf("%s/%s", bucket, srcObj)),
|
||||
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatusOn,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Happy path: user has s3:GetObject, s3:PutObject and s3:PutObjectLegalHold
|
||||
policy = fmt.Sprintf(`{
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "%s",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": %s
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "%s",
|
||||
"Action": ["s3:PutObject","s3:PutObjectLegalHold"],
|
||||
"Resource": %s
|
||||
}
|
||||
]
|
||||
}`, testuser.access, srcObjectResource, testuser.access, dstObjectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = userClient.CopyObject(ctx, &s3.CopyObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &dstObj,
|
||||
CopySource: getPtr(fmt.Sprintf("%s/%s", bucket, srcObj)),
|
||||
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatusOn,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cleanupLockedObjects(s3client, bucket, []objToDelete{{key: dstObj, removeLegalHold: true}})
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
func AccessControl_CopyObject_with_retention_policy(s *S3Conf) error {
|
||||
testName := "AccessControl_CopyObject_with_retention_policy"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
testuser := getUser("user")
|
||||
if err := createUsers(s, []user{testuser}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srcObj, dstObj := "source-object", "dst-object"
|
||||
_, err := putObjectWithData(0, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &srcObj,
|
||||
}, s3client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dstObjectResource := fmt.Sprintf(`"arn:aws:s3:::%s/%s"`, bucket, dstObj)
|
||||
srcObjectResource := fmt.Sprintf(`"arn:aws:s3:::%s/%s"`, bucket, srcObj)
|
||||
|
||||
// Error path: user has s3:PutObject, s3:GetObject but not s3:PutObjectRetention
|
||||
policy := fmt.Sprintf(`{
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "%s",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": %s
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "%s",
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": %s
|
||||
}
|
||||
]
|
||||
}`, testuser.access, srcObjectResource, testuser.access, dstObjectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userClient := s.getUserClient(testuser)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = userClient.CopyObject(ctx, &s3.CopyObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &dstObj,
|
||||
CopySource: getPtr(fmt.Sprintf("%s/%s", bucket, srcObj)),
|
||||
ObjectLockMode: types.ObjectLockModeGovernance,
|
||||
ObjectLockRetainUntilDate: getPtr(time.Now().AddDate(1, 0, 0)),
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Happy path: user has s3:GetObject, s3:PutObject and s3:PutObjectRetention
|
||||
policy = fmt.Sprintf(`{
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "%s",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": %s
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "%s",
|
||||
"Action": ["s3:PutObject","s3:PutObjectRetention"],
|
||||
"Resource": %s
|
||||
}
|
||||
]
|
||||
}`, testuser.access, srcObjectResource, testuser.access, dstObjectResource)
|
||||
if err := putBucketPolicy(s3client, bucket, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = userClient.CopyObject(ctx, &s3.CopyObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &dstObj,
|
||||
CopySource: getPtr(fmt.Sprintf("%s/%s", bucket, srcObj)),
|
||||
ObjectLockMode: types.ObjectLockModeGovernance,
|
||||
ObjectLockRetainUntilDate: getPtr(time.Now().AddDate(1, 0, 0)),
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cleanupLockedObjects(s3client, bucket, []objToDelete{{key: dstObj}})
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
@@ -1021,6 +1021,15 @@ func TestAccessControl(ts *TestState) {
|
||||
ts.Run(AccessControl_root_PutBucketAcl)
|
||||
ts.Run(AccessControl_user_PutBucketAcl_with_policy_access)
|
||||
ts.Run(AccessControl_copy_object_with_starting_slash_for_user)
|
||||
ts.Run(AccessControl_PutObject_with_tagging_policy)
|
||||
ts.Run(AccessControl_PutObject_with_legal_hold_policy)
|
||||
ts.Run(AccessControl_PutObject_with_retention_policy)
|
||||
ts.Run(AccessControl_CreateMultipartUpload_with_tagging_policy)
|
||||
ts.Run(AccessControl_CreateMultipartUpload_with_legal_hold_policy)
|
||||
ts.Run(AccessControl_CreateMultipartUpload_with_retention_policy)
|
||||
ts.Run(AccessControl_CopyObject_with_tagging_policy)
|
||||
ts.Run(AccessControl_CopyObject_with_legal_hold_policy)
|
||||
ts.Run(AccessControl_CopyObject_with_retention_policy)
|
||||
}
|
||||
|
||||
func TestPublicBuckets(ts *TestState) {
|
||||
@@ -1861,6 +1870,15 @@ func GetIntTests() IntTests {
|
||||
"AccessControl_root_PutBucketAcl": AccessControl_root_PutBucketAcl,
|
||||
"AccessControl_user_PutBucketAcl_with_policy_access": AccessControl_user_PutBucketAcl_with_policy_access,
|
||||
"AccessControl_copy_object_with_starting_slash_for_user": AccessControl_copy_object_with_starting_slash_for_user,
|
||||
"AccessControl_PutObject_with_tagging_policy": AccessControl_PutObject_with_tagging_policy,
|
||||
"AccessControl_PutObject_with_legal_hold_policy": AccessControl_PutObject_with_legal_hold_policy,
|
||||
"AccessControl_PutObject_with_retention_policy": AccessControl_PutObject_with_retention_policy,
|
||||
"AccessControl_CreateMultipartUpload_with_tagging_policy": AccessControl_CreateMultipartUpload_with_tagging_policy,
|
||||
"AccessControl_CreateMultipartUpload_with_legal_hold_policy": AccessControl_CreateMultipartUpload_with_legal_hold_policy,
|
||||
"AccessControl_CreateMultipartUpload_with_retention_policy": AccessControl_CreateMultipartUpload_with_retention_policy,
|
||||
"AccessControl_CopyObject_with_tagging_policy": AccessControl_CopyObject_with_tagging_policy,
|
||||
"AccessControl_CopyObject_with_legal_hold_policy": AccessControl_CopyObject_with_legal_hold_policy,
|
||||
"AccessControl_CopyObject_with_retention_policy": AccessControl_CopyObject_with_retention_policy,
|
||||
"PublicBucket_default_private_bucket": PublicBucket_default_private_bucket,
|
||||
"PublicBucket_public_bucket_policy": PublicBucket_public_bucket_policy,
|
||||
"PublicBucket_public_object_policy": PublicBucket_public_object_policy,
|
||||
|
||||
Reference in New Issue
Block a user