mirror of
https://github.com/versity/versitygw.git
synced 2026-04-20 20:50:28 +00:00
feat: add response header overrides for GetObject
GetObject allows overriding response headers with the following paramters: response-cache-control response-content-disposition response-content-encoding response-content-language response-content-type response-expires This is only valid for signed (and pre-singed) requests. An error is returned for anonymous requests if these are set. More info on the GetObject overrides can be found in the GetObject API reference. This also clarifies the naming of the AccessOptions IsPublicBucket to IsPublicRequest to indicate this is a public access request and not just accessing a bucket that allows public access. Fixes #1501
This commit is contained in:
@@ -70,20 +70,20 @@ func VerifyObjectCopyAccess(ctx context.Context, be backend.Backend, copySource
|
||||
}
|
||||
|
||||
type AccessOptions struct {
|
||||
Acl ACL
|
||||
AclPermission Permission
|
||||
IsRoot bool
|
||||
Acc Account
|
||||
Bucket string
|
||||
Object string
|
||||
Action Action
|
||||
Readonly bool
|
||||
IsBucketPublic bool
|
||||
Acl ACL
|
||||
AclPermission Permission
|
||||
IsRoot bool
|
||||
Acc Account
|
||||
Bucket string
|
||||
Object string
|
||||
Action Action
|
||||
Readonly bool
|
||||
IsPublicRequest bool
|
||||
}
|
||||
|
||||
func VerifyAccess(ctx context.Context, be backend.Backend, opts AccessOptions) error {
|
||||
// Skip the access check for public buckets
|
||||
if opts.IsBucketPublic {
|
||||
// Skip the access check for public bucket requests
|
||||
if opts.IsPublicRequest {
|
||||
return nil
|
||||
}
|
||||
if opts.Readonly {
|
||||
|
||||
@@ -31,14 +31,14 @@ func (c S3ApiController) DeleteBucketTagging(ctx *fiber.Ctx) (*Response, error)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketTaggingAction,
|
||||
IsBucketPublic: IsBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketTaggingAction,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -132,14 +132,14 @@ func (c S3ApiController) DeleteBucketCors(ctx *fiber.Ctx) (*Response, error) {
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketCorsAction,
|
||||
IsBucketPublic: IsBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketCorsAction,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -167,14 +167,14 @@ func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) (*Response, error) {
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.DeleteBucketAction,
|
||||
IsBucketPublic: IsBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.DeleteBucketAction,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
|
||||
@@ -35,14 +35,14 @@ func (c S3ApiController) GetBucketTagging(ctx *fiber.Ctx) (*Response, error) {
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketTaggingAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketTaggingAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -87,14 +87,14 @@ func (c S3ApiController) GetBucketOwnershipControls(ctx *fiber.Ctx) (*Response,
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketOwnershipControlsAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketOwnershipControlsAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -127,14 +127,14 @@ func (c S3ApiController) GetBucketVersioning(ctx *fiber.Ctx) (*Response, error)
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketVersioningAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketVersioningAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -169,14 +169,14 @@ func (c S3ApiController) GetBucketCors(ctx *fiber.Ctx) (*Response, error) {
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketCorsAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketCorsAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -212,14 +212,14 @@ func (c S3ApiController) GetBucketPolicy(ctx *fiber.Ctx) (*Response, error) {
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketPolicyAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketPolicyAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -246,14 +246,14 @@ func (c S3ApiController) GetBucketPolicyStatus(ctx *fiber.Ctx) (*Response, error
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketPolicyStatusAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketPolicyStatusAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -307,14 +307,14 @@ func (c S3ApiController) ListObjectVersions(ctx *fiber.Ctx) (*Response, error) {
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketVersionsAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketVersionsAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -362,14 +362,14 @@ func (c S3ApiController) GetObjectLockConfiguration(ctx *fiber.Ctx) (*Response,
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketObjectLockConfigurationAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketObjectLockConfigurationAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -407,14 +407,14 @@ func (c S3ApiController) GetBucketAcl(ctx *fiber.Ctx) (*Response, error) {
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionReadAcp,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketAclAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionReadAcp,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketAclAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -458,14 +458,14 @@ func (c S3ApiController) ListMultipartUploads(ctx *fiber.Ctx) (*Response, error)
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketMultipartUploadsAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketMultipartUploadsAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -517,14 +517,14 @@ func (c S3ApiController) ListObjectsV2(ctx *fiber.Ctx) (*Response, error) {
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -576,14 +576,14 @@ func (c S3ApiController) ListObjects(ctx *fiber.Ctx) (*Response, error) {
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
|
||||
@@ -31,14 +31,14 @@ func (c S3ApiController) HeadBucket(ctx *fiber.Ctx) (*Response, error) {
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
|
||||
@@ -39,14 +39,14 @@ func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) (*Response, error) {
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.DeleteObjectAction,
|
||||
IsBucketPublic: IsBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.DeleteObjectAction,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
|
||||
@@ -40,14 +40,14 @@ func (c S3ApiController) PutBucketTagging(ctx *fiber.Ctx) (*Response, error) {
|
||||
isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketTaggingAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketTaggingAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -136,14 +136,14 @@ func (c S3ApiController) PutBucketVersioning(ctx *fiber.Ctx) (*Response, error)
|
||||
isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketVersioningAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketVersioningAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -190,14 +190,14 @@ func (c S3ApiController) PutObjectLockConfiguration(ctx *fiber.Ctx) (*Response,
|
||||
isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
|
||||
if err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketObjectLockConfigurationAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketObjectLockConfigurationAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
}); err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
@@ -231,14 +231,14 @@ func (c S3ApiController) PutBucketCors(ctx *fiber.Ctx) (*Response, error) {
|
||||
isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketCorsAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketCorsAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
|
||||
@@ -37,15 +37,15 @@ func (c S3ApiController) DeleteObjectTagging(ctx *fiber.Ctx) (*Response, error)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.DeleteObjectTaggingAction,
|
||||
IsBucketPublic: isBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.DeleteObjectTaggingAction,
|
||||
IsPublicRequest: isBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -76,15 +76,15 @@ func (c S3ApiController) AbortMultipartUpload(ctx *fiber.Ctx) (*Response, error)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.AbortMultipartUploadAction,
|
||||
IsBucketPublic: isBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.AbortMultipartUploadAction,
|
||||
IsPublicRequest: isBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -123,15 +123,15 @@ func (c S3ApiController) DeleteObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.DeleteObjectAction,
|
||||
IsBucketPublic: isBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.DeleteObjectAction,
|
||||
IsPublicRequest: isBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
|
||||
@@ -41,15 +41,15 @@ func (c S3ApiController) GetObjectTagging(ctx *fiber.Ctx) (*Response, error) {
|
||||
isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectTaggingAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectTaggingAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -95,15 +95,15 @@ func (c S3ApiController) GetObjectRetention(ctx *fiber.Ctx) (*Response, error) {
|
||||
isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectRetentionAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectRetentionAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -142,15 +142,15 @@ func (c S3ApiController) GetObjectLegalHold(ctx *fiber.Ctx) (*Response, error) {
|
||||
isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectLegalHoldAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectLegalHoldAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -179,15 +179,15 @@ func (c S3ApiController) GetObjectAcl(ctx *fiber.Ctx) (*Response, error) {
|
||||
isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionReadAcp,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAclAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionReadAcp,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAclAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -221,15 +221,15 @@ func (c S3ApiController) ListParts(ctx *fiber.Ctx) (*Response, error) {
|
||||
isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.ListMultipartUploadPartsAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.ListMultipartUploadPartsAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -294,15 +294,15 @@ func (c S3ApiController) GetObjectAttributes(ctx *fiber.Ctx) (*Response, error)
|
||||
isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAttributesAction,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAttributesAction,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -380,28 +380,57 @@ func (c S3ApiController) GetObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
versionId := ctx.Query("versionId")
|
||||
acceptRange := ctx.Get("Range")
|
||||
checksumMode := types.ChecksumMode(ctx.Get("x-amz-checksum-mode"))
|
||||
|
||||
// Extract response override query parameters
|
||||
responseOverrides := map[string]*string{
|
||||
"Cache-Control": utils.GetQueryParam(ctx, "response-cache-control"),
|
||||
"Content-Disposition": utils.GetQueryParam(ctx, "response-content-disposition"),
|
||||
"Content-Encoding": utils.GetQueryParam(ctx, "response-content-encoding"),
|
||||
"Content-Language": utils.GetQueryParam(ctx, "response-content-language"),
|
||||
"Content-Type": utils.GetQueryParam(ctx, "response-content-type"),
|
||||
"Expires": utils.GetQueryParam(ctx, "response-expires"),
|
||||
}
|
||||
|
||||
// Check if any response override parameters are present
|
||||
hasResponseOverrides := false
|
||||
for _, override := range responseOverrides {
|
||||
if override != nil {
|
||||
hasResponseOverrides = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// context locals
|
||||
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
|
||||
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
isPublicBucketRequest := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
utils.ContextKeySkipResBodyLog.Set(ctx, true)
|
||||
|
||||
// Validate that response override parameters are not used with anonymous requests
|
||||
if hasResponseOverrides && isPublicBucketRequest {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
}, s3err.GetAPIError(s3err.ErrAnonymousResponseHeaders)
|
||||
}
|
||||
|
||||
action := auth.GetObjectAction
|
||||
if ctx.Request().URI().QueryArgs().Has("versionId") {
|
||||
action = auth.GetObjectVersionAction
|
||||
}
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: action,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: action,
|
||||
IsPublicRequest: isPublicBucketRequest,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -478,17 +507,17 @@ func (c S3ApiController) GetObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
"x-amz-restore": res.Restore,
|
||||
"accept-ranges": res.AcceptRanges,
|
||||
"Content-Range": res.ContentRange,
|
||||
"Content-Disposition": res.ContentDisposition,
|
||||
"Content-Encoding": res.ContentEncoding,
|
||||
"Content-Language": res.ContentLanguage,
|
||||
"Cache-Control": res.CacheControl,
|
||||
"Expires": res.ExpiresString,
|
||||
"Content-Disposition": utils.ApplyOverride(res.ContentDisposition, responseOverrides["Content-Disposition"]),
|
||||
"Content-Encoding": utils.ApplyOverride(res.ContentEncoding, responseOverrides["Content-Encoding"]),
|
||||
"Content-Language": utils.ApplyOverride(res.ContentLanguage, responseOverrides["Content-Language"]),
|
||||
"Cache-Control": utils.ApplyOverride(res.CacheControl, responseOverrides["Cache-Control"]),
|
||||
"Expires": utils.ApplyOverride(res.ExpiresString, responseOverrides["Expires"]),
|
||||
"x-amz-checksum-crc32": res.ChecksumCRC32,
|
||||
"x-amz-checksum-crc64nvme": res.ChecksumCRC64NVME,
|
||||
"x-amz-checksum-crc32c": res.ChecksumCRC32C,
|
||||
"x-amz-checksum-sha1": res.ChecksumSHA1,
|
||||
"x-amz-checksum-sha256": res.ChecksumSHA256,
|
||||
"Content-Type": res.ContentType,
|
||||
"Content-Type": utils.ApplyOverride(res.ContentType, responseOverrides["Content-Type"]),
|
||||
"x-amz-version-id": res.VersionId,
|
||||
"Content-Length": utils.ConvertPtrToStringPtr(res.ContentLength),
|
||||
"x-amz-mp-parts-count": utils.ConvertPtrToStringPtr(res.PartsCount),
|
||||
|
||||
@@ -48,15 +48,15 @@ func (c S3ApiController) HeadObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: action,
|
||||
IsBucketPublic: isPublicBucket,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: action,
|
||||
IsPublicRequest: isPublicBucket,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
|
||||
@@ -41,15 +41,15 @@ func (c S3ApiController) RestoreObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.RestoreObjectAction,
|
||||
IsBucketPublic: isBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.RestoreObjectAction,
|
||||
IsPublicRequest: isBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -92,15 +92,15 @@ func (c S3ApiController) SelectObjectContent(ctx *fiber.Ctx) (*Response, error)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAction,
|
||||
IsBucketPublic: isBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAction,
|
||||
IsPublicRequest: isBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -243,15 +243,15 @@ func (c S3ApiController) CompleteMultipartUpload(ctx *fiber.Ctx) (*Response, err
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
IsBucketPublic: isBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
IsPublicRequest: isBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
|
||||
@@ -43,15 +43,15 @@ func (c S3ApiController) PutObjectTagging(ctx *fiber.Ctx) (*Response, error) {
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectTaggingAction,
|
||||
IsBucketPublic: IsBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectTaggingAction,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -90,15 +90,15 @@ func (c S3ApiController) PutObjectRetention(ctx *fiber.Ctx) (*Response, error) {
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
if err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectRetentionAction,
|
||||
IsBucketPublic: IsBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectRetentionAction,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
}); err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
@@ -146,15 +146,15 @@ func (c S3ApiController) PutObjectLegalHold(ctx *fiber.Ctx) (*Response, error) {
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
if err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectLegalHoldAction,
|
||||
IsBucketPublic: IsBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectLegalHoldAction,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
}); err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
@@ -214,15 +214,15 @@ func (c S3ApiController) UploadPart(ctx *fiber.Ctx) (*Response, error) {
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
IsBucketPublic: IsBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -329,14 +329,14 @@ func (c S3ApiController) UploadPartCopy(ctx *fiber.Ctx) (*Response, error) {
|
||||
|
||||
err = auth.VerifyObjectCopyAccess(ctx.Context(), c.be, copySource,
|
||||
auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
IsBucketPublic: IsBucketPublic,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -628,15 +628,15 @@ func (c S3ApiController) PutObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be,
|
||||
auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
IsBucketPublic: IsBucketPublic,
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
// access to anonymous requesters
|
||||
func AuthorizePublicBucketAccess(be backend.Backend, s3action string, policyPermission auth.Action, permission auth.Permission) fiber.Handler {
|
||||
return func(ctx *fiber.Ctx) error {
|
||||
// skip for auhtneicated requests
|
||||
// skip for authenticated requests
|
||||
if ctx.Query("X-Amz-Algorithm") != "" || ctx.Get("Authorization") != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -779,3 +779,20 @@ func ValidateCopySource(copysource string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetQueryParam returns a pointer to the query parameter value if it exists
|
||||
func GetQueryParam(ctx *fiber.Ctx, key string) *string {
|
||||
value := ctx.Query(key)
|
||||
if value == "" {
|
||||
return nil
|
||||
}
|
||||
return &value
|
||||
}
|
||||
|
||||
// ApplyOverride returns the override value if it exists and status is 200, otherwise returns original
|
||||
func ApplyOverride(original, override *string) *string {
|
||||
if override != nil {
|
||||
return override
|
||||
}
|
||||
return original
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ const (
|
||||
ErrAnonymousCopyObject
|
||||
ErrAnonymousPutBucketOwnership
|
||||
ErrAnonymousGetBucketOwnership
|
||||
ErrAnonymousResponseHeaders
|
||||
ErrMethodNotAllowed
|
||||
ErrBucketNotEmpty
|
||||
ErrVersionedBucketNotEmpty
|
||||
@@ -225,6 +226,11 @@ var errorCodeResponse = map[ErrorCode]APIError{
|
||||
Description: "s3:GetBucketOwnershipControls does not support Anonymous requests!",
|
||||
HTTPStatusCode: http.StatusForbidden,
|
||||
},
|
||||
ErrAnonymousResponseHeaders: {
|
||||
Code: "InvalidRequest",
|
||||
Description: "Request specific response headers cannot be used for anonymous GET requests.",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrMethodNotAllowed: {
|
||||
Code: "MethodNotAllowed",
|
||||
Description: "The specified method is not allowed against this resource.",
|
||||
|
||||
@@ -211,6 +211,9 @@ func TestGetObject(s *S3Conf) {
|
||||
GetObject_directory_success(s)
|
||||
GetObject_by_range_resp_status(s)
|
||||
GetObject_non_existing_dir_object(s)
|
||||
GetObject_overrides_success(s)
|
||||
GetObject_overrides_presign_success(s)
|
||||
GetObject_overrides_fail_public(s)
|
||||
}
|
||||
|
||||
func TestListObjects(s *S3Conf) {
|
||||
@@ -896,7 +899,7 @@ func TestAccessControl(s *S3Conf) {
|
||||
}
|
||||
|
||||
func TestPublicBuckets(s *S3Conf) {
|
||||
PublicBucket_default_privet_bucket(s)
|
||||
PublicBucket_default_private_bucket(s)
|
||||
PublicBucket_public_bucket_policy(s)
|
||||
PublicBucket_public_object_policy(s)
|
||||
PublicBucket_public_acl(s)
|
||||
@@ -1133,6 +1136,9 @@ func GetIntTests() IntTests {
|
||||
"GetObject_directory_success": GetObject_directory_success,
|
||||
"GetObject_by_range_resp_status": GetObject_by_range_resp_status,
|
||||
"GetObject_non_existing_dir_object": GetObject_non_existing_dir_object,
|
||||
"GetObject_overrides_success": GetObject_overrides_success,
|
||||
"GetObject_overrides_presign_success": GetObject_overrides_presign_success,
|
||||
"GetObject_overrides_fail_public": GetObject_overrides_fail_public,
|
||||
"ListObjects_non_existing_bucket": ListObjects_non_existing_bucket,
|
||||
"ListObjects_with_prefix": ListObjects_with_prefix,
|
||||
"ListObjects_truncated": ListObjects_truncated,
|
||||
@@ -1484,7 +1490,7 @@ 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,
|
||||
"PublicBucket_default_privet_bucket": PublicBucket_default_privet_bucket,
|
||||
"PublicBucket_default_private_bucket": PublicBucket_default_private_bucket,
|
||||
"PublicBucket_public_bucket_policy": PublicBucket_public_bucket_policy,
|
||||
"PublicBucket_public_object_policy": PublicBucket_public_object_policy,
|
||||
"PublicBucket_public_acl": PublicBucket_public_acl,
|
||||
|
||||
@@ -4773,6 +4773,405 @@ func GetObject_non_existing_dir_object(s *S3Conf) error {
|
||||
})
|
||||
}
|
||||
|
||||
func GetObject_overrides_success(s *S3Conf) error {
|
||||
testName := "GetObject_overrides_success"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
// Test data
|
||||
objKey := "test-object"
|
||||
objContent := "test content for response overrides"
|
||||
exp := time.Now()
|
||||
|
||||
// Put an object first
|
||||
_, err := s3client.PutObject(context.Background(), &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
Body: strings.NewReader(objContent),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to put object: %v", err)
|
||||
}
|
||||
|
||||
for _, test := range []PublicBucketTestCase{
|
||||
{
|
||||
Action: "GetObject",
|
||||
Call: func(ctx context.Context) error {
|
||||
_, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
ResponseCacheControl: getPtr("max-age=90"),
|
||||
})
|
||||
return err
|
||||
},
|
||||
ExpectedErr: nil,
|
||||
},
|
||||
{
|
||||
Action: "GetObject",
|
||||
Call: func(ctx context.Context) error {
|
||||
_, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
ResponseContentDisposition: getPtr("inline"),
|
||||
})
|
||||
return err
|
||||
},
|
||||
ExpectedErr: nil,
|
||||
},
|
||||
{
|
||||
Action: "GetObject",
|
||||
Call: func(ctx context.Context) error {
|
||||
_, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
ResponseContentEncoding: getPtr("txt"),
|
||||
})
|
||||
return err
|
||||
},
|
||||
ExpectedErr: nil,
|
||||
},
|
||||
{
|
||||
Action: "GetObject",
|
||||
Call: func(ctx context.Context) error {
|
||||
_, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
ResponseContentLanguage: getPtr("en"),
|
||||
})
|
||||
return err
|
||||
},
|
||||
ExpectedErr: nil,
|
||||
},
|
||||
{
|
||||
Action: "GetObject",
|
||||
Call: func(ctx context.Context) error {
|
||||
_, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
ResponseContentType: getPtr("application/json"),
|
||||
})
|
||||
return err
|
||||
},
|
||||
ExpectedErr: nil,
|
||||
},
|
||||
{
|
||||
Action: "GetObject",
|
||||
Call: func(ctx context.Context) error {
|
||||
_, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
ResponseExpires: &exp,
|
||||
})
|
||||
return err
|
||||
},
|
||||
ExpectedErr: nil,
|
||||
},
|
||||
} {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
err := test.Call(ctx)
|
||||
cancel()
|
||||
if err == nil && test.ExpectedErr != nil {
|
||||
return fmt.Errorf("%v: expected err %v, instead got successful response", test.Action, test.ExpectedErr)
|
||||
}
|
||||
if err != nil {
|
||||
if test.ExpectedErr == nil {
|
||||
return fmt.Errorf("%v: expected no error, instead got %v", test.Action, err)
|
||||
}
|
||||
|
||||
apiErr, ok := test.ExpectedErr.(s3err.APIError)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid error type provided in the test, expected s3err.APIError")
|
||||
}
|
||||
|
||||
if err := checkApiErr(err, apiErr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func GetObject_overrides_presign_success(s *S3Conf) error {
|
||||
testName := "GetObject_overrides_presign_success"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
// Test data
|
||||
objKey := "test-object"
|
||||
objContent := "test content for response overrides"
|
||||
|
||||
// Put an object first
|
||||
_, err := s3client.PutObject(context.Background(), &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
Body: strings.NewReader(objContent),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to put object: %v", err)
|
||||
}
|
||||
|
||||
// Test cases for each response override parameter
|
||||
testCases := []struct {
|
||||
name string
|
||||
queryParam string
|
||||
expectedHeader string
|
||||
expectedValue string
|
||||
}{
|
||||
{
|
||||
name: "response-cache-control",
|
||||
queryParam: "response-cache-control=no-cache",
|
||||
expectedHeader: "Cache-Control",
|
||||
expectedValue: "no-cache",
|
||||
},
|
||||
{
|
||||
name: "response-content-disposition",
|
||||
queryParam: "response-content-disposition=attachment%3B%20filename%3D%22test.txt%22",
|
||||
expectedHeader: "Content-Disposition",
|
||||
expectedValue: "attachment; filename=\"test.txt\"",
|
||||
},
|
||||
{
|
||||
name: "response-content-encoding",
|
||||
queryParam: "response-content-encoding=txt",
|
||||
expectedHeader: "Content-Encoding",
|
||||
expectedValue: "txt",
|
||||
},
|
||||
{
|
||||
name: "response-content-language",
|
||||
queryParam: "response-content-language=en-US",
|
||||
expectedHeader: "Content-Language",
|
||||
expectedValue: "en-US",
|
||||
},
|
||||
{
|
||||
name: "response-content-type",
|
||||
queryParam: "response-content-type=text%2Fplain",
|
||||
expectedHeader: "Content-Type",
|
||||
expectedValue: "text/plain",
|
||||
},
|
||||
{
|
||||
name: "response-expires",
|
||||
queryParam: "response-expires=Thu%2C%2001%20Dec%202024%2016%3A00%3A00%20GMT",
|
||||
expectedHeader: "Expires",
|
||||
expectedValue: "Thu, 01 Dec 2024 16:00:00 GMT",
|
||||
},
|
||||
}
|
||||
|
||||
// Test each override parameter individually
|
||||
for _, tc := range testCases {
|
||||
// Create a signed request with the response override parameter
|
||||
req, err := createSignedReq(
|
||||
http.MethodGet,
|
||||
s.endpoint,
|
||||
fmt.Sprintf("%s/%s?%s", bucket, objKey, tc.queryParam),
|
||||
s.awsID,
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
nil,
|
||||
time.Now(),
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create signed request for %s: %v", tc.name, err)
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute request for %s: %v", tc.name, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("expected status 200 for %s, got %d", tc.name, resp.StatusCode)
|
||||
}
|
||||
|
||||
// Verify the response override header is set correctly
|
||||
actualValue := resp.Header.Get(tc.expectedHeader)
|
||||
if actualValue != tc.expectedValue {
|
||||
return fmt.Errorf("expected %s header to be %q for %s, got %q",
|
||||
tc.expectedHeader, tc.expectedValue, tc.name, actualValue)
|
||||
}
|
||||
|
||||
// Verify content is still correct
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read response body for %s: %v", tc.name, err)
|
||||
}
|
||||
|
||||
if string(body) != objContent {
|
||||
return fmt.Errorf("expected content %q for %s, got %q", objContent, tc.name, string(body))
|
||||
}
|
||||
}
|
||||
|
||||
// Test multiple override parameters together
|
||||
multiParam := "response-cache-control=max-age%3D3600&response-content-type=application%2Fjson&response-content-disposition=inline"
|
||||
req, err := createSignedReq(
|
||||
http.MethodGet,
|
||||
s.endpoint,
|
||||
fmt.Sprintf("%s/%s?%s", bucket, objKey, multiParam),
|
||||
s.awsID,
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
nil,
|
||||
time.Now(),
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create signed request for multiple overrides: %v", err)
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute request for multiple overrides: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("expected status 200 for multiple overrides, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Verify all override headers are set correctly
|
||||
expectedHeaders := map[string]string{
|
||||
"Cache-Control": "max-age=3600",
|
||||
"Content-Type": "application/json",
|
||||
"Content-Disposition": "inline",
|
||||
}
|
||||
|
||||
for headerName, expectedValue := range expectedHeaders {
|
||||
actualValue := resp.Header.Get(headerName)
|
||||
if actualValue != expectedValue {
|
||||
return fmt.Errorf("expected %s header to be %q for multiple overrides, got %q",
|
||||
headerName, expectedValue, actualValue)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func GetObject_overrides_fail_public(s *S3Conf) error {
|
||||
testName := "GetObject_overrides_fail_public"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
rootClient := s.GetClient()
|
||||
// Grant public access to the bucket for bucket operations
|
||||
err := grantPublicBucketPolicy(rootClient, bucket, policyTypeObject)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Test data
|
||||
objKey := "test-object"
|
||||
objContent := "test content for response overrides"
|
||||
exp := time.Now()
|
||||
|
||||
// Put an object first
|
||||
_, err = rootClient.PutObject(context.Background(), &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
Body: strings.NewReader(objContent),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to put object: %v", err)
|
||||
}
|
||||
|
||||
for _, test := range []PublicBucketTestCase{
|
||||
{
|
||||
Action: "GetObject",
|
||||
Call: func(ctx context.Context) error {
|
||||
_, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
ResponseCacheControl: getPtr("max-age=90"),
|
||||
})
|
||||
return err
|
||||
},
|
||||
ExpectedErr: s3err.GetAPIError(s3err.ErrAnonymousResponseHeaders),
|
||||
},
|
||||
{
|
||||
Action: "GetObject",
|
||||
Call: func(ctx context.Context) error {
|
||||
_, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
ResponseContentDisposition: getPtr("inline"),
|
||||
})
|
||||
return err
|
||||
},
|
||||
ExpectedErr: s3err.GetAPIError(s3err.ErrAnonymousResponseHeaders),
|
||||
},
|
||||
{
|
||||
Action: "GetObject",
|
||||
Call: func(ctx context.Context) error {
|
||||
_, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
ResponseContentEncoding: getPtr("txt"),
|
||||
})
|
||||
return err
|
||||
},
|
||||
ExpectedErr: s3err.GetAPIError(s3err.ErrAnonymousResponseHeaders),
|
||||
},
|
||||
{
|
||||
Action: "GetObject",
|
||||
Call: func(ctx context.Context) error {
|
||||
_, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
ResponseContentLanguage: getPtr("en"),
|
||||
})
|
||||
return err
|
||||
},
|
||||
ExpectedErr: s3err.GetAPIError(s3err.ErrAnonymousResponseHeaders),
|
||||
},
|
||||
{
|
||||
Action: "GetObject",
|
||||
Call: func(ctx context.Context) error {
|
||||
_, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
ResponseContentType: getPtr("application/json"),
|
||||
})
|
||||
return err
|
||||
},
|
||||
ExpectedErr: s3err.GetAPIError(s3err.ErrAnonymousResponseHeaders),
|
||||
},
|
||||
{
|
||||
Action: "GetObject",
|
||||
Call: func(ctx context.Context) error {
|
||||
_, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objKey,
|
||||
ResponseExpires: &exp,
|
||||
})
|
||||
return err
|
||||
},
|
||||
ExpectedErr: s3err.GetAPIError(s3err.ErrAnonymousResponseHeaders),
|
||||
},
|
||||
} {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
err := test.Call(ctx)
|
||||
cancel()
|
||||
if err == nil && test.ExpectedErr != nil {
|
||||
return fmt.Errorf("%v: expected err %v, instead got successful response", test.Action, test.ExpectedErr)
|
||||
}
|
||||
if err != nil {
|
||||
if test.ExpectedErr == nil {
|
||||
return fmt.Errorf("%v: expected no error, instead got %v", test.Action, err)
|
||||
}
|
||||
|
||||
apiErr, ok := test.ExpectedErr.(s3err.APIError)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid error type provided in the test, expected s3err.APIError")
|
||||
}
|
||||
|
||||
if err := checkApiErr(err, apiErr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}, withAnonymousClient())
|
||||
}
|
||||
|
||||
func ListObjects_non_existing_bucket(s *S3Conf) error {
|
||||
testName := "ListObjects_non_existing_bucket"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -17194,8 +17593,8 @@ func AccessControl_copy_object_with_starting_slash_for_user(s *S3Conf) error {
|
||||
}
|
||||
|
||||
// Public bucket tests
|
||||
func PublicBucket_default_privet_bucket(s *S3Conf) error {
|
||||
testName := "PublicBucket_default_privet_bucket"
|
||||
func PublicBucket_default_private_bucket(s *S3Conf) error {
|
||||
testName := "PublicBucket_default_private_bucket"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
partNumber := int32(1)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user