mirror of
https://github.com/versity/versitygw.git
synced 2026-08-21 22:56:28 +00:00
feat: Implemented GetBucketACL, PutBucketACL posix functions, fixed a… (#92)
* feat: Implemented GetBucketACL, PutBucketACL posix functions, fixed authentication middleware signed headers bug * fix: Fixed GetBucketAcl return type, fixed staticcheck uppercase error, fixed unit tests for PutActions
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
// Copyright 2023 Versity Software
|
||||
// This file is licensed under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package auth
|
||||
|
||||
import "github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
|
||||
type ACL struct {
|
||||
ACL types.BucketCannedACL
|
||||
Owner string
|
||||
Grantees []Grantee
|
||||
}
|
||||
|
||||
type Grantee struct {
|
||||
Permission types.Permission
|
||||
Access string
|
||||
}
|
||||
|
||||
type GetBucketAclOutput struct {
|
||||
Owner *types.Owner
|
||||
AccessControlList AccessControlList
|
||||
}
|
||||
|
||||
type AccessControlList struct {
|
||||
Grants []types.Grant
|
||||
}
|
||||
+5
-4
@@ -20,6 +20,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/versity/versitygw/backend/auth"
|
||||
"github.com/versity/versitygw/s3err"
|
||||
"github.com/versity/versitygw/s3response"
|
||||
)
|
||||
@@ -32,8 +33,8 @@ type Backend interface {
|
||||
|
||||
ListBuckets() (*s3.ListBucketsOutput, error)
|
||||
HeadBucket(bucket string) (*s3.HeadBucketOutput, error)
|
||||
GetBucketAcl(bucket string) (*s3.GetBucketAclOutput, error)
|
||||
PutBucket(bucket string) error
|
||||
GetBucketAcl(bucket string) (*auth.GetBucketAclOutput, error)
|
||||
PutBucket(bucket, owner string) error
|
||||
PutBucketAcl(*s3.PutBucketAclInput) error
|
||||
DeleteBucket(bucket string) error
|
||||
|
||||
@@ -90,13 +91,13 @@ func (BackendUnsupported) RestoreObject(bucket, object string, restoreRequest *s
|
||||
func (BackendUnsupported) UploadPartCopy(*s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
}
|
||||
func (BackendUnsupported) GetBucketAcl(bucket string) (*s3.GetBucketAclOutput, error) {
|
||||
func (BackendUnsupported) GetBucketAcl(bucket string) (*auth.GetBucketAclOutput, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
}
|
||||
func (BackendUnsupported) HeadBucket(bucket string) (*s3.HeadBucketOutput, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
}
|
||||
func (BackendUnsupported) PutBucket(bucket string) error {
|
||||
func (BackendUnsupported) PutBucket(bucket, owner string) error {
|
||||
return s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
}
|
||||
func (BackendUnsupported) DeleteBucket(bucket string) error {
|
||||
|
||||
+14
-69
@@ -6,6 +6,7 @@ package backend
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/versity/versitygw/backend/auth"
|
||||
"github.com/versity/versitygw/s3response"
|
||||
"io"
|
||||
"sync"
|
||||
@@ -45,7 +46,7 @@ var _ Backend = &BackendMock{}
|
||||
// DeleteObjectsFunc: func(bucket string, objects *s3.DeleteObjectsInput) error {
|
||||
// panic("mock out the DeleteObjects method")
|
||||
// },
|
||||
// GetBucketAclFunc: func(bucket string) (*s3.GetBucketAclOutput, error) {
|
||||
// GetBucketAclFunc: func(bucket string) (*auth.GetBucketAclOutput, error) {
|
||||
// panic("mock out the GetBucketAcl method")
|
||||
// },
|
||||
// GetObjectFunc: func(bucket string, object string, acceptRange string, writer io.Writer) (*s3.GetObjectOutput, error) {
|
||||
@@ -81,7 +82,7 @@ var _ Backend = &BackendMock{}
|
||||
// ListObjectsV2Func: func(bucket string, prefix string, marker string, delim string, maxkeys int) (*s3.ListObjectsV2Output, error) {
|
||||
// panic("mock out the ListObjectsV2 method")
|
||||
// },
|
||||
// PutBucketFunc: func(bucket string) error {
|
||||
// PutBucketFunc: func(bucket string, owner string) error {
|
||||
// panic("mock out the PutBucket method")
|
||||
// },
|
||||
// PutBucketAclFunc: func(putBucketAclInput *s3.PutBucketAclInput) error {
|
||||
@@ -111,9 +112,6 @@ var _ Backend = &BackendMock{}
|
||||
// StringFunc: func() string {
|
||||
// panic("mock out the String method")
|
||||
// },
|
||||
// UploadPartFunc: func(bucket string, object string, uploadId string, Body io.ReadSeeker) (*s3.UploadPartOutput, error) {
|
||||
// panic("mock out the UploadPart method")
|
||||
// },
|
||||
// UploadPartCopyFunc: func(uploadPartCopyInput *s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error) {
|
||||
// panic("mock out the UploadPartCopy method")
|
||||
// },
|
||||
@@ -149,7 +147,7 @@ type BackendMock struct {
|
||||
DeleteObjectsFunc func(bucket string, objects *s3.DeleteObjectsInput) error
|
||||
|
||||
// GetBucketAclFunc mocks the GetBucketAcl method.
|
||||
GetBucketAclFunc func(bucket string) (*s3.GetBucketAclOutput, error)
|
||||
GetBucketAclFunc func(bucket string) (*auth.GetBucketAclOutput, error)
|
||||
|
||||
// GetObjectFunc mocks the GetObject method.
|
||||
GetObjectFunc func(bucket string, object string, acceptRange string, writer io.Writer) (*s3.GetObjectOutput, error)
|
||||
@@ -185,7 +183,7 @@ type BackendMock struct {
|
||||
ListObjectsV2Func func(bucket string, prefix string, marker string, delim string, maxkeys int) (*s3.ListObjectsV2Output, error)
|
||||
|
||||
// PutBucketFunc mocks the PutBucket method.
|
||||
PutBucketFunc func(bucket string) error
|
||||
PutBucketFunc func(bucket string, owner string) error
|
||||
|
||||
// PutBucketAclFunc mocks the PutBucketAcl method.
|
||||
PutBucketAclFunc func(putBucketAclInput *s3.PutBucketAclInput) error
|
||||
@@ -214,9 +212,6 @@ type BackendMock struct {
|
||||
// StringFunc mocks the String method.
|
||||
StringFunc func() string
|
||||
|
||||
// UploadPartFunc mocks the UploadPart method.
|
||||
UploadPartFunc func(bucket string, object string, uploadId string, Body io.ReadSeeker) (*s3.UploadPartOutput, error)
|
||||
|
||||
// UploadPartCopyFunc mocks the UploadPartCopy method.
|
||||
UploadPartCopyFunc func(uploadPartCopyInput *s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error)
|
||||
|
||||
@@ -390,6 +385,8 @@ type BackendMock struct {
|
||||
PutBucket []struct {
|
||||
// Bucket is the bucket argument value.
|
||||
Bucket string
|
||||
// Owner is the owner argument value.
|
||||
Owner string
|
||||
}
|
||||
// PutBucketAcl holds details about calls to the PutBucketAcl method.
|
||||
PutBucketAcl []struct {
|
||||
@@ -452,17 +449,6 @@ type BackendMock struct {
|
||||
// String holds details about calls to the String method.
|
||||
String []struct {
|
||||
}
|
||||
// UploadPart holds details about calls to the UploadPart method.
|
||||
UploadPart []struct {
|
||||
// Bucket is the bucket argument value.
|
||||
Bucket string
|
||||
// Object is the object argument value.
|
||||
Object string
|
||||
// UploadId is the uploadId argument value.
|
||||
UploadId string
|
||||
// Body is the Body argument value.
|
||||
Body io.ReadSeeker
|
||||
}
|
||||
// UploadPartCopy holds details about calls to the UploadPartCopy method.
|
||||
UploadPartCopy []struct {
|
||||
// UploadPartCopyInput is the uploadPartCopyInput argument value.
|
||||
@@ -499,7 +485,6 @@ type BackendMock struct {
|
||||
lockSetTags sync.RWMutex
|
||||
lockShutdown sync.RWMutex
|
||||
lockString sync.RWMutex
|
||||
lockUploadPart sync.RWMutex
|
||||
lockUploadPartCopy sync.RWMutex
|
||||
}
|
||||
|
||||
@@ -812,7 +797,7 @@ func (mock *BackendMock) DeleteObjectsCalls() []struct {
|
||||
}
|
||||
|
||||
// GetBucketAcl calls GetBucketAclFunc.
|
||||
func (mock *BackendMock) GetBucketAcl(bucket string) (*s3.GetBucketAclOutput, error) {
|
||||
func (mock *BackendMock) GetBucketAcl(bucket string) (*auth.GetBucketAclOutput, error) {
|
||||
if mock.GetBucketAclFunc == nil {
|
||||
panic("BackendMock.GetBucketAclFunc: method is nil but Backend.GetBucketAcl was just called")
|
||||
}
|
||||
@@ -1271,19 +1256,21 @@ func (mock *BackendMock) ListObjectsV2Calls() []struct {
|
||||
}
|
||||
|
||||
// PutBucket calls PutBucketFunc.
|
||||
func (mock *BackendMock) PutBucket(bucket string) error {
|
||||
func (mock *BackendMock) PutBucket(bucket string, owner string) error {
|
||||
if mock.PutBucketFunc == nil {
|
||||
panic("BackendMock.PutBucketFunc: method is nil but Backend.PutBucket was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Bucket string
|
||||
Owner string
|
||||
}{
|
||||
Bucket: bucket,
|
||||
Owner: owner,
|
||||
}
|
||||
mock.lockPutBucket.Lock()
|
||||
mock.calls.PutBucket = append(mock.calls.PutBucket, callInfo)
|
||||
mock.lockPutBucket.Unlock()
|
||||
return mock.PutBucketFunc(bucket)
|
||||
return mock.PutBucketFunc(bucket, owner)
|
||||
}
|
||||
|
||||
// PutBucketCalls gets all the calls that were made to PutBucket.
|
||||
@@ -1292,9 +1279,11 @@ func (mock *BackendMock) PutBucket(bucket string) error {
|
||||
// len(mockedBackend.PutBucketCalls())
|
||||
func (mock *BackendMock) PutBucketCalls() []struct {
|
||||
Bucket string
|
||||
Owner string
|
||||
} {
|
||||
var calls []struct {
|
||||
Bucket string
|
||||
Owner string
|
||||
}
|
||||
mock.lockPutBucket.RLock()
|
||||
calls = mock.calls.PutBucket
|
||||
@@ -1620,50 +1609,6 @@ func (mock *BackendMock) StringCalls() []struct {
|
||||
return calls
|
||||
}
|
||||
|
||||
// UploadPart calls UploadPartFunc.
|
||||
func (mock *BackendMock) UploadPart(bucket string, object string, uploadId string, Body io.ReadSeeker) (*s3.UploadPartOutput, error) {
|
||||
if mock.UploadPartFunc == nil {
|
||||
panic("BackendMock.UploadPartFunc: method is nil but Backend.UploadPart was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Bucket string
|
||||
Object string
|
||||
UploadId string
|
||||
Body io.ReadSeeker
|
||||
}{
|
||||
Bucket: bucket,
|
||||
Object: object,
|
||||
UploadId: uploadId,
|
||||
Body: Body,
|
||||
}
|
||||
mock.lockUploadPart.Lock()
|
||||
mock.calls.UploadPart = append(mock.calls.UploadPart, callInfo)
|
||||
mock.lockUploadPart.Unlock()
|
||||
return mock.UploadPartFunc(bucket, object, uploadId, Body)
|
||||
}
|
||||
|
||||
// UploadPartCalls gets all the calls that were made to UploadPart.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedBackend.UploadPartCalls())
|
||||
func (mock *BackendMock) UploadPartCalls() []struct {
|
||||
Bucket string
|
||||
Object string
|
||||
UploadId string
|
||||
Body io.ReadSeeker
|
||||
} {
|
||||
var calls []struct {
|
||||
Bucket string
|
||||
Object string
|
||||
UploadId string
|
||||
Body io.ReadSeeker
|
||||
}
|
||||
mock.lockUploadPart.RLock()
|
||||
calls = mock.calls.UploadPart
|
||||
mock.lockUploadPart.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// UploadPartCopy calls UploadPartCopyFunc.
|
||||
func (mock *BackendMock) UploadPartCopy(uploadPartCopyInput *s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error) {
|
||||
if mock.UploadPartCopyFunc == nil {
|
||||
|
||||
+14
-10
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/versity/versitygw/backend/auth"
|
||||
"github.com/versity/versitygw/s3err"
|
||||
)
|
||||
|
||||
@@ -120,7 +121,7 @@ func TestBackend_GetBucketAcl(t *testing.T) {
|
||||
tests = append(tests, test{
|
||||
name: "get bucket acl error",
|
||||
c: &BackendMock{
|
||||
GetBucketAclFunc: func(bucket string) (*s3.GetBucketAclOutput, error) {
|
||||
GetBucketAclFunc: func(bucket string) (*auth.GetBucketAclOutput, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
},
|
||||
},
|
||||
@@ -140,8 +141,9 @@ func TestBackend_GetBucketAcl(t *testing.T) {
|
||||
}
|
||||
func TestBackend_PutBucket(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
bucketName string
|
||||
ctx context.Context
|
||||
bucketName string
|
||||
bucketOwner string
|
||||
}
|
||||
type test struct {
|
||||
name string
|
||||
@@ -153,31 +155,33 @@ func TestBackend_PutBucket(t *testing.T) {
|
||||
tests = append(tests, test{
|
||||
name: "put bucket ",
|
||||
c: &BackendMock{
|
||||
PutBucketFunc: func(bucket string) error {
|
||||
PutBucketFunc: func(bucket, owner string) error {
|
||||
return s3err.GetAPIError(0)
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
bucketName: "b1",
|
||||
ctx: context.Background(),
|
||||
bucketName: "b1",
|
||||
bucketOwner: "owner",
|
||||
},
|
||||
wantErr: false,
|
||||
}, test{
|
||||
name: "put bucket error",
|
||||
c: &BackendMock{
|
||||
PutBucketFunc: func(bucket string) error {
|
||||
PutBucketFunc: func(bucket, owner string) error {
|
||||
return s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
bucketName: "b2",
|
||||
ctx: context.Background(),
|
||||
bucketName: "b2",
|
||||
bucketOwner: "owner",
|
||||
},
|
||||
wantErr: true,
|
||||
})
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := tt.c.PutBucket(tt.args.bucketName); (err.(s3err.APIError).Code != "") != tt.wantErr {
|
||||
if err := tt.c.PutBucket(tt.args.bucketName, tt.args.bucketOwner); (err.(s3err.APIError).Code != "") != tt.wantErr {
|
||||
t.Errorf("Backend.PutBucket() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
|
||||
+150
-1
@@ -35,6 +35,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/xattr"
|
||||
"github.com/versity/versitygw/backend"
|
||||
"github.com/versity/versitygw/backend/auth"
|
||||
"github.com/versity/versitygw/s3err"
|
||||
"github.com/versity/versitygw/s3response"
|
||||
)
|
||||
@@ -121,7 +122,7 @@ func (p *Posix) HeadBucket(bucket string) (*s3.HeadBucketOutput, error) {
|
||||
return &s3.HeadBucketOutput{}, nil
|
||||
}
|
||||
|
||||
func (p *Posix) PutBucket(bucket string) error {
|
||||
func (p *Posix) PutBucket(bucket string, owner string) error {
|
||||
err := os.Mkdir(bucket, 0777)
|
||||
if err != nil && os.IsExist(err) {
|
||||
return s3err.GetAPIError(s3err.ErrBucketAlreadyExists)
|
||||
@@ -130,6 +131,16 @@ func (p *Posix) PutBucket(bucket string) error {
|
||||
return fmt.Errorf("mkdir bucket: %w", err)
|
||||
}
|
||||
|
||||
acl := auth.ACL{ACL: "private", Owner: owner, Grantees: []auth.Grantee{}}
|
||||
jsonACL, err := json.Marshal(acl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal acl: %w", err)
|
||||
}
|
||||
|
||||
if err := xattr.Set(bucket, "user.acl", jsonACL); err != nil {
|
||||
return fmt.Errorf("set acl: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1088,6 +1099,122 @@ func (p *Posix) ListObjectsV2(bucket, prefix, marker, delim string, maxkeys int)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Posix) PutBucketAcl(input *s3.PutBucketAclInput) error {
|
||||
var ACL auth.ACL
|
||||
acl, err := xattr.Get(*input.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 != *input.AccessControlPolicy.Owner.ID {
|
||||
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 input.ACL != "" {
|
||||
ACL.ACL = input.ACL
|
||||
}
|
||||
|
||||
ACLJson, err := json.Marshal(ACL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing error: %w", err)
|
||||
}
|
||||
|
||||
if err := xattr.Set(*input.Bucket, "user.acl", ACLJson); err != nil {
|
||||
return fmt.Errorf("set acl: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Posix) GetBucketAcl(bucket string) (*auth.GetBucketAclOutput, error) {
|
||||
var ACL auth.ACL
|
||||
acl, err := xattr.Get(bucket, "user.acl")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get acl: %w", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(acl, &ACL); err != nil {
|
||||
return nil, fmt.Errorf("parse acl: %w", err)
|
||||
}
|
||||
|
||||
grants := []types.Grant{}
|
||||
|
||||
for _, elem := range ACL.Grantees {
|
||||
grants = append(grants, types.Grant{Grantee: &types.Grantee{ID: &elem.Access}, Permission: elem.Permission})
|
||||
}
|
||||
|
||||
return &auth.GetBucketAclOutput{
|
||||
Owner: &types.Owner{
|
||||
ID: &ACL.Owner,
|
||||
},
|
||||
AccessControlList: auth.AccessControlList{
|
||||
Grants: grants,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Posix) GetTags(bucket, object string) (map[string]string, error) {
|
||||
_, err := os.Stat(bucket)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
@@ -1174,3 +1301,25 @@ func isNoAttr(err error) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkIfAccountsExist(accs []string) ([]string, error) {
|
||||
var data auth.IAMConfig
|
||||
result := []string{}
|
||||
|
||||
file, err := os.ReadFile("users.json")
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("unable to read config file: %w", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(file, &data); err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
for _, acc := range accs {
|
||||
_, ok := data.AccessAccounts[acc]
|
||||
if !ok {
|
||||
result = append(result, acc)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/versity/versitygw/backend"
|
||||
"github.com/versity/versitygw/backend/auth"
|
||||
"github.com/versity/versitygw/s3response"
|
||||
"io"
|
||||
"sync"
|
||||
@@ -46,7 +47,7 @@ var _ backend.Backend = &BackendMock{}
|
||||
// DeleteObjectsFunc: func(bucket string, objects *s3.DeleteObjectsInput) error {
|
||||
// panic("mock out the DeleteObjects method")
|
||||
// },
|
||||
// GetBucketAclFunc: func(bucket string) (*s3.GetBucketAclOutput, error) {
|
||||
// GetBucketAclFunc: func(bucket string) (*auth.GetBucketAclOutput, error) {
|
||||
// panic("mock out the GetBucketAcl method")
|
||||
// },
|
||||
// GetObjectFunc: func(bucket string, object string, acceptRange string, writer io.Writer) (*s3.GetObjectOutput, error) {
|
||||
@@ -82,7 +83,7 @@ var _ backend.Backend = &BackendMock{}
|
||||
// ListObjectsV2Func: func(bucket string, prefix string, marker string, delim string, maxkeys int) (*s3.ListObjectsV2Output, error) {
|
||||
// panic("mock out the ListObjectsV2 method")
|
||||
// },
|
||||
// PutBucketFunc: func(bucket string) error {
|
||||
// PutBucketFunc: func(bucket string, owner string) error {
|
||||
// panic("mock out the PutBucket method")
|
||||
// },
|
||||
// PutBucketAclFunc: func(putBucketAclInput *s3.PutBucketAclInput) error {
|
||||
@@ -112,9 +113,6 @@ var _ backend.Backend = &BackendMock{}
|
||||
// StringFunc: func() string {
|
||||
// panic("mock out the String method")
|
||||
// },
|
||||
// UploadPartFunc: func(bucket string, object string, uploadId string, Body io.ReadSeeker) (*s3.UploadPartOutput, error) {
|
||||
// panic("mock out the UploadPart method")
|
||||
// },
|
||||
// UploadPartCopyFunc: func(uploadPartCopyInput *s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error) {
|
||||
// panic("mock out the UploadPartCopy method")
|
||||
// },
|
||||
@@ -150,7 +148,7 @@ type BackendMock struct {
|
||||
DeleteObjectsFunc func(bucket string, objects *s3.DeleteObjectsInput) error
|
||||
|
||||
// GetBucketAclFunc mocks the GetBucketAcl method.
|
||||
GetBucketAclFunc func(bucket string) (*s3.GetBucketAclOutput, error)
|
||||
GetBucketAclFunc func(bucket string) (*auth.GetBucketAclOutput, error)
|
||||
|
||||
// GetObjectFunc mocks the GetObject method.
|
||||
GetObjectFunc func(bucket string, object string, acceptRange string, writer io.Writer) (*s3.GetObjectOutput, error)
|
||||
@@ -186,7 +184,7 @@ type BackendMock struct {
|
||||
ListObjectsV2Func func(bucket string, prefix string, marker string, delim string, maxkeys int) (*s3.ListObjectsV2Output, error)
|
||||
|
||||
// PutBucketFunc mocks the PutBucket method.
|
||||
PutBucketFunc func(bucket string) error
|
||||
PutBucketFunc func(bucket string, owner string) error
|
||||
|
||||
// PutBucketAclFunc mocks the PutBucketAcl method.
|
||||
PutBucketAclFunc func(putBucketAclInput *s3.PutBucketAclInput) error
|
||||
@@ -215,9 +213,6 @@ type BackendMock struct {
|
||||
// StringFunc mocks the String method.
|
||||
StringFunc func() string
|
||||
|
||||
// UploadPartFunc mocks the UploadPart method.
|
||||
UploadPartFunc func(bucket string, object string, uploadId string, Body io.ReadSeeker) (*s3.UploadPartOutput, error)
|
||||
|
||||
// UploadPartCopyFunc mocks the UploadPartCopy method.
|
||||
UploadPartCopyFunc func(uploadPartCopyInput *s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error)
|
||||
|
||||
@@ -391,6 +386,8 @@ type BackendMock struct {
|
||||
PutBucket []struct {
|
||||
// Bucket is the bucket argument value.
|
||||
Bucket string
|
||||
// Owner is the owner argument value.
|
||||
Owner string
|
||||
}
|
||||
// PutBucketAcl holds details about calls to the PutBucketAcl method.
|
||||
PutBucketAcl []struct {
|
||||
@@ -453,17 +450,6 @@ type BackendMock struct {
|
||||
// String holds details about calls to the String method.
|
||||
String []struct {
|
||||
}
|
||||
// UploadPart holds details about calls to the UploadPart method.
|
||||
UploadPart []struct {
|
||||
// Bucket is the bucket argument value.
|
||||
Bucket string
|
||||
// Object is the object argument value.
|
||||
Object string
|
||||
// UploadId is the uploadId argument value.
|
||||
UploadId string
|
||||
// Body is the Body argument value.
|
||||
Body io.ReadSeeker
|
||||
}
|
||||
// UploadPartCopy holds details about calls to the UploadPartCopy method.
|
||||
UploadPartCopy []struct {
|
||||
// UploadPartCopyInput is the uploadPartCopyInput argument value.
|
||||
@@ -500,7 +486,6 @@ type BackendMock struct {
|
||||
lockSetTags sync.RWMutex
|
||||
lockShutdown sync.RWMutex
|
||||
lockString sync.RWMutex
|
||||
lockUploadPart sync.RWMutex
|
||||
lockUploadPartCopy sync.RWMutex
|
||||
}
|
||||
|
||||
@@ -813,7 +798,7 @@ func (mock *BackendMock) DeleteObjectsCalls() []struct {
|
||||
}
|
||||
|
||||
// GetBucketAcl calls GetBucketAclFunc.
|
||||
func (mock *BackendMock) GetBucketAcl(bucket string) (*s3.GetBucketAclOutput, error) {
|
||||
func (mock *BackendMock) GetBucketAcl(bucket string) (*auth.GetBucketAclOutput, error) {
|
||||
if mock.GetBucketAclFunc == nil {
|
||||
panic("BackendMock.GetBucketAclFunc: method is nil but Backend.GetBucketAcl was just called")
|
||||
}
|
||||
@@ -1272,19 +1257,21 @@ func (mock *BackendMock) ListObjectsV2Calls() []struct {
|
||||
}
|
||||
|
||||
// PutBucket calls PutBucketFunc.
|
||||
func (mock *BackendMock) PutBucket(bucket string) error {
|
||||
func (mock *BackendMock) PutBucket(bucket string, owner string) error {
|
||||
if mock.PutBucketFunc == nil {
|
||||
panic("BackendMock.PutBucketFunc: method is nil but Backend.PutBucket was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Bucket string
|
||||
Owner string
|
||||
}{
|
||||
Bucket: bucket,
|
||||
Owner: owner,
|
||||
}
|
||||
mock.lockPutBucket.Lock()
|
||||
mock.calls.PutBucket = append(mock.calls.PutBucket, callInfo)
|
||||
mock.lockPutBucket.Unlock()
|
||||
return mock.PutBucketFunc(bucket)
|
||||
return mock.PutBucketFunc(bucket, owner)
|
||||
}
|
||||
|
||||
// PutBucketCalls gets all the calls that were made to PutBucket.
|
||||
@@ -1293,9 +1280,11 @@ func (mock *BackendMock) PutBucket(bucket string) error {
|
||||
// len(mockedBackend.PutBucketCalls())
|
||||
func (mock *BackendMock) PutBucketCalls() []struct {
|
||||
Bucket string
|
||||
Owner string
|
||||
} {
|
||||
var calls []struct {
|
||||
Bucket string
|
||||
Owner string
|
||||
}
|
||||
mock.lockPutBucket.RLock()
|
||||
calls = mock.calls.PutBucket
|
||||
@@ -1621,50 +1610,6 @@ func (mock *BackendMock) StringCalls() []struct {
|
||||
return calls
|
||||
}
|
||||
|
||||
// UploadPart calls UploadPartFunc.
|
||||
func (mock *BackendMock) UploadPart(bucket string, object string, uploadId string, Body io.ReadSeeker) (*s3.UploadPartOutput, error) {
|
||||
if mock.UploadPartFunc == nil {
|
||||
panic("BackendMock.UploadPartFunc: method is nil but Backend.UploadPart was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Bucket string
|
||||
Object string
|
||||
UploadId string
|
||||
Body io.ReadSeeker
|
||||
}{
|
||||
Bucket: bucket,
|
||||
Object: object,
|
||||
UploadId: uploadId,
|
||||
Body: Body,
|
||||
}
|
||||
mock.lockUploadPart.Lock()
|
||||
mock.calls.UploadPart = append(mock.calls.UploadPart, callInfo)
|
||||
mock.lockUploadPart.Unlock()
|
||||
return mock.UploadPartFunc(bucket, object, uploadId, Body)
|
||||
}
|
||||
|
||||
// UploadPartCalls gets all the calls that were made to UploadPart.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedBackend.UploadPartCalls())
|
||||
func (mock *BackendMock) UploadPartCalls() []struct {
|
||||
Bucket string
|
||||
Object string
|
||||
UploadId string
|
||||
Body io.ReadSeeker
|
||||
} {
|
||||
var calls []struct {
|
||||
Bucket string
|
||||
Object string
|
||||
UploadId string
|
||||
Body io.ReadSeeker
|
||||
}
|
||||
mock.lockUploadPart.RLock()
|
||||
calls = mock.calls.UploadPart
|
||||
mock.lockUploadPart.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// UploadPartCopy calls UploadPartCopyFunc.
|
||||
func (mock *BackendMock) UploadPartCopy(uploadPartCopyInput *s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error) {
|
||||
if mock.UploadPartCopyFunc == nil {
|
||||
|
||||
+13
-10
@@ -152,15 +152,17 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
bucket, acl, grantFullControl, grantRead, grantReadACP, granWrite, grantWriteACP :=
|
||||
bucket, acl, grantFullControl, grantRead, grantReadACP, granWrite, grantWriteACP, access :=
|
||||
ctx.Params("bucket"),
|
||||
ctx.Get("X-Amz-Acl"),
|
||||
ctx.Get("X-Amz-Grant-Full-Control"),
|
||||
ctx.Get("X-Amz-Grant-Read"),
|
||||
ctx.Get("X-Amz-Grant-Read-Acp"),
|
||||
ctx.Get("X-Amz-Grant-Write"),
|
||||
ctx.Get("X-Amz-Grant-Write-Acp")
|
||||
ctx.Get("X-Amz-Grant-Write-Acp"),
|
||||
ctx.Locals("access")
|
||||
|
||||
owner := access.(string)
|
||||
grants := grantFullControl + grantRead + grantReadACP + granWrite + grantWriteACP
|
||||
|
||||
if grants != "" || acl != "" {
|
||||
@@ -168,19 +170,20 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
return errors.New("wrong api call")
|
||||
}
|
||||
err := c.be.PutBucketAcl(&s3.PutBucketAclInput{
|
||||
Bucket: &bucket,
|
||||
ACL: types.BucketCannedACL(acl),
|
||||
GrantFullControl: &grantFullControl,
|
||||
GrantRead: &grantRead,
|
||||
GrantReadACP: &grantReadACP,
|
||||
GrantWrite: &granWrite,
|
||||
GrantWriteACP: &grantWriteACP,
|
||||
Bucket: &bucket,
|
||||
ACL: types.BucketCannedACL(acl),
|
||||
GrantFullControl: &grantFullControl,
|
||||
GrantRead: &grantRead,
|
||||
GrantReadACP: &grantReadACP,
|
||||
GrantWrite: &granWrite,
|
||||
GrantWriteACP: &grantWriteACP,
|
||||
AccessControlPolicy: &types.AccessControlPolicy{Owner: &types.Owner{ID: &owner}},
|
||||
})
|
||||
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
err := c.be.PutBucket(bucket)
|
||||
err := c.be.PutBucket(bucket, owner)
|
||||
return SendResponse(ctx, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/versity/versitygw/backend"
|
||||
"github.com/versity/versitygw/backend/auth"
|
||||
"github.com/versity/versitygw/s3err"
|
||||
"github.com/versity/versitygw/s3response"
|
||||
)
|
||||
@@ -231,8 +232,8 @@ func TestS3ApiController_ListActions(t *testing.T) {
|
||||
|
||||
app := fiber.New()
|
||||
s3ApiController := S3ApiController{be: &BackendMock{
|
||||
GetBucketAclFunc: func(bucket string) (*s3.GetBucketAclOutput, error) {
|
||||
return &s3.GetBucketAclOutput{}, nil
|
||||
GetBucketAclFunc: func(bucket string) (*auth.GetBucketAclOutput, error) {
|
||||
return nil, nil
|
||||
},
|
||||
ListMultipartUploadsFunc: func(output *s3.ListMultipartUploadsInput) (s3response.ListMultipartUploadsResponse, error) {
|
||||
return s3response.ListMultipartUploadsResponse{}, nil
|
||||
@@ -333,10 +334,15 @@ func TestS3ApiController_PutBucketActions(t *testing.T) {
|
||||
PutBucketAclFunc: func(*s3.PutBucketAclInput) error {
|
||||
return nil
|
||||
},
|
||||
PutBucketFunc: func(bucket string) error {
|
||||
PutBucketFunc: func(bucket, owner string) error {
|
||||
return nil
|
||||
},
|
||||
}}
|
||||
// Mock ctx.Locals
|
||||
app.Use(func(ctx *fiber.Ctx) error {
|
||||
ctx.Locals("access", "valid access")
|
||||
return ctx.Next()
|
||||
})
|
||||
app.Put("/:bucket", s3ApiController.PutBucketActions)
|
||||
|
||||
// Error case
|
||||
@@ -406,9 +412,6 @@ func TestS3ApiController_PutActions(t *testing.T) {
|
||||
UploadPartCopyFunc: func(*s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error) {
|
||||
return &s3.UploadPartCopyOutput{}, nil
|
||||
},
|
||||
UploadPartFunc: func(bucket, object, uploadId string, Body io.ReadSeeker) (*s3.UploadPartOutput, error) {
|
||||
return &s3.UploadPartOutput{}, nil
|
||||
},
|
||||
PutObjectAclFunc: func(*s3.PutObjectAclInput) error {
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -68,7 +68,7 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, debug bool) fib
|
||||
return controllers.SendResponse(ctx, s3err.GetAPIError(s3err.ErrCredMalformed))
|
||||
}
|
||||
|
||||
signHdrKv := strings.Split(authParts[2], "=")
|
||||
signHdrKv := strings.Split(authParts[2][:len(authParts[2])-1], "=")
|
||||
if len(signHdrKv) != 2 {
|
||||
return controllers.SendResponse(ctx, s3err.GetAPIError(s3err.ErrCredMalformed))
|
||||
}
|
||||
@@ -135,6 +135,7 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, debug bool) fib
|
||||
}
|
||||
|
||||
ctx.Locals("role", account.Role)
|
||||
ctx.Locals("access", creds[0])
|
||||
|
||||
return ctx.Next()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user