mirror of
https://github.com/versity/versitygw.git
synced 2026-01-05 03:24:04 +00:00
feat: ACL set up finished: added VerifyACL function, added admin checker function on list buckets, fixed all the unit tests
This commit is contained in:
@@ -14,7 +14,15 @@
|
||||
|
||||
package auth
|
||||
|
||||
import "github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/pkg/xattr"
|
||||
"github.com/versity/versitygw/s3err"
|
||||
)
|
||||
|
||||
type ACL struct {
|
||||
ACL types.BucketCannedACL
|
||||
@@ -35,3 +43,88 @@ type GetBucketAclOutput struct {
|
||||
type AccessControlList struct {
|
||||
Grants []types.Grant
|
||||
}
|
||||
|
||||
type ACLService interface {
|
||||
VerifyACL(bucket, access string, permission types.Permission, isRoot bool) error
|
||||
IsAdmin(access string, isRoot bool) error
|
||||
}
|
||||
|
||||
type ACLServiceUnsupported struct{}
|
||||
|
||||
var _ ACLService = &ACLServiceUnsupported{}
|
||||
|
||||
func (ACLServiceUnsupported) VerifyACL(bucket, access string, permission types.Permission, isRoot bool) error {
|
||||
var ACL ACL
|
||||
|
||||
if isRoot {
|
||||
return nil
|
||||
}
|
||||
|
||||
acl, err := xattr.Get(bucket, "user.acl")
|
||||
if err != nil {
|
||||
return fmt.Errorf("get acl: %w", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(acl, &ACL); err != nil {
|
||||
return fmt.Errorf("parse acl: %w", err)
|
||||
}
|
||||
if ACL.Owner == access {
|
||||
return nil
|
||||
}
|
||||
|
||||
if ACL.ACL != "" {
|
||||
if (permission == "READ" || permission == "READ_ACP") && (ACL.ACL != "public-read" && ACL.ACL != "public-read-write") {
|
||||
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||
}
|
||||
if (permission == "WRITE" || permission == "WRITE_ACP") && ACL.ACL != "public-read-write" {
|
||||
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
grantee := Grantee{Access: access, Permission: permission}
|
||||
granteeFullCtrl := Grantee{Access: access, Permission: "FULL_CONTROL"}
|
||||
|
||||
isFound := false
|
||||
|
||||
for _, grt := range ACL.Grantees {
|
||||
if grt == grantee || grt == granteeFullCtrl {
|
||||
isFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isFound {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||
}
|
||||
|
||||
func (ACLServiceUnsupported) IsAdmin(access string, isRoot bool) error {
|
||||
var data IAMConfig
|
||||
|
||||
if isRoot {
|
||||
return nil
|
||||
}
|
||||
|
||||
file, err := os.ReadFile("users.json")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read config file: %w", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(file, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
acc, ok := data.AccessAccounts[access]
|
||||
if !ok {
|
||||
return fmt.Errorf("user does not exist")
|
||||
}
|
||||
|
||||
if acc.Role == "admin" {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("only admin users have access to this resource")
|
||||
}
|
||||
|
||||
@@ -1114,66 +1114,60 @@ func (p *Posix) PutBucketAcl(input *s3.PutBucketAclInput) error {
|
||||
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||
}
|
||||
|
||||
grantees := []auth.Grantee{}
|
||||
|
||||
fullControlList, readList, readACPList, writeList, writeACPList := []string{}, []string{}, []string{}, []string{}, []string{}
|
||||
|
||||
if *input.GrantFullControl != "" {
|
||||
fullControlList = strings.Split(*input.GrantFullControl, ",")
|
||||
for _, str := range fullControlList {
|
||||
grantees = append(grantees, auth.Grantee{Access: str, Permission: "FULL_CONTROL"})
|
||||
}
|
||||
}
|
||||
if *input.GrantRead != "" {
|
||||
readList = strings.Split(*input.GrantRead, ",")
|
||||
for _, str := range readList {
|
||||
grantees = append(grantees, auth.Grantee{Access: str, Permission: "READ"})
|
||||
}
|
||||
}
|
||||
if *input.GrantReadACP != "" {
|
||||
readACPList = strings.Split(*input.GrantReadACP, ",")
|
||||
for _, str := range readACPList {
|
||||
grantees = append(grantees, auth.Grantee{Access: str, Permission: "READ_ACP"})
|
||||
}
|
||||
}
|
||||
if *input.GrantWrite != "" {
|
||||
writeList = strings.Split(*input.GrantWrite, ",")
|
||||
for _, str := range writeList {
|
||||
grantees = append(grantees, auth.Grantee{Access: str, Permission: "WRITE"})
|
||||
}
|
||||
}
|
||||
if *input.GrantWriteACP != "" {
|
||||
writeACPList = strings.Split(*input.GrantWriteACP, ",")
|
||||
for _, str := range writeACPList {
|
||||
grantees = append(grantees, auth.Grantee{Access: str, Permission: "WRITE_ACP"})
|
||||
}
|
||||
}
|
||||
|
||||
accs := append(append(append(append(fullControlList, readList...), writeACPList...), readACPList...), writeList...)
|
||||
|
||||
accList, err := checkIfAccountsExist(accs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(accList) > 0 {
|
||||
return fmt.Errorf("accounts does not exist: %s", strings.Join(accList, ", "))
|
||||
}
|
||||
|
||||
for _, elem := range grantees {
|
||||
doesContain := false
|
||||
for _, grantee := range ACL.Grantees {
|
||||
if elem == grantee {
|
||||
doesContain = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !doesContain {
|
||||
ACL.Grantees = append(ACL.Grantees, elem)
|
||||
}
|
||||
}
|
||||
|
||||
// if the ACL is specified, set the ACL, else replace the grantees
|
||||
if input.ACL != "" {
|
||||
ACL.ACL = input.ACL
|
||||
ACL.Grantees = []auth.Grantee{}
|
||||
} else {
|
||||
grantees := []auth.Grantee{}
|
||||
|
||||
fullControlList, readList, readACPList, writeList, writeACPList := []string{}, []string{}, []string{}, []string{}, []string{}
|
||||
|
||||
if *input.GrantFullControl != "" {
|
||||
fullControlList = splitUnique(*input.GrantFullControl, ",")
|
||||
fmt.Println(fullControlList)
|
||||
for _, str := range fullControlList {
|
||||
grantees = append(grantees, auth.Grantee{Access: str, Permission: "FULL_CONTROL"})
|
||||
}
|
||||
}
|
||||
if *input.GrantRead != "" {
|
||||
readList = splitUnique(*input.GrantRead, ",")
|
||||
for _, str := range readList {
|
||||
grantees = append(grantees, auth.Grantee{Access: str, Permission: "READ"})
|
||||
}
|
||||
}
|
||||
if *input.GrantReadACP != "" {
|
||||
readACPList = splitUnique(*input.GrantReadACP, ",")
|
||||
for _, str := range readACPList {
|
||||
grantees = append(grantees, auth.Grantee{Access: str, Permission: "READ_ACP"})
|
||||
}
|
||||
}
|
||||
if *input.GrantWrite != "" {
|
||||
writeList = splitUnique(*input.GrantWrite, ",")
|
||||
for _, str := range writeList {
|
||||
grantees = append(grantees, auth.Grantee{Access: str, Permission: "WRITE"})
|
||||
}
|
||||
}
|
||||
if *input.GrantWriteACP != "" {
|
||||
writeACPList = splitUnique(*input.GrantWriteACP, ",")
|
||||
for _, str := range writeACPList {
|
||||
grantees = append(grantees, auth.Grantee{Access: str, Permission: "WRITE_ACP"})
|
||||
}
|
||||
}
|
||||
|
||||
accs := append(append(append(append(fullControlList, readList...), writeACPList...), readACPList...), writeList...)
|
||||
|
||||
// Check if the specified accounts exist
|
||||
accList, err := checkIfAccountsExist(accs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(accList) > 0 {
|
||||
return fmt.Errorf("accounts does not exist: %s", strings.Join(accList, ", "))
|
||||
}
|
||||
|
||||
ACL.Grantees = grantees
|
||||
ACL.ACL = ""
|
||||
}
|
||||
|
||||
ACLJson, err := json.Marshal(ACL)
|
||||
@@ -1202,7 +1196,8 @@ func (p *Posix) GetBucketAcl(bucket string) (*auth.GetBucketAclOutput, error) {
|
||||
grants := []types.Grant{}
|
||||
|
||||
for _, elem := range ACL.Grantees {
|
||||
grants = append(grants, types.Grant{Grantee: &types.Grantee{ID: &elem.Access}, Permission: elem.Permission})
|
||||
acs := elem.Access
|
||||
grants = append(grants, types.Grant{Grantee: &types.Grantee{ID: &acs}, Permission: elem.Permission})
|
||||
}
|
||||
|
||||
return &auth.GetBucketAclOutput{
|
||||
@@ -1323,3 +1318,18 @@ func checkIfAccountsExist(accs []string) ([]string, error) {
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func splitUnique(s, divider string) []string {
|
||||
elements := strings.Split(s, divider)
|
||||
uniqueElements := make(map[string]bool)
|
||||
result := make([]string, 0, len(elements))
|
||||
|
||||
for _, element := range elements {
|
||||
if _, ok := uniqueElements[element]; !ok {
|
||||
result = append(result, element)
|
||||
uniqueElements[element] = true
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -30,19 +30,25 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/versity/versitygw/backend"
|
||||
"github.com/versity/versitygw/backend/auth"
|
||||
"github.com/versity/versitygw/s3api/utils"
|
||||
"github.com/versity/versitygw/s3err"
|
||||
)
|
||||
|
||||
type S3ApiController struct {
|
||||
be backend.Backend
|
||||
be backend.Backend
|
||||
acl auth.ACLService
|
||||
}
|
||||
|
||||
func New(be backend.Backend) S3ApiController {
|
||||
return S3ApiController{be: be}
|
||||
return S3ApiController{be: be, acl: auth.ACLServiceUnsupported{}}
|
||||
}
|
||||
|
||||
func (c S3ApiController) ListBuckets(ctx *fiber.Ctx) error {
|
||||
access, isRoot := ctx.Locals("access").(string), ctx.Locals("isRoot").(bool)
|
||||
if err := c.acl.IsAdmin(access, isRoot); err != nil {
|
||||
return SendXMLResponse(ctx, nil, err)
|
||||
}
|
||||
res, err := c.be.ListBuckets()
|
||||
return SendXMLResponse(ctx, res, err)
|
||||
}
|
||||
@@ -55,6 +61,8 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
|
||||
maxParts := ctx.QueryInt("max-parts", 0)
|
||||
partNumberMarker := ctx.QueryInt("part-number-marker", 0)
|
||||
acceptRange := ctx.Get("Range")
|
||||
access := ctx.Locals("access").(string)
|
||||
isRoot := ctx.Locals("isRoot").(bool)
|
||||
if keyEnd != "" {
|
||||
key = strings.Join([]string{key, keyEnd}, "/")
|
||||
}
|
||||
@@ -66,20 +74,35 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
|
||||
if partNumberMarker < 0 || (partNumberMarker == 0 && ctx.Query("part-number-marker") != "") {
|
||||
return SendResponse(ctx, s3err.GetAPIError(s3err.ErrInvalidPartNumberMarker))
|
||||
}
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "READ", isRoot); err != nil {
|
||||
return SendXMLResponse(ctx, nil, err)
|
||||
}
|
||||
|
||||
res, err := c.be.ListObjectParts(bucket, key, uploadId, partNumberMarker, maxParts)
|
||||
return SendXMLResponse(ctx, res, err)
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("acl") {
|
||||
if err := c.acl.VerifyACL(bucket, access, "READ_ACP", isRoot); err != nil {
|
||||
return SendXMLResponse(ctx, nil, err)
|
||||
}
|
||||
res, err := c.be.GetObjectAcl(bucket, key)
|
||||
return SendXMLResponse(ctx, res, err)
|
||||
}
|
||||
|
||||
if attrs := ctx.Get("X-Amz-Object-Attributes"); attrs != "" {
|
||||
if err := c.acl.VerifyACL(bucket, access, "READ", isRoot); err != nil {
|
||||
return SendXMLResponse(ctx, nil, err)
|
||||
}
|
||||
res, err := c.be.GetObjectAttributes(bucket, key, strings.Split(attrs, ","))
|
||||
return SendXMLResponse(ctx, res, err)
|
||||
}
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "READ_ACP", isRoot); err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
res, err := c.be.GetObject(bucket, key, acceptRange, ctx.Response().BodyWriter())
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
@@ -131,28 +154,43 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
||||
marker := ctx.Query("continuation-token")
|
||||
delimiter := ctx.Query("delimiter")
|
||||
maxkeys := ctx.QueryInt("max-keys")
|
||||
access := ctx.Locals("access").(string)
|
||||
isRoot := ctx.Locals("isRoot").(bool)
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("acl") {
|
||||
if err := c.acl.VerifyACL(bucket, access, "READ_ACP", isRoot); err != nil {
|
||||
return SendXMLResponse(ctx, nil, err)
|
||||
}
|
||||
res, err := c.be.GetBucketAcl(ctx.Params("bucket"))
|
||||
return SendXMLResponse(ctx, res, err)
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("uploads") {
|
||||
if err := c.acl.VerifyACL(bucket, access, "READ", isRoot); err != nil {
|
||||
return SendXMLResponse(ctx, nil, err)
|
||||
}
|
||||
res, err := c.be.ListMultipartUploads(&s3.ListMultipartUploadsInput{Bucket: aws.String(ctx.Params("bucket"))})
|
||||
return SendXMLResponse(ctx, res, err)
|
||||
}
|
||||
|
||||
if ctx.QueryInt("list-type") == 2 {
|
||||
if err := c.acl.VerifyACL(bucket, access, "READ", isRoot); err != nil {
|
||||
return SendXMLResponse(ctx, nil, err)
|
||||
}
|
||||
res, err := c.be.ListObjectsV2(bucket, prefix, marker, delimiter, maxkeys)
|
||||
return SendXMLResponse(ctx, res, err)
|
||||
}
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "READ", isRoot); err != nil {
|
||||
return SendXMLResponse(ctx, nil, err)
|
||||
}
|
||||
|
||||
res, err := c.be.ListObjects(bucket, prefix, marker, delimiter, maxkeys)
|
||||
return SendXMLResponse(ctx, res, err)
|
||||
}
|
||||
|
||||
func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
bucket, acl, grantFullControl, grantRead, grantReadACP, granWrite, grantWriteACP, access :=
|
||||
bucket, acl, grantFullControl, grantRead, grantReadACP, granWrite, grantWriteACP, access, isRoot :=
|
||||
ctx.Params("bucket"),
|
||||
ctx.Get("X-Amz-Acl"),
|
||||
ctx.Get("X-Amz-Grant-Full-Control"),
|
||||
@@ -160,15 +198,24 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
ctx.Get("X-Amz-Grant-Read-Acp"),
|
||||
ctx.Get("X-Amz-Grant-Write"),
|
||||
ctx.Get("X-Amz-Grant-Write-Acp"),
|
||||
ctx.Locals("access")
|
||||
ctx.Locals("access").(string),
|
||||
ctx.Locals("isRoot").(bool)
|
||||
|
||||
owner := access.(string)
|
||||
grants := grantFullControl + grantRead + grantReadACP + granWrite + grantWriteACP
|
||||
|
||||
if grants != "" || acl != "" {
|
||||
if grants != "" && acl != "" {
|
||||
return errors.New("wrong api call")
|
||||
}
|
||||
|
||||
if acl != "" && acl != "private" && acl != "public-read" && acl != "public-read-write" {
|
||||
return errors.New("wrong api call")
|
||||
}
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "WRITE_ACP", isRoot); err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
err := c.be.PutBucketAcl(&s3.PutBucketAclInput{
|
||||
Bucket: &bucket,
|
||||
ACL: types.BucketCannedACL(acl),
|
||||
@@ -177,13 +224,13 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
GrantReadACP: &grantReadACP,
|
||||
GrantWrite: &granWrite,
|
||||
GrantWriteACP: &grantWriteACP,
|
||||
AccessControlPolicy: &types.AccessControlPolicy{Owner: &types.Owner{ID: &owner}},
|
||||
AccessControlPolicy: &types.AccessControlPolicy{Owner: &types.Owner{ID: &access}},
|
||||
})
|
||||
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
err := c.be.PutBucket(bucket, owner)
|
||||
err := c.be.PutBucket(bucket, access)
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
@@ -193,6 +240,8 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
||||
keyEnd := ctx.Params("*1")
|
||||
uploadId := ctx.Query("uploadId")
|
||||
partNumberStr := ctx.Query("partNumber")
|
||||
access := ctx.Locals("access").(string)
|
||||
isRoot := ctx.Locals("isRoot").(bool)
|
||||
|
||||
// Copy source headers
|
||||
copySource := ctx.Get("X-Amz-Copy-Source")
|
||||
@@ -237,6 +286,10 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
||||
return SendResponse(ctx, s3err.GetAPIError(s3err.ErrInvalidPart))
|
||||
}
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "WRITE", isRoot); err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
body := io.ReadSeeker(bytes.NewReader([]byte(ctx.Body())))
|
||||
etag, err := c.be.PutObjectPart(bucket, keyStart, uploadId,
|
||||
partNumber, contentLength, body)
|
||||
@@ -249,6 +302,10 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
||||
return errors.New("wrong api call")
|
||||
}
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "WRITE_ACP", isRoot); err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
err := c.be.PutObjectAcl(&s3.PutObjectAclInput{
|
||||
Bucket: &bucket,
|
||||
Key: &keyStart,
|
||||
@@ -268,12 +325,20 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
||||
copySourceSplit := strings.Split(copySource, "/")
|
||||
srcBucket, srcObject := copySourceSplit[0], copySourceSplit[1:]
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "WRITE", isRoot); err != nil {
|
||||
return SendXMLResponse(ctx, nil, err)
|
||||
}
|
||||
|
||||
res, err := c.be.CopyObject(srcBucket, strings.Join(srcObject, "/"), bucket, keyStart)
|
||||
return SendXMLResponse(ctx, res, err)
|
||||
}
|
||||
|
||||
metadata := utils.GetUserMetaData(&ctx.Request().Header)
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "WRITE", isRoot); err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
etag, err := c.be.PutObject(&s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &keyStart,
|
||||
@@ -286,17 +351,27 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) error {
|
||||
err := c.be.DeleteBucket(ctx.Params("bucket"))
|
||||
bucket, access, isRoot := ctx.Params("bucket"), ctx.Locals("access").(string), ctx.Locals("isRoot").(bool)
|
||||
if err := c.acl.VerifyACL(bucket, access, "WRITE", isRoot); err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
err := c.be.DeleteBucket(bucket)
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) error {
|
||||
bucket, access, isRoot := ctx.Params("bucket"), ctx.Locals("access").(string), ctx.Locals("isRoot").(bool)
|
||||
var dObj types.Delete
|
||||
|
||||
if err := xml.Unmarshal(ctx.Body(), &dObj); err != nil {
|
||||
return errors.New("wrong api call")
|
||||
}
|
||||
|
||||
err := c.be.DeleteObjects(ctx.Params("bucket"), &s3.DeleteObjectsInput{Delete: &dObj})
|
||||
if err := c.acl.VerifyACL(bucket, access, "WRITE", isRoot); err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
err := c.be.DeleteObjects(bucket, &s3.DeleteObjectsInput{Delete: &dObj})
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
@@ -305,6 +380,8 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
|
||||
key := ctx.Params("key")
|
||||
keyEnd := ctx.Params("*1")
|
||||
uploadId := ctx.Query("uploadId")
|
||||
access := ctx.Locals("access").(string)
|
||||
isRoot := ctx.Locals("isRoot").(bool)
|
||||
|
||||
if keyEnd != "" {
|
||||
key = strings.Join([]string{key, keyEnd}, "/")
|
||||
@@ -313,6 +390,10 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
|
||||
if uploadId != "" {
|
||||
expectedBucketOwner, requestPayer := ctx.Get("X-Amz-Expected-Bucket-Owner"), ctx.Get("X-Amz-Request-Payer")
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "WRITE", isRoot); err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
err := c.be.AbortMultipartUpload(&s3.AbortMultipartUploadInput{
|
||||
UploadId: &uploadId,
|
||||
Bucket: &bucket,
|
||||
@@ -323,12 +404,21 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "WRITE", isRoot); err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
err := c.be.DeleteObject(bucket, key)
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
func (c S3ApiController) HeadBucket(ctx *fiber.Ctx) error {
|
||||
_, err := c.be.HeadBucket(ctx.Params("bucket"))
|
||||
bucket, access, isRoot := ctx.Params("bucket"), ctx.Locals("access").(string), ctx.Locals("isRoot").(bool)
|
||||
if err := c.acl.VerifyACL(bucket, access, "READ", isRoot); err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
_, err := c.be.HeadBucket(bucket)
|
||||
// TODO: set bucket response headers
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
@@ -338,13 +428,17 @@ const (
|
||||
)
|
||||
|
||||
func (c S3ApiController) HeadObject(ctx *fiber.Ctx) error {
|
||||
bucket := ctx.Params("bucket")
|
||||
bucket, access, isRoot := ctx.Params("bucket"), ctx.Locals("access").(string), ctx.Locals("isRoot").(bool)
|
||||
key := ctx.Params("key")
|
||||
keyEnd := ctx.Params("*1")
|
||||
if keyEnd != "" {
|
||||
key = strings.Join([]string{key, keyEnd}, "/")
|
||||
}
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "READ", isRoot); err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
res, err := c.be.HeadObject(bucket, key)
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
@@ -389,6 +483,8 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
|
||||
key := ctx.Params("key")
|
||||
keyEnd := ctx.Params("*1")
|
||||
uploadId := ctx.Query("uploadId")
|
||||
access := ctx.Locals("access").(string)
|
||||
isRoot := ctx.Locals("isRoot").(bool)
|
||||
|
||||
if keyEnd != "" {
|
||||
key = strings.Join([]string{key, keyEnd}, "/")
|
||||
@@ -400,6 +496,11 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
|
||||
if xmlErr != nil {
|
||||
return errors.New("wrong api call")
|
||||
}
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "WRITE", isRoot); err != nil {
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
err := c.be.RestoreObject(bucket, key, &restoreRequest)
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
@@ -413,9 +514,18 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
|
||||
return errors.New("wrong api call")
|
||||
}
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "WRITE", isRoot); err != nil {
|
||||
return SendXMLResponse(ctx, nil, err)
|
||||
}
|
||||
|
||||
res, err := c.be.CompleteMultipartUpload(bucket, key, uploadId, data.Parts)
|
||||
return SendXMLResponse(ctx, res, err)
|
||||
}
|
||||
|
||||
if err := c.acl.VerifyACL(bucket, access, "WRITE", isRoot); err != nil {
|
||||
return SendXMLResponse(ctx, nil, err)
|
||||
}
|
||||
|
||||
res, err := c.be.CreateMultipartUpload(&s3.CreateMultipartUploadInput{Bucket: &bucket, Key: &key})
|
||||
return SendXMLResponse(ctx, res, err)
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ func TestNew(t *testing.T) {
|
||||
}
|
||||
|
||||
be := backend.BackendUnsupported{}
|
||||
acl := auth.ACLServiceUnsupported{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -51,7 +52,8 @@ func TestNew(t *testing.T) {
|
||||
be: be,
|
||||
},
|
||||
want: S3ApiController{
|
||||
be: be,
|
||||
be: be,
|
||||
acl: acl,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -66,58 +68,80 @@ func TestNew(t *testing.T) {
|
||||
|
||||
func TestS3ApiController_ListBuckets(t *testing.T) {
|
||||
type args struct {
|
||||
ctx *fiber.Ctx
|
||||
req *http.Request
|
||||
}
|
||||
|
||||
app := fiber.New()
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
ListBucketsFunc: func() (*s3.ListBucketsOutput, error) {
|
||||
return &s3.ListBucketsOutput{}, nil
|
||||
},
|
||||
},
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
|
||||
app.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
app.Get("/", s3ApiController.ListBuckets)
|
||||
|
||||
// Error case
|
||||
appErr := fiber.New()
|
||||
s3ApiControllerErr := S3ApiController{
|
||||
be: &BackendMock{
|
||||
ListBucketsFunc: func() (*s3.ListBucketsOutput, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrMethodNotAllowed)
|
||||
},
|
||||
},
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
|
||||
appErr.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
appErr.Get("/", s3ApiControllerErr.ListBuckets)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
c S3ApiController
|
||||
args args
|
||||
app *fiber.App
|
||||
wantErr bool
|
||||
statusCode int
|
||||
}{
|
||||
{
|
||||
name: "List-bucket-not-implemented",
|
||||
c: S3ApiController{
|
||||
be: backend.BackendUnsupported{},
|
||||
},
|
||||
name: "List-bucket-method-not-allowed",
|
||||
args: args{
|
||||
ctx: app.AcquireCtx(&fasthttp.RequestCtx{}),
|
||||
req: httptest.NewRequest(http.MethodGet, "/", nil),
|
||||
},
|
||||
app: appErr,
|
||||
wantErr: false,
|
||||
statusCode: 501,
|
||||
statusCode: 405,
|
||||
},
|
||||
{
|
||||
name: "list-bucket-success",
|
||||
c: S3ApiController{
|
||||
be: &BackendMock{
|
||||
ListBucketsFunc: func() (*s3.ListBucketsOutput, error) {
|
||||
return &s3.ListBucketsOutput{}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: app.AcquireCtx(&fasthttp.RequestCtx{}),
|
||||
req: httptest.NewRequest(http.MethodGet, "/", nil),
|
||||
},
|
||||
app: app,
|
||||
wantErr: false,
|
||||
statusCode: 200,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.c.ListBuckets(tt.args.ctx)
|
||||
resp, err := tt.app.Test(tt.args.req)
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("S3ApiController.ListBuckets() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
statusCode := tt.args.ctx.Response().StatusCode()
|
||||
|
||||
if statusCode != tt.statusCode {
|
||||
t.Errorf("S3ApiController.ListBuckets() code = %v, wantErr %v", statusCode, tt.wantErr)
|
||||
if resp.StatusCode != tt.statusCode {
|
||||
t.Errorf("S3ApiController.ListBuckets() statusCode = %v, wantStatusCode = %v", resp.StatusCode, tt.statusCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -129,20 +153,28 @@ func TestS3ApiController_GetActions(t *testing.T) {
|
||||
}
|
||||
|
||||
app := fiber.New()
|
||||
s3ApiController := S3ApiController{be: &BackendMock{
|
||||
ListObjectPartsFunc: func(bucket, object, uploadID string, partNumberMarker int, maxParts int) (s3response.ListPartsResponse, error) {
|
||||
return s3response.ListPartsResponse{}, nil
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
ListObjectPartsFunc: func(bucket, object, uploadID string, partNumberMarker int, maxParts int) (s3response.ListPartsResponse, error) {
|
||||
return s3response.ListPartsResponse{}, nil
|
||||
},
|
||||
GetObjectAclFunc: func(bucket, object string) (*s3.GetObjectAclOutput, error) {
|
||||
return &s3.GetObjectAclOutput{}, nil
|
||||
},
|
||||
GetObjectAttributesFunc: func(bucket, object string, attributes []string) (*s3.GetObjectAttributesOutput, error) {
|
||||
return &s3.GetObjectAttributesOutput{}, nil
|
||||
},
|
||||
GetObjectFunc: func(bucket, object, acceptRange string, writer io.Writer) (*s3.GetObjectOutput, error) {
|
||||
return &s3.GetObjectOutput{Metadata: nil}, nil
|
||||
},
|
||||
},
|
||||
GetObjectAclFunc: func(bucket, object string) (*s3.GetObjectAclOutput, error) {
|
||||
return &s3.GetObjectAclOutput{}, nil
|
||||
},
|
||||
GetObjectAttributesFunc: func(bucket, object string, attributes []string) (*s3.GetObjectAttributesOutput, error) {
|
||||
return &s3.GetObjectAttributesOutput{}, nil
|
||||
},
|
||||
GetObjectFunc: func(bucket, object, acceptRange string, writer io.Writer) (*s3.GetObjectOutput, error) {
|
||||
return &s3.GetObjectOutput{Metadata: nil}, nil
|
||||
},
|
||||
}}
|
||||
acl: &auth.ACLServiceUnsupported{},
|
||||
}
|
||||
app.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
app.Get("/:bucket/:key/*", s3ApiController.GetActions)
|
||||
|
||||
// GetObjectACL
|
||||
@@ -231,29 +263,47 @@ func TestS3ApiController_ListActions(t *testing.T) {
|
||||
}
|
||||
|
||||
app := fiber.New()
|
||||
s3ApiController := S3ApiController{be: &BackendMock{
|
||||
GetBucketAclFunc: func(bucket string) (*auth.GetBucketAclOutput, error) {
|
||||
return nil, nil
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
GetBucketAclFunc: func(bucket string) (*auth.GetBucketAclOutput, error) {
|
||||
return nil, nil
|
||||
},
|
||||
ListMultipartUploadsFunc: func(output *s3.ListMultipartUploadsInput) (s3response.ListMultipartUploadsResponse, error) {
|
||||
return s3response.ListMultipartUploadsResponse{}, nil
|
||||
},
|
||||
ListObjectsV2Func: func(bucket, prefix, marker, delim string, maxkeys int) (*s3.ListObjectsV2Output, error) {
|
||||
return &s3.ListObjectsV2Output{}, nil
|
||||
},
|
||||
ListObjectsFunc: func(bucket, prefix, marker, delim string, maxkeys int) (*s3.ListObjectsOutput, error) {
|
||||
return &s3.ListObjectsOutput{}, nil
|
||||
},
|
||||
},
|
||||
ListMultipartUploadsFunc: func(output *s3.ListMultipartUploadsInput) (s3response.ListMultipartUploadsResponse, error) {
|
||||
return s3response.ListMultipartUploadsResponse{}, nil
|
||||
},
|
||||
ListObjectsV2Func: func(bucket, prefix, marker, delim string, maxkeys int) (*s3.ListObjectsV2Output, error) {
|
||||
return &s3.ListObjectsV2Output{}, nil
|
||||
},
|
||||
ListObjectsFunc: func(bucket, prefix, marker, delim string, maxkeys int) (*s3.ListObjectsOutput, error) {
|
||||
return &s3.ListObjectsOutput{}, nil
|
||||
},
|
||||
}}
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
|
||||
app.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
|
||||
app.Get("/:bucket", s3ApiController.ListActions)
|
||||
|
||||
//Error case
|
||||
s3ApiControllerError := S3ApiController{be: &BackendMock{
|
||||
ListObjectsFunc: func(bucket, prefix, marker, delim string, maxkeys int) (*s3.ListObjectsOutput, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
s3ApiControllerError := S3ApiController{
|
||||
be: &BackendMock{
|
||||
ListObjectsFunc: func(bucket, prefix, marker, delim string, maxkeys int) (*s3.ListObjectsOutput, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
},
|
||||
},
|
||||
}}
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
appError := fiber.New()
|
||||
appError.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
appError.Get("/:bucket", s3ApiControllerError.ListActions)
|
||||
|
||||
tests := []struct {
|
||||
@@ -330,17 +380,21 @@ func TestS3ApiController_PutBucketActions(t *testing.T) {
|
||||
}
|
||||
|
||||
app := fiber.New()
|
||||
s3ApiController := S3ApiController{be: &BackendMock{
|
||||
PutBucketAclFunc: func(*s3.PutBucketAclInput) error {
|
||||
return nil
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
PutBucketAclFunc: func(*s3.PutBucketAclInput) error {
|
||||
return nil
|
||||
},
|
||||
PutBucketFunc: func(bucket, owner string) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
PutBucketFunc: func(bucket, owner string) error {
|
||||
return nil
|
||||
},
|
||||
}}
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
// Mock ctx.Locals
|
||||
app.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
app.Put("/:bucket", s3ApiController.PutBucketActions)
|
||||
@@ -408,20 +462,28 @@ func TestS3ApiController_PutActions(t *testing.T) {
|
||||
}
|
||||
|
||||
app := fiber.New()
|
||||
s3ApiController := S3ApiController{be: &BackendMock{
|
||||
UploadPartCopyFunc: func(*s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error) {
|
||||
return &s3.UploadPartCopyOutput{}, nil
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
UploadPartCopyFunc: func(*s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error) {
|
||||
return &s3.UploadPartCopyOutput{}, nil
|
||||
},
|
||||
PutObjectAclFunc: func(*s3.PutObjectAclInput) error {
|
||||
return nil
|
||||
},
|
||||
CopyObjectFunc: func(srcBucket, srcObject, DstBucket, dstObject string) (*s3.CopyObjectOutput, error) {
|
||||
return &s3.CopyObjectOutput{}, nil
|
||||
},
|
||||
PutObjectFunc: func(*s3.PutObjectInput) (string, error) {
|
||||
return "Hey", nil
|
||||
},
|
||||
},
|
||||
PutObjectAclFunc: func(*s3.PutObjectAclInput) error {
|
||||
return nil
|
||||
},
|
||||
CopyObjectFunc: func(srcBucket, srcObject, DstBucket, dstObject string) (*s3.CopyObjectOutput, error) {
|
||||
return &s3.CopyObjectOutput{}, nil
|
||||
},
|
||||
PutObjectFunc: func(*s3.PutObjectInput) (string, error) {
|
||||
return "Hey", nil
|
||||
},
|
||||
}}
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
app.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
app.Put("/:bucket/:key/*", s3ApiController.PutActions)
|
||||
|
||||
//PutObjectAcl error
|
||||
@@ -538,23 +600,40 @@ func TestS3ApiController_DeleteBucket(t *testing.T) {
|
||||
}
|
||||
|
||||
app := fiber.New()
|
||||
s3ApiController := S3ApiController{be: &BackendMock{
|
||||
DeleteBucketFunc: func(bucket string) error {
|
||||
return nil
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
DeleteBucketFunc: func(bucket string) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}}
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
|
||||
app.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
|
||||
app.Delete("/:bucket", s3ApiController.DeleteBucket)
|
||||
|
||||
// error case
|
||||
appErr := fiber.New()
|
||||
|
||||
s3ApiControllerErr := S3ApiController{be: &BackendMock{
|
||||
DeleteBucketFunc: func(bucket string) error {
|
||||
return s3err.GetAPIError(48)
|
||||
s3ApiControllerErr := S3ApiController{
|
||||
be: &BackendMock{
|
||||
DeleteBucketFunc: func(bucket string) error {
|
||||
return s3err.GetAPIError(48)
|
||||
},
|
||||
},
|
||||
}}
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
|
||||
appErr.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
appErr.Delete("/:bucket", s3ApiControllerErr.DeleteBucket)
|
||||
|
||||
tests := []struct {
|
||||
@@ -602,12 +681,20 @@ func TestS3ApiController_DeleteObjects(t *testing.T) {
|
||||
}
|
||||
|
||||
app := fiber.New()
|
||||
s3ApiController := S3ApiController{be: &BackendMock{
|
||||
DeleteObjectsFunc: func(bucket string, objects *s3.DeleteObjectsInput) error {
|
||||
return nil
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
DeleteObjectsFunc: func(bucket string, objects *s3.DeleteObjectsInput) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}}
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
|
||||
app.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
app.Post("/:bucket", s3ApiController.DeleteObjects)
|
||||
|
||||
// Valid request body
|
||||
@@ -661,15 +748,23 @@ func TestS3ApiController_DeleteActions(t *testing.T) {
|
||||
}
|
||||
|
||||
app := fiber.New()
|
||||
s3ApiController := S3ApiController{be: &BackendMock{
|
||||
DeleteObjectFunc: func(bucket, object string) error {
|
||||
return nil
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
DeleteObjectFunc: func(bucket, object string) error {
|
||||
return nil
|
||||
},
|
||||
AbortMultipartUploadFunc: func(*s3.AbortMultipartUploadInput) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
AbortMultipartUploadFunc: func(*s3.AbortMultipartUploadInput) error {
|
||||
return nil
|
||||
},
|
||||
}}
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
|
||||
app.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
app.Delete("/:bucket/:key/*", s3ApiController.DeleteActions)
|
||||
|
||||
//Error case
|
||||
@@ -681,6 +776,11 @@ func TestS3ApiController_DeleteActions(t *testing.T) {
|
||||
},
|
||||
}}
|
||||
|
||||
appErr.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
appErr.Delete("/:bucket", s3ApiControllerErr.DeleteBucket)
|
||||
|
||||
tests := []struct {
|
||||
@@ -737,22 +837,39 @@ func TestS3ApiController_HeadBucket(t *testing.T) {
|
||||
}
|
||||
|
||||
app := fiber.New()
|
||||
s3ApiController := S3ApiController{be: &BackendMock{
|
||||
HeadBucketFunc: func(bucket string) (*s3.HeadBucketOutput, error) {
|
||||
return &s3.HeadBucketOutput{}, nil
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
HeadBucketFunc: func(bucket string) (*s3.HeadBucketOutput, error) {
|
||||
return &s3.HeadBucketOutput{}, nil
|
||||
},
|
||||
},
|
||||
}}
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
|
||||
app.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
|
||||
app.Head("/:bucket", s3ApiController.HeadBucket)
|
||||
|
||||
//Error case
|
||||
// Error case
|
||||
appErr := fiber.New()
|
||||
|
||||
s3ApiControllerErr := S3ApiController{be: &BackendMock{
|
||||
HeadBucketFunc: func(bucket string) (*s3.HeadBucketOutput, error) {
|
||||
return nil, s3err.GetAPIError(3)
|
||||
},
|
||||
}}
|
||||
},
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
|
||||
appErr.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
|
||||
appErr.Head("/:bucket", s3ApiControllerErr.HeadBucket)
|
||||
|
||||
@@ -808,29 +925,45 @@ func TestS3ApiController_HeadObject(t *testing.T) {
|
||||
eTag := "Valid etag"
|
||||
lastModifie := time.Now()
|
||||
|
||||
s3ApiController := S3ApiController{be: &BackendMock{
|
||||
HeadObjectFunc: func(bucket, object string) (*s3.HeadObjectOutput, error) {
|
||||
return &s3.HeadObjectOutput{
|
||||
ContentEncoding: &contentEncoding,
|
||||
ContentLength: 64,
|
||||
ContentType: &contentType,
|
||||
LastModified: &lastModifie,
|
||||
ETag: &eTag,
|
||||
}, nil
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
HeadObjectFunc: func(bucket, object string) (*s3.HeadObjectOutput, error) {
|
||||
return &s3.HeadObjectOutput{
|
||||
ContentEncoding: &contentEncoding,
|
||||
ContentLength: 64,
|
||||
ContentType: &contentType,
|
||||
LastModified: &lastModifie,
|
||||
ETag: &eTag,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
}}
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
|
||||
app.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
app.Head("/:bucket/:key/*", s3ApiController.HeadObject)
|
||||
|
||||
//Error case
|
||||
appErr := fiber.New()
|
||||
|
||||
s3ApiControllerErr := S3ApiController{be: &BackendMock{
|
||||
HeadObjectFunc: func(bucket, object string) (*s3.HeadObjectOutput, error) {
|
||||
return nil, s3err.GetAPIError(42)
|
||||
s3ApiControllerErr := S3ApiController{
|
||||
be: &BackendMock{
|
||||
HeadObjectFunc: func(bucket, object string) (*s3.HeadObjectOutput, error) {
|
||||
return nil, s3err.GetAPIError(42)
|
||||
},
|
||||
},
|
||||
}}
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
|
||||
appErr.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
appErr.Head("/:bucket/:key/*", s3ApiControllerErr.HeadObject)
|
||||
|
||||
tests := []struct {
|
||||
@@ -877,18 +1010,26 @@ func TestS3ApiController_CreateActions(t *testing.T) {
|
||||
req *http.Request
|
||||
}
|
||||
app := fiber.New()
|
||||
s3ApiController := S3ApiController{be: &BackendMock{
|
||||
RestoreObjectFunc: func(bucket, object string, restoreRequest *s3.RestoreObjectInput) error {
|
||||
return nil
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
RestoreObjectFunc: func(bucket, object string, restoreRequest *s3.RestoreObjectInput) error {
|
||||
return nil
|
||||
},
|
||||
CompleteMultipartUploadFunc: func(bucket, object, uploadID string, parts []types.Part) (*s3.CompleteMultipartUploadOutput, error) {
|
||||
return &s3.CompleteMultipartUploadOutput{}, nil
|
||||
},
|
||||
CreateMultipartUploadFunc: func(*s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) {
|
||||
return &s3.CreateMultipartUploadOutput{}, nil
|
||||
},
|
||||
},
|
||||
CompleteMultipartUploadFunc: func(bucket, object, uploadID string, parts []types.Part) (*s3.CompleteMultipartUploadOutput, error) {
|
||||
return &s3.CompleteMultipartUploadOutput{}, nil
|
||||
},
|
||||
CreateMultipartUploadFunc: func(*s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) {
|
||||
return &s3.CreateMultipartUploadOutput{}, nil
|
||||
},
|
||||
}}
|
||||
acl: auth.ACLServiceUnsupported{},
|
||||
}
|
||||
|
||||
app.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
ctx.Locals("isRoot", true)
|
||||
return ctx.Next()
|
||||
})
|
||||
app.Post("/:bucket/:key/*", s3ApiController.CreateActions)
|
||||
|
||||
tests := []struct {
|
||||
|
||||
@@ -136,6 +136,7 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, debug bool) fib
|
||||
|
||||
ctx.Locals("role", account.Role)
|
||||
ctx.Locals("access", creds[0])
|
||||
ctx.Locals("isRoot", creds[0] == root.Access)
|
||||
|
||||
return ctx.Next()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user