mirror of
https://github.com/versity/versitygw.git
synced 2026-08-15 11:46:14 +00:00
feat: Closes 417, Added the following versioning related actions: PutBucketVersioning, GetBucketVersioning, ListObjectVersions. Added versionId support in FE for the following actions: GetObject, DeleteObject
This commit is contained in:
+20
@@ -250,3 +250,23 @@ func MayCreateBucket(acct Account, isRoot bool) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsAdminOrOwner(acct Account, isRoot bool, acl ACL) error {
|
||||
// Owner check
|
||||
if acct.Access == acl.Owner {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Root user has access over almost everything
|
||||
if isRoot {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Admin user case
|
||||
if acct.Role == RoleAdmin {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return access denied in all other cases
|
||||
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||
}
|
||||
|
||||
+14
-1
@@ -38,6 +38,8 @@ type Backend interface {
|
||||
CreateBucket(_ context.Context, _ *s3.CreateBucketInput, defaultACL []byte) error
|
||||
PutBucketAcl(_ context.Context, bucket string, data []byte) error
|
||||
DeleteBucket(context.Context, *s3.DeleteBucketInput) error
|
||||
PutBucketVersioning(context.Context, *s3.PutBucketVersioningInput) error
|
||||
GetBucketVersioning(_ context.Context, bucket string) (*s3.GetBucketVersioningOutput, error)
|
||||
|
||||
// multipart operations
|
||||
CreateMultipartUpload(context.Context, *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error)
|
||||
@@ -60,6 +62,7 @@ type Backend interface {
|
||||
DeleteObject(context.Context, *s3.DeleteObjectInput) error
|
||||
DeleteObjects(context.Context, *s3.DeleteObjectsInput) (s3response.DeleteObjectsResult, error)
|
||||
PutObjectAcl(context.Context, *s3.PutObjectAclInput) error
|
||||
ListObjectVersions(context.Context, *s3.ListObjectVersionsInput) (*s3.ListObjectVersionsOutput, error)
|
||||
|
||||
// special case object operations
|
||||
RestoreObject(context.Context, *s3.RestoreObjectInput) error
|
||||
@@ -70,7 +73,7 @@ type Backend interface {
|
||||
PutBucketTagging(_ context.Context, bucket string, tags map[string]string) error
|
||||
DeleteBucketTagging(_ context.Context, bucket string) error
|
||||
|
||||
// object tags operations
|
||||
// object tagging operations
|
||||
GetObjectTagging(_ context.Context, bucket, object string) (map[string]string, error)
|
||||
PutObjectTagging(_ context.Context, bucket, object string, tags map[string]string) error
|
||||
DeleteObjectTagging(_ context.Context, bucket, object string) error
|
||||
@@ -109,6 +112,12 @@ func (BackendUnsupported) PutBucketAcl(_ context.Context, bucket string, data []
|
||||
func (BackendUnsupported) DeleteBucket(context.Context, *s3.DeleteBucketInput) error {
|
||||
return s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
}
|
||||
func (BackendUnsupported) PutBucketVersioning(context.Context, *s3.PutBucketVersioningInput) error {
|
||||
return s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
}
|
||||
func (BackendUnsupported) GetBucketVersioning(_ context.Context, bucket string) (*s3.GetBucketVersioningOutput, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
}
|
||||
|
||||
func (BackendUnsupported) CreateMultipartUpload(context.Context, *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
@@ -184,6 +193,10 @@ func (BackendUnsupported) SelectObjectContent(ctx context.Context, input *s3.Sel
|
||||
}
|
||||
}
|
||||
|
||||
func (BackendUnsupported) ListObjectVersions(context.Context, *s3.ListObjectVersionsInput) (*s3.ListObjectVersionsOutput, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
}
|
||||
|
||||
func (BackendUnsupported) GetBucketTagging(_ context.Context, bucket string) (map[string]string, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
}
|
||||
|
||||
@@ -62,6 +62,9 @@ var _ backend.Backend = &BackendMock{}
|
||||
// GetBucketTaggingFunc: func(contextMoqParam context.Context, bucket string) (map[string]string, error) {
|
||||
// panic("mock out the GetBucketTagging method")
|
||||
// },
|
||||
// GetBucketVersioningFunc: func(contextMoqParam context.Context, bucket string) (*s3.GetBucketVersioningOutput, error) {
|
||||
// panic("mock out the GetBucketVersioning method")
|
||||
// },
|
||||
// GetObjectFunc: func(contextMoqParam context.Context, getObjectInput *s3.GetObjectInput, writer io.Writer) (*s3.GetObjectOutput, error) {
|
||||
// panic("mock out the GetObject method")
|
||||
// },
|
||||
@@ -89,6 +92,9 @@ var _ backend.Backend = &BackendMock{}
|
||||
// ListMultipartUploadsFunc: func(contextMoqParam context.Context, listMultipartUploadsInput *s3.ListMultipartUploadsInput) (s3response.ListMultipartUploadsResult, error) {
|
||||
// panic("mock out the ListMultipartUploads method")
|
||||
// },
|
||||
// ListObjectVersionsFunc: func(contextMoqParam context.Context, listObjectVersionsInput *s3.ListObjectVersionsInput) (*s3.ListObjectVersionsOutput, error) {
|
||||
// panic("mock out the ListObjectVersions method")
|
||||
// },
|
||||
// ListObjectsFunc: func(contextMoqParam context.Context, listObjectsInput *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) {
|
||||
// panic("mock out the ListObjects method")
|
||||
// },
|
||||
@@ -104,6 +110,9 @@ var _ backend.Backend = &BackendMock{}
|
||||
// PutBucketTaggingFunc: func(contextMoqParam context.Context, bucket string, tags map[string]string) error {
|
||||
// panic("mock out the PutBucketTagging method")
|
||||
// },
|
||||
// PutBucketVersioningFunc: func(contextMoqParam context.Context, putBucketVersioningInput *s3.PutBucketVersioningInput) error {
|
||||
// panic("mock out the PutBucketVersioning method")
|
||||
// },
|
||||
// PutObjectFunc: func(contextMoqParam context.Context, putObjectInput *s3.PutObjectInput) (string, error) {
|
||||
// panic("mock out the PutObject method")
|
||||
// },
|
||||
@@ -177,6 +186,9 @@ type BackendMock struct {
|
||||
// GetBucketTaggingFunc mocks the GetBucketTagging method.
|
||||
GetBucketTaggingFunc func(contextMoqParam context.Context, bucket string) (map[string]string, error)
|
||||
|
||||
// GetBucketVersioningFunc mocks the GetBucketVersioning method.
|
||||
GetBucketVersioningFunc func(contextMoqParam context.Context, bucket string) (*s3.GetBucketVersioningOutput, error)
|
||||
|
||||
// GetObjectFunc mocks the GetObject method.
|
||||
GetObjectFunc func(contextMoqParam context.Context, getObjectInput *s3.GetObjectInput, writer io.Writer) (*s3.GetObjectOutput, error)
|
||||
|
||||
@@ -204,6 +216,9 @@ type BackendMock struct {
|
||||
// ListMultipartUploadsFunc mocks the ListMultipartUploads method.
|
||||
ListMultipartUploadsFunc func(contextMoqParam context.Context, listMultipartUploadsInput *s3.ListMultipartUploadsInput) (s3response.ListMultipartUploadsResult, error)
|
||||
|
||||
// ListObjectVersionsFunc mocks the ListObjectVersions method.
|
||||
ListObjectVersionsFunc func(contextMoqParam context.Context, listObjectVersionsInput *s3.ListObjectVersionsInput) (*s3.ListObjectVersionsOutput, error)
|
||||
|
||||
// ListObjectsFunc mocks the ListObjects method.
|
||||
ListObjectsFunc func(contextMoqParam context.Context, listObjectsInput *s3.ListObjectsInput) (*s3.ListObjectsOutput, error)
|
||||
|
||||
@@ -219,6 +234,9 @@ type BackendMock struct {
|
||||
// PutBucketTaggingFunc mocks the PutBucketTagging method.
|
||||
PutBucketTaggingFunc func(contextMoqParam context.Context, bucket string, tags map[string]string) error
|
||||
|
||||
// PutBucketVersioningFunc mocks the PutBucketVersioning method.
|
||||
PutBucketVersioningFunc func(contextMoqParam context.Context, putBucketVersioningInput *s3.PutBucketVersioningInput) error
|
||||
|
||||
// PutObjectFunc mocks the PutObject method.
|
||||
PutObjectFunc func(contextMoqParam context.Context, putObjectInput *s3.PutObjectInput) (string, error)
|
||||
|
||||
@@ -345,6 +363,13 @@ type BackendMock struct {
|
||||
// Bucket is the bucket argument value.
|
||||
Bucket string
|
||||
}
|
||||
// GetBucketVersioning holds details about calls to the GetBucketVersioning method.
|
||||
GetBucketVersioning []struct {
|
||||
// ContextMoqParam is the contextMoqParam argument value.
|
||||
ContextMoqParam context.Context
|
||||
// Bucket is the bucket argument value.
|
||||
Bucket string
|
||||
}
|
||||
// GetObject holds details about calls to the GetObject method.
|
||||
GetObject []struct {
|
||||
// ContextMoqParam is the contextMoqParam argument value.
|
||||
@@ -412,6 +437,13 @@ type BackendMock struct {
|
||||
// ListMultipartUploadsInput is the listMultipartUploadsInput argument value.
|
||||
ListMultipartUploadsInput *s3.ListMultipartUploadsInput
|
||||
}
|
||||
// ListObjectVersions holds details about calls to the ListObjectVersions method.
|
||||
ListObjectVersions []struct {
|
||||
// ContextMoqParam is the contextMoqParam argument value.
|
||||
ContextMoqParam context.Context
|
||||
// ListObjectVersionsInput is the listObjectVersionsInput argument value.
|
||||
ListObjectVersionsInput *s3.ListObjectVersionsInput
|
||||
}
|
||||
// ListObjects holds details about calls to the ListObjects method.
|
||||
ListObjects []struct {
|
||||
// ContextMoqParam is the contextMoqParam argument value.
|
||||
@@ -451,6 +483,13 @@ type BackendMock struct {
|
||||
// Tags is the tags argument value.
|
||||
Tags map[string]string
|
||||
}
|
||||
// PutBucketVersioning holds details about calls to the PutBucketVersioning method.
|
||||
PutBucketVersioning []struct {
|
||||
// ContextMoqParam is the contextMoqParam argument value.
|
||||
ContextMoqParam context.Context
|
||||
// PutBucketVersioningInput is the putBucketVersioningInput argument value.
|
||||
PutBucketVersioningInput *s3.PutBucketVersioningInput
|
||||
}
|
||||
// PutObject holds details about calls to the PutObject method.
|
||||
PutObject []struct {
|
||||
// ContextMoqParam is the contextMoqParam argument value.
|
||||
@@ -524,6 +563,7 @@ type BackendMock struct {
|
||||
lockDeleteObjects sync.RWMutex
|
||||
lockGetBucketAcl sync.RWMutex
|
||||
lockGetBucketTagging sync.RWMutex
|
||||
lockGetBucketVersioning sync.RWMutex
|
||||
lockGetObject sync.RWMutex
|
||||
lockGetObjectAcl sync.RWMutex
|
||||
lockGetObjectAttributes sync.RWMutex
|
||||
@@ -533,11 +573,13 @@ type BackendMock struct {
|
||||
lockListBuckets sync.RWMutex
|
||||
lockListBucketsAndOwners sync.RWMutex
|
||||
lockListMultipartUploads sync.RWMutex
|
||||
lockListObjectVersions sync.RWMutex
|
||||
lockListObjects sync.RWMutex
|
||||
lockListObjectsV2 sync.RWMutex
|
||||
lockListParts sync.RWMutex
|
||||
lockPutBucketAcl sync.RWMutex
|
||||
lockPutBucketTagging sync.RWMutex
|
||||
lockPutBucketVersioning sync.RWMutex
|
||||
lockPutObject sync.RWMutex
|
||||
lockPutObjectAcl sync.RWMutex
|
||||
lockPutObjectTagging sync.RWMutex
|
||||
@@ -1029,6 +1071,42 @@ func (mock *BackendMock) GetBucketTaggingCalls() []struct {
|
||||
return calls
|
||||
}
|
||||
|
||||
// GetBucketVersioning calls GetBucketVersioningFunc.
|
||||
func (mock *BackendMock) GetBucketVersioning(contextMoqParam context.Context, bucket string) (*s3.GetBucketVersioningOutput, error) {
|
||||
if mock.GetBucketVersioningFunc == nil {
|
||||
panic("BackendMock.GetBucketVersioningFunc: method is nil but Backend.GetBucketVersioning was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
ContextMoqParam context.Context
|
||||
Bucket string
|
||||
}{
|
||||
ContextMoqParam: contextMoqParam,
|
||||
Bucket: bucket,
|
||||
}
|
||||
mock.lockGetBucketVersioning.Lock()
|
||||
mock.calls.GetBucketVersioning = append(mock.calls.GetBucketVersioning, callInfo)
|
||||
mock.lockGetBucketVersioning.Unlock()
|
||||
return mock.GetBucketVersioningFunc(contextMoqParam, bucket)
|
||||
}
|
||||
|
||||
// GetBucketVersioningCalls gets all the calls that were made to GetBucketVersioning.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedBackend.GetBucketVersioningCalls())
|
||||
func (mock *BackendMock) GetBucketVersioningCalls() []struct {
|
||||
ContextMoqParam context.Context
|
||||
Bucket string
|
||||
} {
|
||||
var calls []struct {
|
||||
ContextMoqParam context.Context
|
||||
Bucket string
|
||||
}
|
||||
mock.lockGetBucketVersioning.RLock()
|
||||
calls = mock.calls.GetBucketVersioning
|
||||
mock.lockGetBucketVersioning.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// GetObject calls GetObjectFunc.
|
||||
func (mock *BackendMock) GetObject(contextMoqParam context.Context, getObjectInput *s3.GetObjectInput, writer io.Writer) (*s3.GetObjectOutput, error) {
|
||||
if mock.GetObjectFunc == nil {
|
||||
@@ -1361,6 +1439,42 @@ func (mock *BackendMock) ListMultipartUploadsCalls() []struct {
|
||||
return calls
|
||||
}
|
||||
|
||||
// ListObjectVersions calls ListObjectVersionsFunc.
|
||||
func (mock *BackendMock) ListObjectVersions(contextMoqParam context.Context, listObjectVersionsInput *s3.ListObjectVersionsInput) (*s3.ListObjectVersionsOutput, error) {
|
||||
if mock.ListObjectVersionsFunc == nil {
|
||||
panic("BackendMock.ListObjectVersionsFunc: method is nil but Backend.ListObjectVersions was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
ContextMoqParam context.Context
|
||||
ListObjectVersionsInput *s3.ListObjectVersionsInput
|
||||
}{
|
||||
ContextMoqParam: contextMoqParam,
|
||||
ListObjectVersionsInput: listObjectVersionsInput,
|
||||
}
|
||||
mock.lockListObjectVersions.Lock()
|
||||
mock.calls.ListObjectVersions = append(mock.calls.ListObjectVersions, callInfo)
|
||||
mock.lockListObjectVersions.Unlock()
|
||||
return mock.ListObjectVersionsFunc(contextMoqParam, listObjectVersionsInput)
|
||||
}
|
||||
|
||||
// ListObjectVersionsCalls gets all the calls that were made to ListObjectVersions.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedBackend.ListObjectVersionsCalls())
|
||||
func (mock *BackendMock) ListObjectVersionsCalls() []struct {
|
||||
ContextMoqParam context.Context
|
||||
ListObjectVersionsInput *s3.ListObjectVersionsInput
|
||||
} {
|
||||
var calls []struct {
|
||||
ContextMoqParam context.Context
|
||||
ListObjectVersionsInput *s3.ListObjectVersionsInput
|
||||
}
|
||||
mock.lockListObjectVersions.RLock()
|
||||
calls = mock.calls.ListObjectVersions
|
||||
mock.lockListObjectVersions.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// ListObjects calls ListObjectsFunc.
|
||||
func (mock *BackendMock) ListObjects(contextMoqParam context.Context, listObjectsInput *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) {
|
||||
if mock.ListObjectsFunc == nil {
|
||||
@@ -1549,6 +1663,42 @@ func (mock *BackendMock) PutBucketTaggingCalls() []struct {
|
||||
return calls
|
||||
}
|
||||
|
||||
// PutBucketVersioning calls PutBucketVersioningFunc.
|
||||
func (mock *BackendMock) PutBucketVersioning(contextMoqParam context.Context, putBucketVersioningInput *s3.PutBucketVersioningInput) error {
|
||||
if mock.PutBucketVersioningFunc == nil {
|
||||
panic("BackendMock.PutBucketVersioningFunc: method is nil but Backend.PutBucketVersioning was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
ContextMoqParam context.Context
|
||||
PutBucketVersioningInput *s3.PutBucketVersioningInput
|
||||
}{
|
||||
ContextMoqParam: contextMoqParam,
|
||||
PutBucketVersioningInput: putBucketVersioningInput,
|
||||
}
|
||||
mock.lockPutBucketVersioning.Lock()
|
||||
mock.calls.PutBucketVersioning = append(mock.calls.PutBucketVersioning, callInfo)
|
||||
mock.lockPutBucketVersioning.Unlock()
|
||||
return mock.PutBucketVersioningFunc(contextMoqParam, putBucketVersioningInput)
|
||||
}
|
||||
|
||||
// PutBucketVersioningCalls gets all the calls that were made to PutBucketVersioning.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedBackend.PutBucketVersioningCalls())
|
||||
func (mock *BackendMock) PutBucketVersioningCalls() []struct {
|
||||
ContextMoqParam context.Context
|
||||
PutBucketVersioningInput *s3.PutBucketVersioningInput
|
||||
} {
|
||||
var calls []struct {
|
||||
ContextMoqParam context.Context
|
||||
PutBucketVersioningInput *s3.PutBucketVersioningInput
|
||||
}
|
||||
mock.lockPutBucketVersioning.RLock()
|
||||
calls = mock.calls.PutBucketVersioning
|
||||
mock.lockPutBucketVersioning.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// PutObject calls PutObjectFunc.
|
||||
func (mock *BackendMock) PutObject(contextMoqParam context.Context, putObjectInput *s3.PutObjectInput) (string, error) {
|
||||
if mock.PutObjectFunc == nil {
|
||||
|
||||
+115
-5
@@ -74,6 +74,7 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
|
||||
acct := ctx.Locals("account").(auth.Account)
|
||||
isRoot := ctx.Locals("isRoot").(bool)
|
||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||
versionId := ctx.Query("versionId")
|
||||
if keyEnd != "" {
|
||||
key = strings.Join([]string{key, keyEnd}, "/")
|
||||
}
|
||||
@@ -229,9 +230,10 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
|
||||
|
||||
ctx.Locals("logResBody", false)
|
||||
res, err := c.be.GetObject(ctx.Context(), &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &key,
|
||||
Range: &acceptRange,
|
||||
Bucket: &bucket,
|
||||
Key: &key,
|
||||
Range: &acceptRange,
|
||||
VersionId: &versionId,
|
||||
}, ctx.Response().BodyWriter())
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
@@ -333,6 +335,7 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
||||
keyMarker := ctx.Query("key-marker")
|
||||
maxUploadsStr := ctx.Query("max-uploads")
|
||||
uploadIdMarker := ctx.Query("upload-id-marker")
|
||||
versionIdMarker := ctx.Query("version-id-marker")
|
||||
acct := ctx.Locals("account").(auth.Account)
|
||||
isRoot := ctx.Locals("isRoot").(bool)
|
||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||
@@ -374,6 +377,72 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("versioning") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "GetBucketVersioning",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
})
|
||||
}
|
||||
// Only admin users and the bucket owner are allowed to get the versioning state of a bucket.
|
||||
if err := auth.IsAdminOrOwner(acct, isRoot, parsedAcl); err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "GetBucketVersioning",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
})
|
||||
}
|
||||
|
||||
data, err := c.be.GetBucketVersioning(ctx.Context(), bucket)
|
||||
return SendXMLResponse(ctx, data, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "GetBucketVersioning",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
})
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("versions") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "ListObjectVersions",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
})
|
||||
}
|
||||
|
||||
maxkeys, err := utils.ParseUint(maxkeysStr)
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "ListObjectVersions",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
})
|
||||
}
|
||||
|
||||
data, err := c.be.ListObjectVersions(ctx.Context(), &s3.ListObjectVersionsInput{
|
||||
Bucket: &bucket,
|
||||
Delimiter: &delimiter,
|
||||
KeyMarker: &keyMarker,
|
||||
MaxKeys: &maxkeys,
|
||||
Prefix: &prefix,
|
||||
VersionIdMarker: &versionIdMarker,
|
||||
})
|
||||
return SendXMLResponse(ctx, data, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "ListObjectVersions",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
})
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("acl") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ_ACP", isRoot)
|
||||
if err != nil {
|
||||
@@ -518,6 +587,8 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
grantReadACP := ctx.Get("X-Amz-Grant-Read-Acp")
|
||||
granWrite := ctx.Get("X-Amz-Grant-Write")
|
||||
grantWriteACP := ctx.Get("X-Amz-Grant-Write-Acp")
|
||||
mfa := ctx.Get("X-Amz-Mfa")
|
||||
contentMD5 := ctx.Get("Content-MD5")
|
||||
acct := ctx.Locals("account").(auth.Account)
|
||||
isRoot := ctx.Locals("isRoot").(bool)
|
||||
|
||||
@@ -568,6 +639,43 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("versioning") {
|
||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "PutBucketVersioning",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
})
|
||||
}
|
||||
|
||||
var versioningConf types.VersioningConfiguration
|
||||
err = xml.Unmarshal(ctx.Body(), &versioningConf)
|
||||
if err != nil {
|
||||
return SendResponse(ctx, s3err.GetAPIError(s3err.ErrInvalidRequest),
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "PutBucketVersioning",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
})
|
||||
}
|
||||
|
||||
err = c.be.PutBucketVersioning(ctx.Context(), &s3.PutBucketVersioningInput{
|
||||
Bucket: &bucket,
|
||||
MFA: &mfa,
|
||||
VersioningConfiguration: &versioningConf,
|
||||
ContentMD5: &contentMD5,
|
||||
})
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "PutBucketVersioning",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
})
|
||||
}
|
||||
|
||||
grants := grantFullControl + grantRead + grantReadACP + granWrite + grantWriteACP
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("acl") {
|
||||
@@ -1218,6 +1326,7 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
|
||||
key := ctx.Params("key")
|
||||
keyEnd := ctx.Params("*1")
|
||||
uploadId := ctx.Query("uploadId")
|
||||
versionId := ctx.Query("versionId")
|
||||
acct := ctx.Locals("account").(auth.Account)
|
||||
isRoot := ctx.Locals("isRoot").(bool)
|
||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||
@@ -1291,8 +1400,9 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
|
||||
|
||||
err = c.be.DeleteObject(ctx.Context(),
|
||||
&s3.DeleteObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &key,
|
||||
Bucket: &bucket,
|
||||
Key: &key,
|
||||
VersionId: &versionId,
|
||||
})
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
|
||||
@@ -346,6 +346,12 @@ func TestS3ApiController_ListActions(t *testing.T) {
|
||||
GetBucketTaggingFunc: func(contextMoqParam context.Context, bucket string) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
},
|
||||
GetBucketVersioningFunc: func(contextMoqParam context.Context, bucket string) (*s3.GetBucketVersioningOutput, error) {
|
||||
return &s3.GetBucketVersioningOutput{}, nil
|
||||
},
|
||||
ListObjectVersionsFunc: func(contextMoqParam context.Context, listObjectVersionsInput *s3.ListObjectVersionsInput) (*s3.ListObjectVersionsOutput, error) {
|
||||
return &s3.ListObjectVersionsOutput{}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -453,6 +459,24 @@ func TestS3ApiController_ListActions(t *testing.T) {
|
||||
wantErr: false,
|
||||
statusCode: 501,
|
||||
},
|
||||
{
|
||||
name: "List-actions-get-bucket-versioning-success",
|
||||
app: app,
|
||||
args: args{
|
||||
req: httptest.NewRequest(http.MethodGet, "/my-bucket?versioning", nil),
|
||||
},
|
||||
wantErr: false,
|
||||
statusCode: 200,
|
||||
},
|
||||
{
|
||||
name: "List-actions-list-object-versions-success",
|
||||
app: app,
|
||||
args: args{
|
||||
req: httptest.NewRequest(http.MethodGet, "/my-bucket?versions", nil),
|
||||
},
|
||||
wantErr: false,
|
||||
statusCode: 200,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -527,6 +551,13 @@ func TestS3ApiController_PutBucketActions(t *testing.T) {
|
||||
</Tagging>
|
||||
`
|
||||
|
||||
versioningBody := `
|
||||
<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Status>Enabled</Status>
|
||||
<MfaDelete>Enabled</MfaDelete>
|
||||
</VersioningConfiguration>
|
||||
`
|
||||
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
GetBucketAclFunc: func(context.Context, *s3.GetBucketAclInput) ([]byte, error) {
|
||||
@@ -541,6 +572,9 @@ func TestS3ApiController_PutBucketActions(t *testing.T) {
|
||||
PutBucketTaggingFunc: func(contextMoqParam context.Context, bucket string, tags map[string]string) error {
|
||||
return nil
|
||||
},
|
||||
PutBucketVersioningFunc: func(contextMoqParam context.Context, putBucketVersioningInput *s3.PutBucketVersioningInput) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
// Mock ctx.Locals
|
||||
@@ -599,6 +633,24 @@ func TestS3ApiController_PutBucketActions(t *testing.T) {
|
||||
wantErr: false,
|
||||
statusCode: 200,
|
||||
},
|
||||
{
|
||||
name: "Put-bucket-versioning-invalid-body",
|
||||
app: app,
|
||||
args: args{
|
||||
req: httptest.NewRequest(http.MethodPut, "/my-bucket?versioning", nil),
|
||||
},
|
||||
wantErr: false,
|
||||
statusCode: 400,
|
||||
},
|
||||
{
|
||||
name: "Put-bucket-versioning-success",
|
||||
app: app,
|
||||
args: args{
|
||||
req: httptest.NewRequest(http.MethodPut, "/my-bucket?versioning", strings.NewReader(versioningBody)),
|
||||
},
|
||||
wantErr: false,
|
||||
statusCode: 200,
|
||||
},
|
||||
{
|
||||
name: "Put-bucket-acl-invalid-acl",
|
||||
app: app,
|
||||
@@ -906,12 +958,12 @@ func TestS3ApiController_PutActions(t *testing.T) {
|
||||
resp, err := tt.app.Test(tt.args.req)
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("S3ApiController.GetActions() %v error = %v, wantErr %v",
|
||||
t.Errorf("S3ApiController.PutActions() %v error = %v, wantErr %v",
|
||||
tt.name, err, tt.wantErr)
|
||||
}
|
||||
|
||||
if resp.StatusCode != tt.statusCode {
|
||||
t.Errorf("S3ApiController.GetActions() %v statusCode = %v, wantStatusCode = %v",
|
||||
t.Errorf("S3ApiController.PutActions() %v statusCode = %v, wantStatusCode = %v",
|
||||
tt.name, resp.StatusCode, tt.statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,12 @@ func AclParser(be backend.Backend, logger s3log.AuditLogger) fiber.Handler {
|
||||
if ctx.Method() == http.MethodPatch {
|
||||
return ctx.Next()
|
||||
}
|
||||
if len(pathParts) == 2 && pathParts[1] != "" && ctx.Method() == http.MethodPut && !ctx.Request().URI().QueryArgs().Has("acl") && !ctx.Request().URI().QueryArgs().Has("tagging") {
|
||||
if len(pathParts) == 2 &&
|
||||
pathParts[1] != "" &&
|
||||
ctx.Method() == http.MethodPut &&
|
||||
!ctx.Request().URI().QueryArgs().Has("acl") &&
|
||||
!ctx.Request().URI().QueryArgs().Has("tagging") &&
|
||||
!ctx.Request().URI().QueryArgs().Has("versioning") {
|
||||
if err := auth.MayCreateBucket(acct, isRoot); err != nil {
|
||||
return controllers.SendXMLResponse(ctx, nil, err, &controllers.MetaOpts{Logger: logger, Action: "CreateBucket"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user