feat: implements unit tests for object PUT controllers

This commit is contained in:
niksis02
2025-07-22 20:55:22 +04:00
parent ba76aea17a
commit 7f9ab35347
4 changed files with 1310 additions and 146 deletions
+2 -1
View File
@@ -17,6 +17,7 @@ package auth
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/versity/versitygw/s3err"
@@ -194,7 +195,7 @@ func ValidatePolicyDocument(policyBin []byte, bucket string, iam IAMService) err
func VerifyBucketPolicy(policy []byte, access, bucket, object string, action Action) error {
var bucketPolicy BucketPolicy
if err := json.Unmarshal(policy, &bucketPolicy); err != nil {
return err
return fmt.Errorf("failed to parse the bucket policy: %w", err)
}
resource := bucket
+9 -9
View File
@@ -54,15 +54,15 @@ var (
)
type testInput struct {
bucket string
object string
body []byte
locals map[utils.ContextKey]any
headers map[string]string
queries map[string]string
beRes any
beErr error
extraMockErr error
bucket string
body []byte
locals map[utils.ContextKey]any
headers map[string]string
queries map[string]string
beRes any
beErr error
extraMockErr error
extraMockResp any
}
type testOutput struct {
+56 -136
View File
@@ -42,16 +42,7 @@ func (c S3ApiController) PutObjectTagging(ctx *fiber.Ctx) (*Response, error) {
IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx)
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
tagging, err := utils.ParseTagging(ctx.Body(), utils.TagLimitObject)
if err != nil {
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, err
}
err = auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
Readonly: c.readonly,
Acl: parsedAcl,
AclPermission: auth.PermissionWrite,
@@ -70,6 +61,15 @@ func (c S3ApiController) PutObjectTagging(ctx *fiber.Ctx) (*Response, error) {
}, err
}
tagging, err := utils.ParseTagging(ctx.Body(), utils.TagLimitObject)
if err != nil {
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, err
}
err = c.be.PutObjectTagging(ctx.Context(), bucket, key, tagging)
return &Response{
MetaOpts: &MetaOptions{
@@ -145,6 +145,24 @@ func (c S3ApiController) PutObjectLegalHold(ctx *fiber.Ctx) (*Response, error) {
IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx)
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,
}); err != nil {
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, err
}
var legalHold types.ObjectLockLegalHold
if err := xml.Unmarshal(ctx.Body(), &legalHold); err != nil {
debuglogger.Logf("failed to parse request body: %v", err)
@@ -164,24 +182,6 @@ func (c S3ApiController) PutObjectLegalHold(ctx *fiber.Ctx) (*Response, error) {
}, s3err.GetAPIError(s3err.ErrMalformedXML)
}
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,
}); err != nil {
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, err
}
err := c.be.PutObjectLegalHold(ctx.Context(), bucket, key, versionId, legalHold.Status == types.ObjectLockLegalHoldStatusOn)
return &Response{
MetaOpts: &MetaOptions{
@@ -212,15 +212,6 @@ func (c S3ApiController) UploadPart(ctx *fiber.Ctx) (*Response, error) {
contentLengthStr = decodedLength
}
if partNumber < 1 || partNumber > 10000 {
debuglogger.Logf("invalid part number: %d", partNumber)
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, s3err.GetAPIError(s3err.ErrInvalidPartNumber)
}
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
Readonly: c.readonly,
@@ -241,6 +232,15 @@ func (c S3ApiController) UploadPart(ctx *fiber.Ctx) (*Response, error) {
}, err
}
if partNumber < 1 || partNumber > 10000 {
debuglogger.Logf("invalid part number: %d", partNumber)
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, s3err.GetAPIError(s3err.ErrInvalidPartNumber)
}
contentLength, err := strconv.ParseInt(contentLengthStr, 10, 64)
if err != nil {
debuglogger.Logf("error parsing content length %q: %v", contentLengthStr, err)
@@ -329,15 +329,6 @@ func (c S3ApiController) UploadPartCopy(ctx *fiber.Ctx) (*Response, error) {
}, s3err.GetAPIError(s3err.ErrInvalidCopySource)
}
if partNumber < 1 || partNumber > 10000 {
debuglogger.Logf("invalid part number: %d", partNumber)
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, s3err.GetAPIError(s3err.ErrInvalidPartNumber)
}
err = auth.VerifyObjectCopyAccess(ctx.Context(), c.be, copySource,
auth.AccessOptions{
Acl: parsedAcl,
@@ -357,6 +348,15 @@ func (c S3ApiController) UploadPartCopy(ctx *fiber.Ctx) (*Response, error) {
}, err
}
if partNumber < 1 || partNumber > 10000 {
debuglogger.Logf("invalid part number: %d", partNumber)
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, s3err.GetAPIError(s3err.ErrInvalidPartNumber)
}
resp, err := c.be.UploadPartCopy(ctx.Context(),
&s3.UploadPartCopyInput{
Bucket: &bucket,
@@ -390,7 +390,6 @@ func (c S3ApiController) PutObjectAcl(ctx *fiber.Ctx) (*Response, error) {
grantReadACP := ctx.Get("X-Amz-Grant-Read-Acp")
granWrite := ctx.Get("X-Amz-Grant-Write")
grantWriteACP := ctx.Get("X-Amz-Grant-Write-Acp")
grants := grantFullControl + grantRead + grantReadACP + granWrite + grantWriteACP
// context locals
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)
@@ -415,95 +414,16 @@ func (c S3ApiController) PutObjectAcl(ctx *fiber.Ctx) (*Response, error) {
}, err
}
var input *s3.PutObjectAclInput
if len(ctx.Body()) > 0 {
if grants+acl != "" {
debuglogger.Logf("invalid request: %q (grants) %q (acl)", grants, acl)
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, s3err.GetAPIError(s3err.ErrInvalidRequest)
}
var accessControlPolicy auth.AccessControlPolicy
err := xml.Unmarshal(ctx.Body(), &accessControlPolicy)
if err != nil {
debuglogger.Logf("error unmarshalling access control policy: %v", err)
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, s3err.GetAPIError(s3err.ErrInvalidRequest)
}
//TODO: This part will be changed when object acls are implemented
grants := []types.Grant{}
for _, grt := range accessControlPolicy.AccessControlList.Grants {
grants = append(grants, types.Grant{
Grantee: &types.Grantee{
ID: &grt.Grantee.ID,
Type: grt.Grantee.Type,
},
Permission: types.Permission(grt.Permission),
})
}
input = &s3.PutObjectAclInput{
Bucket: &bucket,
Key: &key,
ACL: "",
AccessControlPolicy: &types.AccessControlPolicy{
Owner: accessControlPolicy.Owner,
Grants: grants,
},
}
}
if acl != "" {
if acl != "private" && acl != "public-read" && acl != "public-read-write" {
debuglogger.Logf("invalid acl: %q", acl)
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, s3err.GetAPIError(s3err.ErrInvalidRequest)
}
if len(ctx.Body()) > 0 || grants != "" {
debuglogger.Logf("invalid request: %q (grants) %q (acl) %v (body len)", grants, acl, len(ctx.Body()))
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, s3err.GetAPIError(s3err.ErrInvalidRequest)
}
input = &s3.PutObjectAclInput{
Bucket: &bucket,
Key: &key,
ACL: types.ObjectCannedACL(acl),
AccessControlPolicy: &types.AccessControlPolicy{
Owner: &types.Owner{ID: &parsedAcl.Owner},
},
}
}
if grants != "" {
input = &s3.PutObjectAclInput{
Bucket: &bucket,
Key: &key,
GrantFullControl: &grantFullControl,
GrantRead: &grantRead,
GrantReadACP: &grantReadACP,
GrantWrite: &granWrite,
GrantWriteACP: &grantWriteACP,
AccessControlPolicy: &types.AccessControlPolicy{
Owner: &types.Owner{ID: &parsedAcl.Owner},
},
ACL: "",
}
}
err = c.be.PutObjectAcl(ctx.Context(), input)
err = c.be.PutObjectAcl(ctx.Context(), &s3.PutObjectAclInput{
Bucket: &bucket,
Key: &key,
GrantFullControl: &grantFullControl,
GrantRead: &grantRead,
GrantWrite: &granWrite,
ACL: types.ObjectCannedACL(acl),
GrantReadACP: &grantReadACP,
GrantWriteACP: &grantWriteACP,
})
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
File diff suppressed because it is too large Load Diff