From fb27e2703e872e657a95df039f7c2a65de8c2c25 Mon Sep 17 00:00:00 2001 From: jonaustin09 Date: Fri, 24 May 2024 13:50:41 -0400 Subject: [PATCH 1/2] feat: Implemented to logic to bypass governance retention --- auth/acl.go | 2 +- auth/bucket_policy.go | 2 +- auth/object_lock.go | 49 ++++++++++++++-------- backend/azure/azure.go | 4 +- backend/backend.go | 4 +- backend/posix/posix.go | 37 +++++++++++++++-- backend/s3proxy/s3.go | 11 ++--- s3api/controllers/backend_moq_test.go | 14 +++++-- s3api/controllers/base.go | 24 +++++++++-- s3api/controllers/base_test.go | 2 +- tests/integration/group-tests.go | 2 - tests/integration/tests.go | 58 +-------------------------- 12 files changed, 109 insertions(+), 100 deletions(-) diff --git a/auth/acl.go b/auth/acl.go index bd8e808c..b51742ba 100644 --- a/auth/acl.go +++ b/auth/acl.go @@ -309,7 +309,7 @@ func VerifyAccess(ctx context.Context, be backend.Backend, opts AccessOptions) e return s3err.GetAPIError(s3err.ErrAccessDenied) } - if err := verifyBucketPolicy(policy, opts.Acc.Access, opts.Bucket, opts.Object, opts.Action); err != nil { + if err := VerifyBucketPolicy(policy, opts.Acc.Access, opts.Bucket, opts.Object, opts.Action); err != nil { return err } if err := verifyACL(opts.Acl, opts.Acc.Access, opts.AclPermission); err != nil { diff --git a/auth/bucket_policy.go b/auth/bucket_policy.go index 4602269c..67fc7f3f 100644 --- a/auth/bucket_policy.go +++ b/auth/bucket_policy.go @@ -115,7 +115,7 @@ func ValidatePolicyDocument(policyBin []byte, bucket string, iam IAMService) err return nil } -func verifyBucketPolicy(policy []byte, access, bucket, object string, action Action) error { +func VerifyBucketPolicy(policy []byte, access, bucket, object string, action Action) error { // If bucket policy is not set if policy == nil { return nil diff --git a/auth/object_lock.go b/auth/object_lock.go index 464e4ae0..b59712fe 100644 --- a/auth/object_lock.go +++ b/auth/object_lock.go @@ -135,7 +135,7 @@ func ParseObjectLegalHoldOutput(status *bool) *types.ObjectLockLegalHold { } } -func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects []string, be backend.Backend) error { +func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects []string, bypass bool, be backend.Backend) error { data, err := be.GetObjectLockConfiguration(ctx, bucket) if err != nil { if errors.Is(err, s3err.GetAPIError(s3err.ErrObjectLockConfigurationNotFound)) { @@ -157,7 +157,7 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [ objExists := true for _, obj := range objects { - var checkRetention bool = true + checkRetention := true retentionData, err := be.GetObjectRetention(ctx, bucket, obj, "") if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchKey)) { objExists = false @@ -180,16 +180,20 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [ if retention.RetainUntilDate.After(time.Now()) { switch retention.Mode { case types.ObjectLockRetentionModeGovernance: - policy, err := be.GetBucketPolicy(ctx, bucket) - if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchBucketPolicy)) { - return s3err.GetAPIError(s3err.ErrObjectLocked) - } - if err != nil { - return err - } - err = verifyBucketPolicy(policy, userAccess, bucket, obj, BypassGovernanceRetentionAction) - if err != nil { + if !bypass { return s3err.GetAPIError(s3err.ErrObjectLocked) + } else { + policy, err := be.GetBucketPolicy(ctx, bucket) + if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchBucketPolicy)) { + return s3err.GetAPIError(s3err.ErrAccessDenied) + } + if err != nil { + return err + } + err = VerifyBucketPolicy(policy, userAccess, bucket, obj, BypassGovernanceRetentionAction) + if err != nil { + return s3err.GetAPIError(s3err.ErrAccessDenied) + } } case types.ObjectLockRetentionModeCompliance: return s3err.GetAPIError(s3err.ErrObjectLocked) @@ -211,6 +215,8 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [ } } + fmt.Println(objExists, "objExists") + if bucketLockConfig.DefaultRetention != nil && bucketLockConfig.CreatedAt != nil && objExists { expirationDate := *bucketLockConfig.CreatedAt if bucketLockConfig.DefaultRetention.Days != nil { @@ -223,13 +229,20 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [ if expirationDate.After(time.Now()) { switch bucketLockConfig.DefaultRetention.Mode { case types.ObjectLockRetentionModeGovernance: - policy, err := be.GetBucketPolicy(ctx, bucket) - if err != nil { - return err - } - err = verifyBucketPolicy(policy, userAccess, bucket, "", BypassGovernanceRetentionAction) - if err != nil { + if !bypass { return s3err.GetAPIError(s3err.ErrObjectLocked) + } else { + policy, err := be.GetBucketPolicy(ctx, bucket) + if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchBucketPolicy)) { + return s3err.GetAPIError(s3err.ErrAccessDenied) + } + if err != nil { + return err + } + err = VerifyBucketPolicy(policy, userAccess, bucket, "", BypassGovernanceRetentionAction) + if err != nil { + return s3err.GetAPIError(s3err.ErrAccessDenied) + } } case types.ObjectLockRetentionModeCompliance: return s3err.GetAPIError(s3err.ErrObjectLocked) @@ -237,5 +250,7 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [ } } + fmt.Println("the code is hereeeeee") + return nil } diff --git a/backend/azure/azure.go b/backend/azure/azure.go index 6e06eab4..9eae15f2 100644 --- a/backend/azure/azure.go +++ b/backend/azure/azure.go @@ -250,7 +250,7 @@ func (az *Azure) PutObject(ctx context.Context, po *s3.PutObjectInput) (string, if err != nil { return "", fmt.Errorf("parse object lock retention: %w", err) } - if err := az.PutObjectRetention(ctx, *po.Bucket, *po.Key, "", retParsed); err != nil { + if err := az.PutObjectRetention(ctx, *po.Bucket, *po.Key, "", true, retParsed); err != nil { return "", err } } @@ -1024,7 +1024,7 @@ func (az *Azure) GetObjectLockConfiguration(ctx context.Context, bucket string) return []byte(*config), nil } -func (az *Azure) PutObjectRetention(ctx context.Context, bucket, object, versionId string, retention []byte) error { +func (az *Azure) PutObjectRetention(ctx context.Context, bucket, object, versionId string, bypass bool, retention []byte) error { contClient, err := az.getContainerClient(bucket) if err != nil { return err diff --git a/backend/backend.go b/backend/backend.go index 3e742310..5fe7078e 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -84,7 +84,7 @@ type Backend interface { // object lock operations PutObjectLockConfiguration(_ context.Context, bucket string, config []byte) error GetObjectLockConfiguration(_ context.Context, bucket string) ([]byte, error) - PutObjectRetention(_ context.Context, bucket, object, versionId string, retention []byte) error + PutObjectRetention(_ context.Context, bucket, object, versionId string, bypass bool, retention []byte) error GetObjectRetention(_ context.Context, bucket, object, versionId string) ([]byte, error) PutObjectLegalHold(_ context.Context, bucket, object, versionId string, status bool) error GetObjectLegalHold(_ context.Context, bucket, object, versionId string) (*bool, error) @@ -243,7 +243,7 @@ func (BackendUnsupported) PutObjectLockConfiguration(_ context.Context, bucket s func (BackendUnsupported) GetObjectLockConfiguration(_ context.Context, bucket string) ([]byte, error) { return nil, s3err.GetAPIError(s3err.ErrNotImplemented) } -func (BackendUnsupported) PutObjectRetention(_ context.Context, bucket, object, versionId string, retention []byte) error { +func (BackendUnsupported) PutObjectRetention(_ context.Context, bucket, object, versionId string, bypass bool, retention []byte) error { return s3err.GetAPIError(s3err.ErrNotImplemented) } func (BackendUnsupported) GetObjectRetention(_ context.Context, bucket, object, versionId string) ([]byte, error) { diff --git a/backend/posix/posix.go b/backend/posix/posix.go index 7bca3f36..b59fdeda 100644 --- a/backend/posix/posix.go +++ b/backend/posix/posix.go @@ -430,7 +430,7 @@ func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu *s3.CreateMultipa os.Remove(tmppath) return nil, fmt.Errorf("parse object lock retention: %w", err) } - if err := p.PutObjectRetention(ctx, bucket, filepath.Join(objdir, uploadID), "", retParsed); err != nil { + if err := p.PutObjectRetention(ctx, bucket, filepath.Join(objdir, uploadID), "", true, retParsed); err != nil { // cleanup object if returning error os.RemoveAll(filepath.Join(tmppath, uploadID)) os.Remove(tmppath) @@ -1403,7 +1403,9 @@ func (p *Posix) PutObject(ctx context.Context, po *s3.PutObjectInput) (string, e if err != nil { return "", fmt.Errorf("parse object lock retention: %w", err) } - if err := p.PutObjectRetention(ctx, *po.Bucket, *po.Key, "", retParsed); err != nil { + fmt.Println("putting object retention") + if err := p.PutObjectRetention(ctx, *po.Bucket, *po.Key, "", true, retParsed); err != nil { + fmt.Println("put object retention error: ", err) return "", err } } @@ -2459,7 +2461,7 @@ func (p *Posix) GetObjectLegalHold(_ context.Context, bucket, object, versionId return &result, nil } -func (p *Posix) PutObjectRetention(_ context.Context, bucket, object, versionId string, retention []byte) error { +func (p *Posix) PutObjectRetention(_ context.Context, bucket, object, versionId string, bypass bool, retention []byte) error { _, err := os.Stat(bucket) if errors.Is(err, fs.ErrNotExist) { return s3err.GetAPIError(s3err.ErrNoSuchBucket) @@ -2485,11 +2487,38 @@ func (p *Posix) PutObjectRetention(_ context.Context, bucket, object, versionId return s3err.GetAPIError(s3err.ErrInvalidBucketObjectLockConfiguration) } - err = p.meta.StoreAttribute(bucket, object, objectRetentionKey, retention) + objectLockCfg, err := p.meta.RetrieveAttribute(bucket, object, objectRetentionKey) if errors.Is(err, fs.ErrNotExist) { return s3err.GetAPIError(s3err.ErrNoSuchKey) } + if errors.Is(err, meta.ErrNoSuchKey) { + if err := p.meta.StoreAttribute(bucket, object, objectRetentionKey, retention); err != nil { + return fmt.Errorf("set object lock config: %w", err) + } + + return nil + } if err != nil { + return fmt.Errorf("get object lock config: %w", err) + } + + var lockCfg types.ObjectLockRetention + if err := json.Unmarshal(objectLockCfg, &lockCfg); err != nil { + return fmt.Errorf("unmarshal object lock config: %w", err) + } + + switch lockCfg.Mode { + // Compliance mode can't be overriden + case types.ObjectLockRetentionModeCompliance: + return s3err.GetAPIError(s3err.ErrMethodNotAllowed) + // To override governance mode user should have "s3:BypassGovernanceRetention" permission + case types.ObjectLockRetentionModeGovernance: + if !bypass { + return s3err.GetAPIError(s3err.ErrMethodNotAllowed) + } + } + + if err := p.meta.StoreAttribute(bucket, object, objectRetentionKey, retention); err != nil { return fmt.Errorf("set object lock config: %w", err) } diff --git a/backend/s3proxy/s3.go b/backend/s3proxy/s3.go index 5871a990..98ffd8c9 100644 --- a/backend/s3proxy/s3.go +++ b/backend/s3proxy/s3.go @@ -530,17 +530,18 @@ func (s *S3Proxy) GetObjectLockConfiguration(ctx context.Context, bucket string) return json.Marshal(config) } -func (s *S3Proxy) PutObjectRetention(ctx context.Context, bucket, object, versionId string, retention []byte) error { +func (s *S3Proxy) PutObjectRetention(ctx context.Context, bucket, object, versionId string, bypass bool, retention []byte) error { ret, err := auth.ParseObjectLockRetentionOutput(retention) if err != nil { return err } _, err = s.client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{ - Bucket: &bucket, - Key: &object, - VersionId: &versionId, - Retention: ret, + Bucket: &bucket, + Key: &object, + VersionId: &versionId, + Retention: ret, + BypassGovernanceRetention: &bypass, }) return handleError(err) } diff --git a/s3api/controllers/backend_moq_test.go b/s3api/controllers/backend_moq_test.go index dc42bdce..12583c6b 100644 --- a/s3api/controllers/backend_moq_test.go +++ b/s3api/controllers/backend_moq_test.go @@ -143,7 +143,7 @@ var _ backend.Backend = &BackendMock{} // PutObjectLockConfigurationFunc: func(contextMoqParam context.Context, bucket string, config []byte) error { // panic("mock out the PutObjectLockConfiguration method") // }, -// PutObjectRetentionFunc: func(contextMoqParam context.Context, bucket string, object string, versionId string, retention []byte) error { +// PutObjectRetentionFunc: func(contextMoqParam context.Context, bucket string, object string, versionId string, bypass bool, retention []byte) error { // panic("mock out the PutObjectRetention method") // }, // PutObjectTaggingFunc: func(contextMoqParam context.Context, bucket string, object string, tags map[string]string) error { @@ -295,7 +295,7 @@ type BackendMock struct { PutObjectLockConfigurationFunc func(contextMoqParam context.Context, bucket string, config []byte) error // PutObjectRetentionFunc mocks the PutObjectRetention method. - PutObjectRetentionFunc func(contextMoqParam context.Context, bucket string, object string, versionId string, retention []byte) error + PutObjectRetentionFunc func(contextMoqParam context.Context, bucket string, object string, versionId string, bypass bool, retention []byte) error // PutObjectTaggingFunc mocks the PutObjectTagging method. PutObjectTaggingFunc func(contextMoqParam context.Context, bucket string, object string, tags map[string]string) error @@ -642,6 +642,8 @@ type BackendMock struct { Object string // VersionId is the versionId argument value. VersionId string + // Bypass is the bypass argument value. + Bypass bool // Retention is the retention argument value. Retention []byte } @@ -2246,7 +2248,7 @@ func (mock *BackendMock) PutObjectLockConfigurationCalls() []struct { } // PutObjectRetention calls PutObjectRetentionFunc. -func (mock *BackendMock) PutObjectRetention(contextMoqParam context.Context, bucket string, object string, versionId string, retention []byte) error { +func (mock *BackendMock) PutObjectRetention(contextMoqParam context.Context, bucket string, object string, versionId string, bypass bool, retention []byte) error { if mock.PutObjectRetentionFunc == nil { panic("BackendMock.PutObjectRetentionFunc: method is nil but Backend.PutObjectRetention was just called") } @@ -2255,18 +2257,20 @@ func (mock *BackendMock) PutObjectRetention(contextMoqParam context.Context, buc Bucket string Object string VersionId string + Bypass bool Retention []byte }{ ContextMoqParam: contextMoqParam, Bucket: bucket, Object: object, VersionId: versionId, + Bypass: bypass, Retention: retention, } mock.lockPutObjectRetention.Lock() mock.calls.PutObjectRetention = append(mock.calls.PutObjectRetention, callInfo) mock.lockPutObjectRetention.Unlock() - return mock.PutObjectRetentionFunc(contextMoqParam, bucket, object, versionId, retention) + return mock.PutObjectRetentionFunc(contextMoqParam, bucket, object, versionId, bypass, retention) } // PutObjectRetentionCalls gets all the calls that were made to PutObjectRetention. @@ -2278,6 +2282,7 @@ func (mock *BackendMock) PutObjectRetentionCalls() []struct { Bucket string Object string VersionId string + Bypass bool Retention []byte } { var calls []struct { @@ -2285,6 +2290,7 @@ func (mock *BackendMock) PutObjectRetentionCalls() []struct { Bucket string Object string VersionId string + Bypass bool Retention []byte } mock.lockPutObjectRetention.RLock() diff --git a/s3api/controllers/base.go b/s3api/controllers/base.go index 5eae739d..bb2d8cf7 100644 --- a/s3api/controllers/base.go +++ b/s3api/controllers/base.go @@ -1383,6 +1383,19 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error { }) } + bypassHdr := ctx.Get("") + bypass := bypassHdr == "true" + if bypass { + policy, err := c.be.GetBucketPolicy(ctx.Context(), bucket) + if err != nil { + bypass = false + } else { + if err := auth.VerifyBucketPolicy(policy, acct.Access, bucket, keyStart, auth.BypassGovernanceRetentionAction); err != nil { + bypass = false + } + } + } + retention, err := auth.ParseObjectLockRetentionInput(ctx.Body()) if err != nil { return SendResponse(ctx, err, &MetaOpts{ @@ -1392,7 +1405,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error { }) } - err = c.be.PutObjectRetention(ctx.Context(), bucket, keyStart, versionId, retention) + err = c.be.PutObjectRetention(ctx.Context(), bucket, keyStart, versionId, bypass, retention) return SendResponse(ctx, err, &MetaOpts{ Logger: c.logger, Action: "PutObjectRetention", @@ -1797,7 +1810,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error { }) } - err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []string{keyStart}, c.be) + err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []string{keyStart}, true, c.be) if err != nil { return SendResponse(ctx, err, &MetaOpts{ @@ -1968,6 +1981,7 @@ func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) error { acct := ctx.Locals("account").(auth.Account) isRoot := ctx.Locals("isRoot").(bool) parsedAcl := ctx.Locals("parsedAcl").(auth.ACL) + bypass := ctx.Get("X-Amz-Bypass-Governance-Retention") var dObj s3response.DeleteObjects err := xml.Unmarshal(ctx.Body(), &dObj) @@ -2002,7 +2016,7 @@ func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) error { }) } - err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, utils.ParseDeleteObjects(dObj.Objects), c.be) + err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, utils.ParseDeleteObjects(dObj.Objects), bypass == "true", c.be) if err != nil { return SendResponse(ctx, err, &MetaOpts{ @@ -2038,6 +2052,8 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error { acct := ctx.Locals("account").(auth.Account) isRoot := ctx.Locals("isRoot").(bool) parsedAcl := ctx.Locals("parsedAcl").(auth.ACL) + bypass := ctx.Get("X-Amz-Bypass-Governance-Retention") + fmt.Println("bypass: ", bypass) if keyEnd != "" { key = strings.Join([]string{key, keyEnd}, "/") @@ -2137,7 +2153,7 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error { }) } - err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []string{key}, c.be) + err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []string{key}, bypass == "true", c.be) if err != nil { return SendResponse(ctx, err, &MetaOpts{ diff --git a/s3api/controllers/base_test.go b/s3api/controllers/base_test.go index abbbbc5b..019a9d24 100644 --- a/s3api/controllers/base_test.go +++ b/s3api/controllers/base_test.go @@ -929,7 +929,7 @@ func TestS3ApiController_PutActions(t *testing.T) { PutObjectLegalHoldFunc: func(contextMoqParam context.Context, bucket, object, versionId string, status bool) error { return nil }, - PutObjectRetentionFunc: func(contextMoqParam context.Context, bucket, object, versionId string, retention []byte) error { + PutObjectRetentionFunc: func(contextMoqParam context.Context, bucket, object, versionId string, bypass bool, retention []byte) error { return nil }, GetObjectLockConfigurationFunc: func(contextMoqParam context.Context, bucket string) ([]byte, error) { diff --git a/tests/integration/group-tests.go b/tests/integration/group-tests.go index 8d1ccba1..99b0dfe0 100644 --- a/tests/integration/group-tests.go +++ b/tests/integration/group-tests.go @@ -373,7 +373,6 @@ func TestGetObjectLegalHold(s *S3Conf) { func TestWORMProtection(s *S3Conf) { WORMProtection_bucket_object_lock_configuration_compliance_mode(s) WORMProtection_object_lock_retention_compliance_root_access_denied(s) - WORMProtection_object_lock_retention_governance_user_access_denied(s) WORMProtection_object_lock_legal_hold_user_access_denied(s) WORMProtection_object_lock_legal_hold_root_overwrite(s) } @@ -689,7 +688,6 @@ func GetIntTests() IntTests { "GetObjectLegalHold_success": GetObjectLegalHold_success, "WORMProtection_bucket_object_lock_configuration_compliance_mode": WORMProtection_bucket_object_lock_configuration_compliance_mode, "WORMProtection_object_lock_retention_compliance_root_access_denied": WORMProtection_object_lock_retention_compliance_root_access_denied, - "WORMProtection_object_lock_retention_governance_user_access_denied": WORMProtection_object_lock_retention_governance_user_access_denied, "WORMProtection_object_lock_legal_hold_user_access_denied": WORMProtection_object_lock_legal_hold_user_access_denied, "WORMProtection_object_lock_legal_hold_root_overwrite": WORMProtection_object_lock_legal_hold_root_overwrite, "PutObject_overwrite_dir_obj": PutObject_overwrite_dir_obj, diff --git a/tests/integration/tests.go b/tests/integration/tests.go index d4ae6a30..5bf3f8b0 100644 --- a/tests/integration/tests.go +++ b/tests/integration/tests.go @@ -2536,6 +2536,7 @@ func PutObject_with_object_lock(s *S3Conf) error { ObjectLockMode: types.ObjectLockModeCompliance, ObjectLockRetainUntilDate: &retainDate, }) + cancel() if err != nil { failF("%v: %v", testName, err) @@ -7725,63 +7726,6 @@ func WORMProtection_object_lock_retention_compliance_root_access_denied(s *S3Con }, withLock()) } -func WORMProtection_object_lock_retention_governance_user_access_denied(s *S3Conf) error { - testName := "WORMProtection_object_lock_retention_governance_user_access_denied" - return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { - if err := changeBucketObjectLockStatus(s3client, bucket, true); err != nil { - return err - } - - object := "my-obj" - - if err := putObjects(s3client, []string{object}, bucket); err != nil { - return err - } - - date := time.Now().Add(time.Hour * 3) - ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) - _, err := s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{ - Bucket: &bucket, - Key: &object, - Retention: &types.ObjectLockRetention{ - Mode: types.ObjectLockRetentionModeGovernance, - RetainUntilDate: &date, - }, - }) - cancel() - if err != nil { - return err - } - - usr := user{ - access: "grt1", - secret: "grt1secret", - role: "user", - } - if err := createUsers(s, []user{usr}); err != nil { - return err - } - if err := changeBucketsOwner(s, []string{bucket}, usr.access); err != nil { - return err - } - - cfg := *s - cfg.awsID = usr.access - cfg.awsSecret = usr.secret - - err = putObjects(s3.NewFromConfig(cfg.Config()), []string{object}, bucket) - if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLocked)); err != nil { - return err - } - - if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil { - return err - } - - return nil - }, withLock()) -} - func WORMProtection_object_lock_legal_hold_user_access_denied(s *S3Conf) error { testName := "WORMProtection_object_lock_legal_hold_user_access_denied" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { From 9e8458a09ff1f4708d2c87cb34a12f641bb90e0a Mon Sep 17 00:00:00 2001 From: jonaustin09 Date: Tue, 28 May 2024 15:17:25 -0400 Subject: [PATCH 2/2] feat: Added integration tests for bypass governance retention functionality --- auth/object_lock.go | 56 ++-- backend/posix/posix.go | 2 - s3api/controllers/base.go | 3 +- tests/integration/group-tests.go | 523 +++++++++++++++--------------- tests/integration/tests.go | 526 +++++++++++++++++++++++++++++-- 5 files changed, 800 insertions(+), 310 deletions(-) diff --git a/auth/object_lock.go b/auth/object_lock.go index b59712fe..76286a0b 100644 --- a/auth/object_lock.go +++ b/auth/object_lock.go @@ -154,13 +154,26 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [ return nil } - objExists := true + checkDefaultRetention := false + + if bucketLockConfig.DefaultRetention != nil && bucketLockConfig.CreatedAt != nil { + expirationDate := *bucketLockConfig.CreatedAt + if bucketLockConfig.DefaultRetention.Days != nil { + expirationDate = expirationDate.AddDate(0, 0, int(*bucketLockConfig.DefaultRetention.Days)) + } + if bucketLockConfig.DefaultRetention.Years != nil { + expirationDate = expirationDate.AddDate(int(*bucketLockConfig.DefaultRetention.Years), 0, 0) + } + + if expirationDate.After(time.Now()) { + checkDefaultRetention = true + } + } for _, obj := range objects { checkRetention := true retentionData, err := be.GetObjectRetention(ctx, bucket, obj, "") if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchKey)) { - objExists = false continue } if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchObjectLockConfiguration)) { @@ -185,14 +198,14 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [ } else { policy, err := be.GetBucketPolicy(ctx, bucket) if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchBucketPolicy)) { - return s3err.GetAPIError(s3err.ErrAccessDenied) + return s3err.GetAPIError(s3err.ErrObjectLocked) } if err != nil { return err } err = VerifyBucketPolicy(policy, userAccess, bucket, obj, BypassGovernanceRetentionAction) if err != nil { - return s3err.GetAPIError(s3err.ErrAccessDenied) + return s3err.GetAPIError(s3err.ErrObjectLocked) } } case types.ObjectLockRetentionModeCompliance: @@ -202,31 +215,22 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [ } } + checkLegalHold := true + status, err := be.GetObjectLegalHold(ctx, bucket, obj, "") - if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchObjectLockConfiguration)) { - continue - } if err != nil { - return err + if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchObjectLockConfiguration)) { + checkLegalHold = false + } else { + return err + } } - if *status { + if checkLegalHold && *status { return s3err.GetAPIError(s3err.ErrObjectLocked) } - } - fmt.Println(objExists, "objExists") - - if bucketLockConfig.DefaultRetention != nil && bucketLockConfig.CreatedAt != nil && objExists { - expirationDate := *bucketLockConfig.CreatedAt - if bucketLockConfig.DefaultRetention.Days != nil { - expirationDate = expirationDate.AddDate(0, 0, int(*bucketLockConfig.DefaultRetention.Days)) - } - if bucketLockConfig.DefaultRetention.Years != nil { - expirationDate = expirationDate.AddDate(int(*bucketLockConfig.DefaultRetention.Years), 0, 0) - } - - if expirationDate.After(time.Now()) { + if checkDefaultRetention { switch bucketLockConfig.DefaultRetention.Mode { case types.ObjectLockRetentionModeGovernance: if !bypass { @@ -234,14 +238,14 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [ } else { policy, err := be.GetBucketPolicy(ctx, bucket) if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchBucketPolicy)) { - return s3err.GetAPIError(s3err.ErrAccessDenied) + return s3err.GetAPIError(s3err.ErrObjectLocked) } if err != nil { return err } - err = VerifyBucketPolicy(policy, userAccess, bucket, "", BypassGovernanceRetentionAction) + err = VerifyBucketPolicy(policy, userAccess, bucket, obj, BypassGovernanceRetentionAction) if err != nil { - return s3err.GetAPIError(s3err.ErrAccessDenied) + return s3err.GetAPIError(s3err.ErrObjectLocked) } } case types.ObjectLockRetentionModeCompliance: @@ -250,7 +254,5 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [ } } - fmt.Println("the code is hereeeeee") - return nil } diff --git a/backend/posix/posix.go b/backend/posix/posix.go index b59fdeda..74fc824d 100644 --- a/backend/posix/posix.go +++ b/backend/posix/posix.go @@ -1403,9 +1403,7 @@ func (p *Posix) PutObject(ctx context.Context, po *s3.PutObjectInput) (string, e if err != nil { return "", fmt.Errorf("parse object lock retention: %w", err) } - fmt.Println("putting object retention") if err := p.PutObjectRetention(ctx, *po.Bucket, *po.Key, "", true, retParsed); err != nil { - fmt.Println("put object retention error: ", err) return "", err } } diff --git a/s3api/controllers/base.go b/s3api/controllers/base.go index bb2d8cf7..de4b5979 100644 --- a/s3api/controllers/base.go +++ b/s3api/controllers/base.go @@ -1383,7 +1383,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error { }) } - bypassHdr := ctx.Get("") + bypassHdr := ctx.Get("X-Amz-Bypass-Governance-Retention") bypass := bypassHdr == "true" if bypass { policy, err := c.be.GetBucketPolicy(ctx.Context(), bucket) @@ -2053,7 +2053,6 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error { isRoot := ctx.Locals("isRoot").(bool) parsedAcl := ctx.Locals("parsedAcl").(auth.ACL) bypass := ctx.Get("X-Amz-Bypass-Governance-Retention") - fmt.Println("bypass: ", bypass) if keyEnd != "" { key = strings.Join([]string{key, keyEnd}, "/") diff --git a/tests/integration/group-tests.go b/tests/integration/group-tests.go index 99b0dfe0..8118b6ca 100644 --- a/tests/integration/group-tests.go +++ b/tests/integration/group-tests.go @@ -343,6 +343,9 @@ func TestPutObjectRetention(s *S3Conf) { PutObjectRetention_disabled_bucket_object_lock_config(s) PutObjectRetention_expired_retain_until_date(s) PutObjectRetention_invalid_mode(s) + PutObjectRetention_overwrite_compliance_mode(s) + PutObjectRetention_overwrite_governance_without_bypass_specified(s) + PutObjectRetention_overwrite_governance_with_permission(s) PutObjectRetention_success(s) } @@ -372,9 +375,14 @@ func TestGetObjectLegalHold(s *S3Conf) { func TestWORMProtection(s *S3Conf) { WORMProtection_bucket_object_lock_configuration_compliance_mode(s) - WORMProtection_object_lock_retention_compliance_root_access_denied(s) - WORMProtection_object_lock_legal_hold_user_access_denied(s) - WORMProtection_object_lock_legal_hold_root_overwrite(s) + WORMProtection_bucket_object_lock_configuration_governance_mode(s) + WORMProtection_bucket_object_lock_governance_bypass_delete(s) + WORMProtection_object_lock_retention_compliance_locked(s) + WORMProtection_object_lock_retention_governance_locked(s) + WORMProtection_object_lock_retention_governance_bypass_overwrite(s) + WORMProtection_object_lock_retention_governance_bypass_delete(s) + WORMProtection_object_lock_retention_governance_bypass_delete_mul(s) + WORMProtection_object_lock_legal_hold_locked(s) } func TestFullFlow(s *S3Conf) { @@ -448,255 +456,264 @@ type IntTests map[string]func(s *S3Conf) error func GetIntTests() IntTests { return IntTests{ - "Authentication_empty_auth_header": Authentication_empty_auth_header, - "Authentication_invalid_auth_header": Authentication_invalid_auth_header, - "Authentication_unsupported_signature_version": Authentication_unsupported_signature_version, - "Authentication_malformed_credentials": Authentication_malformed_credentials, - "Authentication_malformed_credentials_invalid_parts": Authentication_malformed_credentials_invalid_parts, - "Authentication_credentials_terminated_string": Authentication_credentials_terminated_string, - "Authentication_credentials_incorrect_service": Authentication_credentials_incorrect_service, - "Authentication_credentials_incorrect_region": Authentication_credentials_incorrect_region, - "Authentication_credentials_invalid_date": Authentication_credentials_invalid_date, - "Authentication_credentials_future_date": Authentication_credentials_future_date, - "Authentication_credentials_past_date": Authentication_credentials_past_date, - "Authentication_credentials_non_existing_access_key": Authentication_credentials_non_existing_access_key, - "Authentication_invalid_signed_headers": Authentication_invalid_signed_headers, - "Authentication_missing_date_header": Authentication_missing_date_header, - "Authentication_invalid_date_header": Authentication_invalid_date_header, - "Authentication_date_mismatch": Authentication_date_mismatch, - "Authentication_incorrect_payload_hash": Authentication_incorrect_payload_hash, - "Authentication_incorrect_md5": Authentication_incorrect_md5, - "Authentication_signature_error_incorrect_secret_key": Authentication_signature_error_incorrect_secret_key, - "PresignedAuth_missing_algo_query_param": PresignedAuth_missing_algo_query_param, - "PresignedAuth_unsupported_algorithm": PresignedAuth_unsupported_algorithm, - "PresignedAuth_missing_credentials_query_param": PresignedAuth_missing_credentials_query_param, - "PresignedAuth_malformed_creds_invalid_parts": PresignedAuth_malformed_creds_invalid_parts, - "PresignedAuth_creds_invalid_terminator": PresignedAuth_creds_invalid_terminator, - "PresignedAuth_creds_incorrect_service": PresignedAuth_creds_incorrect_service, - "PresignedAuth_creds_incorrect_region": PresignedAuth_creds_incorrect_region, - "PresignedAuth_creds_invalid_date": PresignedAuth_creds_invalid_date, - "PresignedAuth_missing_date_query": PresignedAuth_missing_date_query, - "PresignedAuth_dates_mismatch": PresignedAuth_dates_mismatch, - "PresignedAuth_non_existing_access_key_id": PresignedAuth_non_existing_access_key_id, - "PresignedAuth_missing_signed_headers_query_param": PresignedAuth_missing_signed_headers_query_param, - "PresignedAuth_missing_expiration_query_param": PresignedAuth_missing_expiration_query_param, - "PresignedAuth_invalid_expiration_query_param": PresignedAuth_invalid_expiration_query_param, - "PresignedAuth_negative_expiration_query_param": PresignedAuth_negative_expiration_query_param, - "PresignedAuth_exceeding_expiration_query_param": PresignedAuth_exceeding_expiration_query_param, - "PresignedAuth_expired_request": PresignedAuth_expired_request, - "PresignedAuth_incorrect_secret_key": PresignedAuth_incorrect_secret_key, - "PresignedAuth_PutObject_success": PresignedAuth_PutObject_success, - "PutObject_missing_object_lock_retention_config": PutObject_missing_object_lock_retention_config, - "PutObject_with_object_lock": PutObject_with_object_lock, - "PresignedAuth_Put_GetObject_with_data": PresignedAuth_Put_GetObject_with_data, - "PresignedAuth_Put_GetObject_with_UTF8_chars": PresignedAuth_Put_GetObject_with_UTF8_chars, - "PresignedAuth_UploadPart": PresignedAuth_UploadPart, - "CreateBucket_invalid_bucket_name": CreateBucket_invalid_bucket_name, - "CreateBucket_existing_bucket": CreateBucket_existing_bucket, - "CreateBucket_owned_by_you": CreateBucket_owned_by_you, - "CreateBucket_as_user": CreateBucket_as_user, - "CreateDeleteBucket_success": CreateDeleteBucket_success, - "CreateBucket_default_acl": CreateBucket_default_acl, - "CreateBucket_non_default_acl": CreateBucket_non_default_acl, - "CreateBucket_default_object_lock": CreateBucket_default_object_lock, - "HeadBucket_non_existing_bucket": HeadBucket_non_existing_bucket, - "HeadBucket_success": HeadBucket_success, - "ListBuckets_as_user": ListBuckets_as_user, - "ListBuckets_as_admin": ListBuckets_as_admin, - "ListBuckets_success": ListBuckets_success, - "DeleteBucket_non_existing_bucket": DeleteBucket_non_existing_bucket, - "DeleteBucket_non_empty_bucket": DeleteBucket_non_empty_bucket, - "DeleteBucket_success_status_code": DeleteBucket_success_status_code, - "PutBucketTagging_non_existing_bucket": PutBucketTagging_non_existing_bucket, - "PutBucketTagging_long_tags": PutBucketTagging_long_tags, - "PutBucketTagging_success": PutBucketTagging_success, - "GetBucketTagging_non_existing_bucket": GetBucketTagging_non_existing_bucket, - "GetBucketTagging_unset_tags": GetBucketTagging_unset_tags, - "GetBucketTagging_success": GetBucketTagging_success, - "DeleteBucketTagging_non_existing_object": DeleteBucketTagging_non_existing_object, - "DeleteBucketTagging_success_status": DeleteBucketTagging_success_status, - "DeleteBucketTagging_success": DeleteBucketTagging_success, - "PutObject_non_existing_bucket": PutObject_non_existing_bucket, - "PutObject_special_chars": PutObject_special_chars, - "PutObject_invalid_long_tags": PutObject_invalid_long_tags, - "PutObject_success": PutObject_success, - "HeadObject_non_existing_object": HeadObject_non_existing_object, - "HeadObject_invalid_part_number": HeadObject_invalid_part_number, - "HeadObject_non_existing_mp": HeadObject_non_existing_mp, - "HeadObject_mp_success": HeadObject_mp_success, - "HeadObject_success": HeadObject_success, - "GetObjectAttributes_non_existing_bucket": GetObjectAttributes_non_existing_bucket, - "GetObjectAttributes_non_existing_object": GetObjectAttributes_non_existing_object, - "GetObjectAttributes_existing_object": GetObjectAttributes_existing_object, - "GetObjectAttributes_multipart_upload": GetObjectAttributes_multipart_upload, - "GetObjectAttributes_multipart_upload_truncated": GetObjectAttributes_multipart_upload_truncated, - "GetObject_non_existing_key": GetObject_non_existing_key, - "GetObject_invalid_ranges": GetObject_invalid_ranges, - "GetObject_with_meta": GetObject_with_meta, - "GetObject_success": GetObject_success, - "GetObject_by_range_success": GetObject_by_range_success, - "GetObject_by_range_resp_status": GetObject_by_range_resp_status, - "ListObjects_non_existing_bucket": ListObjects_non_existing_bucket, - "ListObjects_with_prefix": ListObjects_with_prefix, - "ListObject_truncated": ListObject_truncated, - "ListObjects_invalid_max_keys": ListObjects_invalid_max_keys, - "ListObjects_max_keys_0": ListObjects_max_keys_0, - "ListObjects_delimiter": ListObjects_delimiter, - "ListObjects_max_keys_none": ListObjects_max_keys_none, - "ListObjects_marker_not_from_obj_list": ListObjects_marker_not_from_obj_list, - "ListObjectsV2_start_after": ListObjectsV2_start_after, - "ListObjectsV2_both_start_after_and_continuation_token": ListObjectsV2_both_start_after_and_continuation_token, - "ListObjectsV2_start_after_not_in_list": ListObjectsV2_start_after_not_in_list, - "ListObjectsV2_start_after_empty_result": ListObjectsV2_start_after_empty_result, - "DeleteObject_non_existing_object": DeleteObject_non_existing_object, - "DeleteObject_success": DeleteObject_success, - "DeleteObject_success_status_code": DeleteObject_success_status_code, - "DeleteObjects_empty_input": DeleteObjects_empty_input, - "DeleteObjects_non_existing_objects": DeleteObjects_non_existing_objects, - "DeleteObjects_success": DeleteObjects_success, - "CopyObject_non_existing_dst_bucket": CopyObject_non_existing_dst_bucket, - "CopyObject_not_owned_source_bucket": CopyObject_not_owned_source_bucket, - "CopyObject_copy_to_itself": CopyObject_copy_to_itself, - "CopyObject_to_itself_with_new_metadata": CopyObject_to_itself_with_new_metadata, - "CopyObject_success": CopyObject_success, - "PutObjectTagging_non_existing_object": PutObjectTagging_non_existing_object, - "PutObjectTagging_long_tags": PutObjectTagging_long_tags, - "PutObjectTagging_success": PutObjectTagging_success, - "GetObjectTagging_non_existing_object": GetObjectTagging_non_existing_object, - "GetObjectTagging_unset_tags": GetObjectTagging_unset_tags, - "GetObjectTagging_success": GetObjectTagging_success, - "DeleteObjectTagging_non_existing_object": DeleteObjectTagging_non_existing_object, - "DeleteObjectTagging_success_status": DeleteObjectTagging_success_status, - "DeleteObjectTagging_success": DeleteObjectTagging_success, - "CreateMultipartUpload_non_existing_bucket": CreateMultipartUpload_non_existing_bucket, - "CreateMultipartUpload_with_metadata": CreateMultipartUpload_with_metadata, - "CreateMultipartUpload_with_invalid_tagging": CreateMultipartUpload_with_invalid_tagging, - "CreateMultipartUpload_with_tagging": CreateMultipartUpload_with_tagging, - "CreateMultipartUpload_with_content_type": CreateMultipartUpload_with_content_type, - "CreateMultipartUpload_with_object_lock": CreateMultipartUpload_with_object_lock, - "CreateMultipartUpload_with_object_lock_not_enabled": CreateMultipartUpload_with_object_lock_not_enabled, - "CreateMultipartUpload_with_object_lock_invalid_retention": CreateMultipartUpload_with_object_lock_invalid_retention, - "CreateMultipartUpload_past_retain_until_date": CreateMultipartUpload_past_retain_until_date, - "CreateMultipartUpload_success": CreateMultipartUpload_success, - "UploadPart_non_existing_bucket": UploadPart_non_existing_bucket, - "UploadPart_invalid_part_number": UploadPart_invalid_part_number, - "UploadPart_non_existing_key": UploadPart_non_existing_key, - "UploadPart_non_existing_mp_upload": UploadPart_non_existing_mp_upload, - "UploadPart_success": UploadPart_success, - "UploadPartCopy_non_existing_bucket": UploadPartCopy_non_existing_bucket, - "UploadPartCopy_incorrect_uploadId": UploadPartCopy_incorrect_uploadId, - "UploadPartCopy_incorrect_object_key": UploadPartCopy_incorrect_object_key, - "UploadPartCopy_invalid_part_number": UploadPartCopy_invalid_part_number, - "UploadPartCopy_invalid_copy_source": UploadPartCopy_invalid_copy_source, - "UploadPartCopy_non_existing_source_bucket": UploadPartCopy_non_existing_source_bucket, - "UploadPartCopy_non_existing_source_object_key": UploadPartCopy_non_existing_source_object_key, - "UploadPartCopy_success": UploadPartCopy_success, - "UploadPartCopy_by_range_invalid_range": UploadPartCopy_by_range_invalid_range, - "UploadPartCopy_greater_range_than_obj_size": UploadPartCopy_greater_range_than_obj_size, - "UploadPartCopy_by_range_success": UploadPartCopy_by_range_success, - "ListParts_incorrect_uploadId": ListParts_incorrect_uploadId, - "ListParts_incorrect_object_key": ListParts_incorrect_object_key, - "ListParts_success": ListParts_success, - "ListMultipartUploads_non_existing_bucket": ListMultipartUploads_non_existing_bucket, - "ListMultipartUploads_empty_result": ListMultipartUploads_empty_result, - "ListMultipartUploads_invalid_max_uploads": ListMultipartUploads_invalid_max_uploads, - "ListMultipartUploads_max_uploads": ListMultipartUploads_max_uploads, - "ListMultipartUploads_incorrect_next_key_marker": ListMultipartUploads_incorrect_next_key_marker, - "ListMultipartUploads_ignore_upload_id_marker": ListMultipartUploads_ignore_upload_id_marker, - "ListMultipartUploads_success": ListMultipartUploads_success, - "AbortMultipartUpload_non_existing_bucket": AbortMultipartUpload_non_existing_bucket, - "AbortMultipartUpload_incorrect_uploadId": AbortMultipartUpload_incorrect_uploadId, - "AbortMultipartUpload_incorrect_object_key": AbortMultipartUpload_incorrect_object_key, - "AbortMultipartUpload_success": AbortMultipartUpload_success, - "AbortMultipartUpload_success_status_code": AbortMultipartUpload_success_status_code, - "CompletedMultipartUpload_non_existing_bucket": CompletedMultipartUpload_non_existing_bucket, - "CompleteMultipartUpload_invalid_part_number": CompleteMultipartUpload_invalid_part_number, - "CompleteMultipartUpload_invalid_ETag": CompleteMultipartUpload_invalid_ETag, - "CompleteMultipartUpload_success": CompleteMultipartUpload_success, - "PutBucketAcl_non_existing_bucket": PutBucketAcl_non_existing_bucket, - "PutBucketAcl_invalid_acl_canned_and_acp": PutBucketAcl_invalid_acl_canned_and_acp, - "PutBucketAcl_invalid_acl_canned_and_grants": PutBucketAcl_invalid_acl_canned_and_grants, - "PutBucketAcl_invalid_acl_acp_and_grants": PutBucketAcl_invalid_acl_acp_and_grants, - "PutBucketAcl_invalid_owner": PutBucketAcl_invalid_owner, - "PutBucketAcl_success_access_denied": PutBucketAcl_success_access_denied, - "PutBucketAcl_success_grants": PutBucketAcl_success_grants, - "PutBucketAcl_success_canned_acl": PutBucketAcl_success_canned_acl, - "PutBucketAcl_success_acp": PutBucketAcl_success_acp, - "GetBucketAcl_non_existing_bucket": GetBucketAcl_non_existing_bucket, - "GetBucketAcl_access_denied": GetBucketAcl_access_denied, - "GetBucketAcl_success": GetBucketAcl_success, - "PutBucketPolicy_non_existing_bucket": PutBucketPolicy_non_existing_bucket, - "PutBucketPolicy_invalid_effect": PutBucketPolicy_invalid_effect, - "PutBucketPolicy_empty_actions_string": PutBucketPolicy_empty_actions_string, - "PutBucketPolicy_empty_actions_array": PutBucketPolicy_empty_actions_array, - "PutBucketPolicy_invalid_action": PutBucketPolicy_invalid_action, - "PutBucketPolicy_unsupported_action": PutBucketPolicy_unsupported_action, - "PutBucketPolicy_incorrect_action_wildcard_usage": PutBucketPolicy_incorrect_action_wildcard_usage, - "PutBucketPolicy_empty_principals_string": PutBucketPolicy_empty_principals_string, - "PutBucketPolicy_empty_principals_array": PutBucketPolicy_empty_principals_array, - "PutBucketPolicy_principals_aws_struct_empty_string": PutBucketPolicy_principals_aws_struct_empty_string, - "PutBucketPolicy_principals_aws_struct_empty_string_slice": PutBucketPolicy_principals_aws_struct_empty_string_slice, - "PutBucketPolicy_principals_incorrect_wildcard_usage": PutBucketPolicy_principals_incorrect_wildcard_usage, - "PutBucketPolicy_non_existing_principals": PutBucketPolicy_non_existing_principals, - "PutBucketPolicy_empty_resources_string": PutBucketPolicy_empty_resources_string, - "PutBucketPolicy_empty_resources_array": PutBucketPolicy_empty_resources_array, - "PutBucketPolicy_invalid_resource_prefix": PutBucketPolicy_invalid_resource_prefix, - "PutBucketPolicy_invalid_resource_with_starting_slash": PutBucketPolicy_invalid_resource_with_starting_slash, - "PutBucketPolicy_duplicate_resource": PutBucketPolicy_duplicate_resource, - "PutBucketPolicy_incorrect_bucket_name": PutBucketPolicy_incorrect_bucket_name, - "PutBucketPolicy_object_action_on_bucket_resource": PutBucketPolicy_object_action_on_bucket_resource, - "PutBucketPolicy_bucket_action_on_object_resource": PutBucketPolicy_bucket_action_on_object_resource, - "PutBucketPolicy_success": PutBucketPolicy_success, - "GetBucketPolicy_non_existing_bucket": GetBucketPolicy_non_existing_bucket, - "GetBucketPolicy_not_set": GetBucketPolicy_not_set, - "GetBucketPolicy_success": GetBucketPolicy_success, - "DeleteBucketPolicy_non_existing_bucket": DeleteBucketPolicy_non_existing_bucket, - "DeleteBucketPolicy_remove_before_setting": DeleteBucketPolicy_remove_before_setting, - "DeleteBucketPolicy_success": DeleteBucketPolicy_success, - "PutObjectLockConfiguration_non_existing_bucket": PutObjectLockConfiguration_non_existing_bucket, - "PutObjectLockConfiguration_empty_config": PutObjectLockConfiguration_empty_config, - "PutObjectLockConfiguration_not_enabled_on_bucket_creation": PutObjectLockConfiguration_not_enabled_on_bucket_creation, - "PutObjectLockConfiguration_invalid_status": PutObjectLockConfiguration_invalid_status, - "PutObjectLockConfiguration_invalid_mode": PutObjectLockConfiguration_invalid_mode, - "PutObjectLockConfiguration_both_years_and_days": PutObjectLockConfiguration_both_years_and_days, - "PutObjectLockConfiguration_invalid_years_days": PutObjectLockConfiguration_invalid_years_days, - "PutObjectLockConfiguration_success": PutObjectLockConfiguration_success, - "GetObjectLockConfiguration_non_existing_bucket": GetObjectLockConfiguration_non_existing_bucket, - "GetObjectLockConfiguration_unset_config": GetObjectLockConfiguration_unset_config, - "GetObjectLockConfiguration_success": GetObjectLockConfiguration_success, - "PutObjectRetention_non_existing_bucket": PutObjectRetention_non_existing_bucket, - "PutObjectRetention_non_existing_object": PutObjectRetention_non_existing_object, - "PutObjectRetention_unset_bucket_object_lock_config": PutObjectRetention_unset_bucket_object_lock_config, - "PutObjectRetention_disabled_bucket_object_lock_config": PutObjectRetention_disabled_bucket_object_lock_config, - "PutObjectRetention_expired_retain_until_date": PutObjectRetention_expired_retain_until_date, - "PutObjectRetention_invalid_mode": PutObjectRetention_invalid_mode, - "PutObjectRetention_success": PutObjectRetention_success, - "GetObjectRetention_non_existing_bucket": GetObjectRetention_non_existing_bucket, - "GetObjectRetention_non_existing_object": GetObjectRetention_non_existing_object, - "GetObjectRetention_unset_config": GetObjectRetention_unset_config, - "GetObjectRetention_success": GetObjectRetention_success, - "PutObjectLegalHold_non_existing_bucket": PutObjectLegalHold_non_existing_bucket, - "PutObjectLegalHold_non_existing_object": PutObjectLegalHold_non_existing_object, - "PutObjectLegalHold_invalid_body": PutObjectLegalHold_invalid_body, - "PutObjectLegalHold_invalid_status": PutObjectLegalHold_invalid_status, - "PutObjectLegalHold_unset_bucket_object_lock_config": PutObjectLegalHold_unset_bucket_object_lock_config, - "PutObjectLegalHold_disabled_bucket_object_lock_config": PutObjectLegalHold_disabled_bucket_object_lock_config, - "PutObjectLegalHold_success": PutObjectLegalHold_success, - "GetObjectLegalHold_non_existing_bucket": GetObjectLegalHold_non_existing_bucket, - "GetObjectLegalHold_non_existing_object": GetObjectLegalHold_non_existing_object, - "GetObjectLegalHold_unset_config": GetObjectLegalHold_unset_config, - "GetObjectLegalHold_success": GetObjectLegalHold_success, - "WORMProtection_bucket_object_lock_configuration_compliance_mode": WORMProtection_bucket_object_lock_configuration_compliance_mode, - "WORMProtection_object_lock_retention_compliance_root_access_denied": WORMProtection_object_lock_retention_compliance_root_access_denied, - "WORMProtection_object_lock_legal_hold_user_access_denied": WORMProtection_object_lock_legal_hold_user_access_denied, - "WORMProtection_object_lock_legal_hold_root_overwrite": WORMProtection_object_lock_legal_hold_root_overwrite, - "PutObject_overwrite_dir_obj": PutObject_overwrite_dir_obj, - "PutObject_overwrite_file_obj": PutObject_overwrite_file_obj, - "PutObject_dir_obj_with_data": PutObject_dir_obj_with_data, - "CreateMultipartUpload_dir_obj": CreateMultipartUpload_dir_obj, - "IAM_user_access_denied": IAM_user_access_denied, - "IAM_userplus_access_denied": IAM_userplus_access_denied, - "IAM_userplus_CreateBucket": IAM_userplus_CreateBucket, - "IAM_admin_ChangeBucketOwner": IAM_admin_ChangeBucketOwner, + "Authentication_empty_auth_header": Authentication_empty_auth_header, + "Authentication_invalid_auth_header": Authentication_invalid_auth_header, + "Authentication_unsupported_signature_version": Authentication_unsupported_signature_version, + "Authentication_malformed_credentials": Authentication_malformed_credentials, + "Authentication_malformed_credentials_invalid_parts": Authentication_malformed_credentials_invalid_parts, + "Authentication_credentials_terminated_string": Authentication_credentials_terminated_string, + "Authentication_credentials_incorrect_service": Authentication_credentials_incorrect_service, + "Authentication_credentials_incorrect_region": Authentication_credentials_incorrect_region, + "Authentication_credentials_invalid_date": Authentication_credentials_invalid_date, + "Authentication_credentials_future_date": Authentication_credentials_future_date, + "Authentication_credentials_past_date": Authentication_credentials_past_date, + "Authentication_credentials_non_existing_access_key": Authentication_credentials_non_existing_access_key, + "Authentication_invalid_signed_headers": Authentication_invalid_signed_headers, + "Authentication_missing_date_header": Authentication_missing_date_header, + "Authentication_invalid_date_header": Authentication_invalid_date_header, + "Authentication_date_mismatch": Authentication_date_mismatch, + "Authentication_incorrect_payload_hash": Authentication_incorrect_payload_hash, + "Authentication_incorrect_md5": Authentication_incorrect_md5, + "Authentication_signature_error_incorrect_secret_key": Authentication_signature_error_incorrect_secret_key, + "PresignedAuth_missing_algo_query_param": PresignedAuth_missing_algo_query_param, + "PresignedAuth_unsupported_algorithm": PresignedAuth_unsupported_algorithm, + "PresignedAuth_missing_credentials_query_param": PresignedAuth_missing_credentials_query_param, + "PresignedAuth_malformed_creds_invalid_parts": PresignedAuth_malformed_creds_invalid_parts, + "PresignedAuth_creds_invalid_terminator": PresignedAuth_creds_invalid_terminator, + "PresignedAuth_creds_incorrect_service": PresignedAuth_creds_incorrect_service, + "PresignedAuth_creds_incorrect_region": PresignedAuth_creds_incorrect_region, + "PresignedAuth_creds_invalid_date": PresignedAuth_creds_invalid_date, + "PresignedAuth_missing_date_query": PresignedAuth_missing_date_query, + "PresignedAuth_dates_mismatch": PresignedAuth_dates_mismatch, + "PresignedAuth_non_existing_access_key_id": PresignedAuth_non_existing_access_key_id, + "PresignedAuth_missing_signed_headers_query_param": PresignedAuth_missing_signed_headers_query_param, + "PresignedAuth_missing_expiration_query_param": PresignedAuth_missing_expiration_query_param, + "PresignedAuth_invalid_expiration_query_param": PresignedAuth_invalid_expiration_query_param, + "PresignedAuth_negative_expiration_query_param": PresignedAuth_negative_expiration_query_param, + "PresignedAuth_exceeding_expiration_query_param": PresignedAuth_exceeding_expiration_query_param, + "PresignedAuth_expired_request": PresignedAuth_expired_request, + "PresignedAuth_incorrect_secret_key": PresignedAuth_incorrect_secret_key, + "PresignedAuth_PutObject_success": PresignedAuth_PutObject_success, + "PutObject_missing_object_lock_retention_config": PutObject_missing_object_lock_retention_config, + "PutObject_with_object_lock": PutObject_with_object_lock, + "PresignedAuth_Put_GetObject_with_data": PresignedAuth_Put_GetObject_with_data, + "PresignedAuth_Put_GetObject_with_UTF8_chars": PresignedAuth_Put_GetObject_with_UTF8_chars, + "PresignedAuth_UploadPart": PresignedAuth_UploadPart, + "CreateBucket_invalid_bucket_name": CreateBucket_invalid_bucket_name, + "CreateBucket_existing_bucket": CreateBucket_existing_bucket, + "CreateBucket_owned_by_you": CreateBucket_owned_by_you, + "CreateBucket_as_user": CreateBucket_as_user, + "CreateDeleteBucket_success": CreateDeleteBucket_success, + "CreateBucket_default_acl": CreateBucket_default_acl, + "CreateBucket_non_default_acl": CreateBucket_non_default_acl, + "CreateBucket_default_object_lock": CreateBucket_default_object_lock, + "HeadBucket_non_existing_bucket": HeadBucket_non_existing_bucket, + "HeadBucket_success": HeadBucket_success, + "ListBuckets_as_user": ListBuckets_as_user, + "ListBuckets_as_admin": ListBuckets_as_admin, + "ListBuckets_success": ListBuckets_success, + "DeleteBucket_non_existing_bucket": DeleteBucket_non_existing_bucket, + "DeleteBucket_non_empty_bucket": DeleteBucket_non_empty_bucket, + "DeleteBucket_success_status_code": DeleteBucket_success_status_code, + "PutBucketTagging_non_existing_bucket": PutBucketTagging_non_existing_bucket, + "PutBucketTagging_long_tags": PutBucketTagging_long_tags, + "PutBucketTagging_success": PutBucketTagging_success, + "GetBucketTagging_non_existing_bucket": GetBucketTagging_non_existing_bucket, + "GetBucketTagging_unset_tags": GetBucketTagging_unset_tags, + "GetBucketTagging_success": GetBucketTagging_success, + "DeleteBucketTagging_non_existing_object": DeleteBucketTagging_non_existing_object, + "DeleteBucketTagging_success_status": DeleteBucketTagging_success_status, + "DeleteBucketTagging_success": DeleteBucketTagging_success, + "PutObject_non_existing_bucket": PutObject_non_existing_bucket, + "PutObject_special_chars": PutObject_special_chars, + "PutObject_invalid_long_tags": PutObject_invalid_long_tags, + "PutObject_success": PutObject_success, + "HeadObject_non_existing_object": HeadObject_non_existing_object, + "HeadObject_invalid_part_number": HeadObject_invalid_part_number, + "HeadObject_non_existing_mp": HeadObject_non_existing_mp, + "HeadObject_mp_success": HeadObject_mp_success, + "HeadObject_success": HeadObject_success, + "GetObjectAttributes_non_existing_bucket": GetObjectAttributes_non_existing_bucket, + "GetObjectAttributes_non_existing_object": GetObjectAttributes_non_existing_object, + "GetObjectAttributes_existing_object": GetObjectAttributes_existing_object, + "GetObjectAttributes_multipart_upload": GetObjectAttributes_multipart_upload, + "GetObjectAttributes_multipart_upload_truncated": GetObjectAttributes_multipart_upload_truncated, + "GetObject_non_existing_key": GetObject_non_existing_key, + "GetObject_invalid_ranges": GetObject_invalid_ranges, + "GetObject_with_meta": GetObject_with_meta, + "GetObject_success": GetObject_success, + "GetObject_by_range_success": GetObject_by_range_success, + "GetObject_by_range_resp_status": GetObject_by_range_resp_status, + "ListObjects_non_existing_bucket": ListObjects_non_existing_bucket, + "ListObjects_with_prefix": ListObjects_with_prefix, + "ListObject_truncated": ListObject_truncated, + "ListObjects_invalid_max_keys": ListObjects_invalid_max_keys, + "ListObjects_max_keys_0": ListObjects_max_keys_0, + "ListObjects_delimiter": ListObjects_delimiter, + "ListObjects_max_keys_none": ListObjects_max_keys_none, + "ListObjects_marker_not_from_obj_list": ListObjects_marker_not_from_obj_list, + "ListObjectsV2_start_after": ListObjectsV2_start_after, + "ListObjectsV2_both_start_after_and_continuation_token": ListObjectsV2_both_start_after_and_continuation_token, + "ListObjectsV2_start_after_not_in_list": ListObjectsV2_start_after_not_in_list, + "ListObjectsV2_start_after_empty_result": ListObjectsV2_start_after_empty_result, + "DeleteObject_non_existing_object": DeleteObject_non_existing_object, + "DeleteObject_success": DeleteObject_success, + "DeleteObject_success_status_code": DeleteObject_success_status_code, + "DeleteObjects_empty_input": DeleteObjects_empty_input, + "DeleteObjects_non_existing_objects": DeleteObjects_non_existing_objects, + "DeleteObjects_success": DeleteObjects_success, + "CopyObject_non_existing_dst_bucket": CopyObject_non_existing_dst_bucket, + "CopyObject_not_owned_source_bucket": CopyObject_not_owned_source_bucket, + "CopyObject_copy_to_itself": CopyObject_copy_to_itself, + "CopyObject_to_itself_with_new_metadata": CopyObject_to_itself_with_new_metadata, + "CopyObject_success": CopyObject_success, + "PutObjectTagging_non_existing_object": PutObjectTagging_non_existing_object, + "PutObjectTagging_long_tags": PutObjectTagging_long_tags, + "PutObjectTagging_success": PutObjectTagging_success, + "GetObjectTagging_non_existing_object": GetObjectTagging_non_existing_object, + "GetObjectTagging_unset_tags": GetObjectTagging_unset_tags, + "GetObjectTagging_success": GetObjectTagging_success, + "DeleteObjectTagging_non_existing_object": DeleteObjectTagging_non_existing_object, + "DeleteObjectTagging_success_status": DeleteObjectTagging_success_status, + "DeleteObjectTagging_success": DeleteObjectTagging_success, + "CreateMultipartUpload_non_existing_bucket": CreateMultipartUpload_non_existing_bucket, + "CreateMultipartUpload_with_metadata": CreateMultipartUpload_with_metadata, + "CreateMultipartUpload_with_invalid_tagging": CreateMultipartUpload_with_invalid_tagging, + "CreateMultipartUpload_with_tagging": CreateMultipartUpload_with_tagging, + "CreateMultipartUpload_with_content_type": CreateMultipartUpload_with_content_type, + "CreateMultipartUpload_with_object_lock": CreateMultipartUpload_with_object_lock, + "CreateMultipartUpload_with_object_lock_not_enabled": CreateMultipartUpload_with_object_lock_not_enabled, + "CreateMultipartUpload_with_object_lock_invalid_retention": CreateMultipartUpload_with_object_lock_invalid_retention, + "CreateMultipartUpload_past_retain_until_date": CreateMultipartUpload_past_retain_until_date, + "CreateMultipartUpload_success": CreateMultipartUpload_success, + "UploadPart_non_existing_bucket": UploadPart_non_existing_bucket, + "UploadPart_invalid_part_number": UploadPart_invalid_part_number, + "UploadPart_non_existing_key": UploadPart_non_existing_key, + "UploadPart_non_existing_mp_upload": UploadPart_non_existing_mp_upload, + "UploadPart_success": UploadPart_success, + "UploadPartCopy_non_existing_bucket": UploadPartCopy_non_existing_bucket, + "UploadPartCopy_incorrect_uploadId": UploadPartCopy_incorrect_uploadId, + "UploadPartCopy_incorrect_object_key": UploadPartCopy_incorrect_object_key, + "UploadPartCopy_invalid_part_number": UploadPartCopy_invalid_part_number, + "UploadPartCopy_invalid_copy_source": UploadPartCopy_invalid_copy_source, + "UploadPartCopy_non_existing_source_bucket": UploadPartCopy_non_existing_source_bucket, + "UploadPartCopy_non_existing_source_object_key": UploadPartCopy_non_existing_source_object_key, + "UploadPartCopy_success": UploadPartCopy_success, + "UploadPartCopy_by_range_invalid_range": UploadPartCopy_by_range_invalid_range, + "UploadPartCopy_greater_range_than_obj_size": UploadPartCopy_greater_range_than_obj_size, + "UploadPartCopy_by_range_success": UploadPartCopy_by_range_success, + "ListParts_incorrect_uploadId": ListParts_incorrect_uploadId, + "ListParts_incorrect_object_key": ListParts_incorrect_object_key, + "ListParts_success": ListParts_success, + "ListMultipartUploads_non_existing_bucket": ListMultipartUploads_non_existing_bucket, + "ListMultipartUploads_empty_result": ListMultipartUploads_empty_result, + "ListMultipartUploads_invalid_max_uploads": ListMultipartUploads_invalid_max_uploads, + "ListMultipartUploads_max_uploads": ListMultipartUploads_max_uploads, + "ListMultipartUploads_incorrect_next_key_marker": ListMultipartUploads_incorrect_next_key_marker, + "ListMultipartUploads_ignore_upload_id_marker": ListMultipartUploads_ignore_upload_id_marker, + "ListMultipartUploads_success": ListMultipartUploads_success, + "AbortMultipartUpload_non_existing_bucket": AbortMultipartUpload_non_existing_bucket, + "AbortMultipartUpload_incorrect_uploadId": AbortMultipartUpload_incorrect_uploadId, + "AbortMultipartUpload_incorrect_object_key": AbortMultipartUpload_incorrect_object_key, + "AbortMultipartUpload_success": AbortMultipartUpload_success, + "AbortMultipartUpload_success_status_code": AbortMultipartUpload_success_status_code, + "CompletedMultipartUpload_non_existing_bucket": CompletedMultipartUpload_non_existing_bucket, + "CompleteMultipartUpload_invalid_part_number": CompleteMultipartUpload_invalid_part_number, + "CompleteMultipartUpload_invalid_ETag": CompleteMultipartUpload_invalid_ETag, + "CompleteMultipartUpload_success": CompleteMultipartUpload_success, + "PutBucketAcl_non_existing_bucket": PutBucketAcl_non_existing_bucket, + "PutBucketAcl_invalid_acl_canned_and_acp": PutBucketAcl_invalid_acl_canned_and_acp, + "PutBucketAcl_invalid_acl_canned_and_grants": PutBucketAcl_invalid_acl_canned_and_grants, + "PutBucketAcl_invalid_acl_acp_and_grants": PutBucketAcl_invalid_acl_acp_and_grants, + "PutBucketAcl_invalid_owner": PutBucketAcl_invalid_owner, + "PutBucketAcl_success_access_denied": PutBucketAcl_success_access_denied, + "PutBucketAcl_success_grants": PutBucketAcl_success_grants, + "PutBucketAcl_success_canned_acl": PutBucketAcl_success_canned_acl, + "PutBucketAcl_success_acp": PutBucketAcl_success_acp, + "GetBucketAcl_non_existing_bucket": GetBucketAcl_non_existing_bucket, + "GetBucketAcl_access_denied": GetBucketAcl_access_denied, + "GetBucketAcl_success": GetBucketAcl_success, + "PutBucketPolicy_non_existing_bucket": PutBucketPolicy_non_existing_bucket, + "PutBucketPolicy_invalid_effect": PutBucketPolicy_invalid_effect, + "PutBucketPolicy_empty_actions_string": PutBucketPolicy_empty_actions_string, + "PutBucketPolicy_empty_actions_array": PutBucketPolicy_empty_actions_array, + "PutBucketPolicy_invalid_action": PutBucketPolicy_invalid_action, + "PutBucketPolicy_unsupported_action": PutBucketPolicy_unsupported_action, + "PutBucketPolicy_incorrect_action_wildcard_usage": PutBucketPolicy_incorrect_action_wildcard_usage, + "PutBucketPolicy_empty_principals_string": PutBucketPolicy_empty_principals_string, + "PutBucketPolicy_empty_principals_array": PutBucketPolicy_empty_principals_array, + "PutBucketPolicy_principals_aws_struct_empty_string": PutBucketPolicy_principals_aws_struct_empty_string, + "PutBucketPolicy_principals_aws_struct_empty_string_slice": PutBucketPolicy_principals_aws_struct_empty_string_slice, + "PutBucketPolicy_principals_incorrect_wildcard_usage": PutBucketPolicy_principals_incorrect_wildcard_usage, + "PutBucketPolicy_non_existing_principals": PutBucketPolicy_non_existing_principals, + "PutBucketPolicy_empty_resources_string": PutBucketPolicy_empty_resources_string, + "PutBucketPolicy_empty_resources_array": PutBucketPolicy_empty_resources_array, + "PutBucketPolicy_invalid_resource_prefix": PutBucketPolicy_invalid_resource_prefix, + "PutBucketPolicy_invalid_resource_with_starting_slash": PutBucketPolicy_invalid_resource_with_starting_slash, + "PutBucketPolicy_duplicate_resource": PutBucketPolicy_duplicate_resource, + "PutBucketPolicy_incorrect_bucket_name": PutBucketPolicy_incorrect_bucket_name, + "PutBucketPolicy_object_action_on_bucket_resource": PutBucketPolicy_object_action_on_bucket_resource, + "PutBucketPolicy_bucket_action_on_object_resource": PutBucketPolicy_bucket_action_on_object_resource, + "PutBucketPolicy_success": PutBucketPolicy_success, + "GetBucketPolicy_non_existing_bucket": GetBucketPolicy_non_existing_bucket, + "GetBucketPolicy_not_set": GetBucketPolicy_not_set, + "GetBucketPolicy_success": GetBucketPolicy_success, + "DeleteBucketPolicy_non_existing_bucket": DeleteBucketPolicy_non_existing_bucket, + "DeleteBucketPolicy_remove_before_setting": DeleteBucketPolicy_remove_before_setting, + "DeleteBucketPolicy_success": DeleteBucketPolicy_success, + "PutObjectLockConfiguration_non_existing_bucket": PutObjectLockConfiguration_non_existing_bucket, + "PutObjectLockConfiguration_empty_config": PutObjectLockConfiguration_empty_config, + "PutObjectLockConfiguration_not_enabled_on_bucket_creation": PutObjectLockConfiguration_not_enabled_on_bucket_creation, + "PutObjectLockConfiguration_invalid_status": PutObjectLockConfiguration_invalid_status, + "PutObjectLockConfiguration_invalid_mode": PutObjectLockConfiguration_invalid_mode, + "PutObjectLockConfiguration_both_years_and_days": PutObjectLockConfiguration_both_years_and_days, + "PutObjectLockConfiguration_invalid_years_days": PutObjectLockConfiguration_invalid_years_days, + "PutObjectLockConfiguration_success": PutObjectLockConfiguration_success, + "GetObjectLockConfiguration_non_existing_bucket": GetObjectLockConfiguration_non_existing_bucket, + "GetObjectLockConfiguration_unset_config": GetObjectLockConfiguration_unset_config, + "GetObjectLockConfiguration_success": GetObjectLockConfiguration_success, + "PutObjectRetention_non_existing_bucket": PutObjectRetention_non_existing_bucket, + "PutObjectRetention_non_existing_object": PutObjectRetention_non_existing_object, + "PutObjectRetention_unset_bucket_object_lock_config": PutObjectRetention_unset_bucket_object_lock_config, + "PutObjectRetention_disabled_bucket_object_lock_config": PutObjectRetention_disabled_bucket_object_lock_config, + "PutObjectRetention_expired_retain_until_date": PutObjectRetention_expired_retain_until_date, + "PutObjectRetention_invalid_mode": PutObjectRetention_invalid_mode, + "PutObjectRetention_overwrite_compliance_mode": PutObjectRetention_overwrite_compliance_mode, + "PutObjectRetention_overwrite_governance_without_bypass_specified": PutObjectRetention_overwrite_governance_without_bypass_specified, + "PutObjectRetention_overwrite_governance_with_permission": PutObjectRetention_overwrite_governance_with_permission, + "PutObjectRetention_success": PutObjectRetention_success, + "GetObjectRetention_non_existing_bucket": GetObjectRetention_non_existing_bucket, + "GetObjectRetention_non_existing_object": GetObjectRetention_non_existing_object, + "GetObjectRetention_unset_config": GetObjectRetention_unset_config, + "GetObjectRetention_success": GetObjectRetention_success, + "PutObjectLegalHold_non_existing_bucket": PutObjectLegalHold_non_existing_bucket, + "PutObjectLegalHold_non_existing_object": PutObjectLegalHold_non_existing_object, + "PutObjectLegalHold_invalid_body": PutObjectLegalHold_invalid_body, + "PutObjectLegalHold_invalid_status": PutObjectLegalHold_invalid_status, + "PutObjectLegalHold_unset_bucket_object_lock_config": PutObjectLegalHold_unset_bucket_object_lock_config, + "PutObjectLegalHold_disabled_bucket_object_lock_config": PutObjectLegalHold_disabled_bucket_object_lock_config, + "PutObjectLegalHold_success": PutObjectLegalHold_success, + "GetObjectLegalHold_non_existing_bucket": GetObjectLegalHold_non_existing_bucket, + "GetObjectLegalHold_non_existing_object": GetObjectLegalHold_non_existing_object, + "GetObjectLegalHold_unset_config": GetObjectLegalHold_unset_config, + "GetObjectLegalHold_success": GetObjectLegalHold_success, + "WORMProtection_bucket_object_lock_configuration_compliance_mode": WORMProtection_bucket_object_lock_configuration_compliance_mode, + "WORMProtection_bucket_object_lock_configuration_governance_mode": WORMProtection_bucket_object_lock_configuration_governance_mode, + "WORMProtection_bucket_object_lock_governance_bypass_delete": WORMProtection_bucket_object_lock_governance_bypass_delete, + "WORMProtection_bucket_object_lock_governance_bypass_delete_multiple": WORMProtection_bucket_object_lock_governance_bypass_delete_multiple, + "WORMProtection_object_lock_retention_compliance_locked": WORMProtection_object_lock_retention_compliance_locked, + "WORMProtection_object_lock_retention_governance_locked": WORMProtection_object_lock_retention_governance_locked, + "WORMProtection_object_lock_retention_governance_bypass_overwrite": WORMProtection_object_lock_retention_governance_bypass_overwrite, + "WORMProtection_object_lock_retention_governance_bypass_delete": WORMProtection_object_lock_retention_governance_bypass_delete, + "WORMProtection_object_lock_retention_governance_bypass_delete_mul": WORMProtection_object_lock_retention_governance_bypass_delete_mul, + "WORMProtection_object_lock_legal_hold_locked": WORMProtection_object_lock_legal_hold_locked, + "PutObject_overwrite_dir_obj": PutObject_overwrite_dir_obj, + "PutObject_overwrite_file_obj": PutObject_overwrite_file_obj, + "PutObject_dir_obj_with_data": PutObject_dir_obj_with_data, + "CreateMultipartUpload_dir_obj": CreateMultipartUpload_dir_obj, + "IAM_user_access_denied": IAM_user_access_denied, + "IAM_userplus_access_denied": IAM_userplus_access_denied, + "IAM_userplus_CreateBucket": IAM_userplus_CreateBucket, + "IAM_admin_ChangeBucketOwner": IAM_admin_ChangeBucketOwner, } } diff --git a/tests/integration/tests.go b/tests/integration/tests.go index 5bf3f8b0..b09f90f4 100644 --- a/tests/integration/tests.go +++ b/tests/integration/tests.go @@ -7221,6 +7221,155 @@ func PutObjectRetention_invalid_mode(s *S3Conf) error { }, withLock()) } +func PutObjectRetention_overwrite_compliance_mode(s *S3Conf) error { + testName := "PutObjectRetention_overwrite_compliance_mode" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + date := time.Now().Add(time.Hour * 3) + obj := "my-obj" + if err := putObjects(s3client, []string{obj}, bucket); err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{ + Bucket: &bucket, + Key: &obj, + Retention: &types.ObjectLockRetention{ + Mode: types.ObjectLockRetentionModeCompliance, + RetainUntilDate: &date, + }, + }) + cancel() + if err != nil { + return err + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{ + Bucket: &bucket, + Key: &obj, + Retention: &types.ObjectLockRetention{ + Mode: types.ObjectLockRetentionModeGovernance, + RetainUntilDate: &date, + }, + }) + cancel() + if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrMethodNotAllowed)); err != nil { + return err + } + + if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil { + return err + } + + return nil + }, withLock()) +} + +func PutObjectRetention_overwrite_governance_without_bypass_specified(s *S3Conf) error { + testName := "PutObjectRetention_overwrite_governance_without_bypass_specified" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + date := time.Now().Add(time.Hour * 3) + obj := "my-obj" + if err := putObjects(s3client, []string{obj}, bucket); err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{ + Bucket: &bucket, + Key: &obj, + Retention: &types.ObjectLockRetention{ + Mode: types.ObjectLockRetentionModeGovernance, + RetainUntilDate: &date, + }, + }) + cancel() + if err != nil { + return err + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{ + Bucket: &bucket, + Key: &obj, + Retention: &types.ObjectLockRetention{ + Mode: types.ObjectLockRetentionModeCompliance, + RetainUntilDate: &date, + }, + }) + cancel() + if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrMethodNotAllowed)); err != nil { + return err + } + + if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil { + return err + } + + return nil + }, withLock()) +} + +func PutObjectRetention_overwrite_governance_with_permission(s *S3Conf) error { + testName := "PutObjectRetention_overwrite_governance_with_permission" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + date := time.Now().Add(time.Hour * 3) + obj := "my-obj" + if err := putObjects(s3client, []string{obj}, bucket); err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{ + Bucket: &bucket, + Key: &obj, + Retention: &types.ObjectLockRetention{ + Mode: types.ObjectLockRetentionModeGovernance, + RetainUntilDate: &date, + }, + }) + cancel() + if err != nil { + return err + } + + policy := genPolicyDoc("Allow", `"*"`, `["s3:BypassGovernanceRetention"]`, fmt.Sprintf(`"arn:aws:s3:::%v/*"`, bucket)) + bypass := true + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{ + Bucket: &bucket, + Policy: &policy, + }) + cancel() + if err != nil { + return err + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{ + Bucket: &bucket, + Key: &obj, + Retention: &types.ObjectLockRetention{ + Mode: types.ObjectLockRetentionModeCompliance, + RetainUntilDate: &date, + }, + BypassGovernanceRetention: &bypass, + }) + cancel() + if err != nil { + return err + } + + if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil { + return err + } + + return nil + }, withLock()) +} + func PutObjectRetention_success(s *S3Conf) error { testName := "PutObjectRetention_success" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { @@ -7687,13 +7836,177 @@ func WORMProtection_bucket_object_lock_configuration_compliance_mode(s *S3Conf) }, withLock()) } -func WORMProtection_object_lock_retention_compliance_root_access_denied(s *S3Conf) error { - testName := "WORMProtection_object_lock_retention_compliance_root_access_denied" +func WORMProtection_bucket_object_lock_configuration_governance_mode(s *S3Conf) error { + testName := "WORMProtection_bucket_object_lock_configuration_governance_mode" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { - if err := changeBucketObjectLockStatus(s3client, bucket, true); err != nil { + var days int32 = 10 + object := "my-obj" + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutObjectLockConfiguration(ctx, &s3.PutObjectLockConfigurationInput{ + Bucket: &bucket, + ObjectLockConfiguration: &types.ObjectLockConfiguration{ + ObjectLockEnabled: types.ObjectLockEnabledEnabled, + Rule: &types.ObjectLockRule{ + DefaultRetention: &types.DefaultRetention{ + Mode: types.ObjectLockRetentionModeGovernance, + Days: &days, + }, + }, + }, + }) + cancel() + if err != nil { return err } + if err := putObjects(s3client, []string{object}, bucket); err != nil { + return err + } + + if err := checkWORMProtection(s3client, bucket, object); err != nil { + return err + } + if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil { + return err + } + + return nil + }, withLock()) +} + +func WORMProtection_bucket_object_lock_governance_bypass_delete(s *S3Conf) error { + testName := "WORMProtection_bucket_object_lock_governance_bypass_delete" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + var days int32 = 10 + object := "my-obj" + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutObjectLockConfiguration(ctx, &s3.PutObjectLockConfigurationInput{ + Bucket: &bucket, + ObjectLockConfiguration: &types.ObjectLockConfiguration{ + ObjectLockEnabled: types.ObjectLockEnabledEnabled, + Rule: &types.ObjectLockRule{ + DefaultRetention: &types.DefaultRetention{ + Mode: types.ObjectLockRetentionModeGovernance, + Days: &days, + }, + }, + }, + }) + cancel() + if err != nil { + return err + } + + if err := putObjects(s3client, []string{object}, bucket); err != nil { + return err + } + + policy := genPolicyDoc("Allow", `"*"`, `["s3:BypassGovernanceRetention"]`, fmt.Sprintf(`"arn:aws:s3:::%v/*"`, bucket)) + bypass := true + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{ + Bucket: &bucket, + Policy: &policy, + }) + cancel() + if err != nil { + return err + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{ + Bucket: &bucket, + Key: &object, + BypassGovernanceRetention: &bypass, + }) + cancel() + if err != nil { + return err + } + + if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil { + return err + } + + return nil + }, withLock()) +} + +func WORMProtection_bucket_object_lock_governance_bypass_delete_multiple(s *S3Conf) error { + testName := "WORMProtection_bucket_object_lock_governance_bypass_delete_multiple" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + var days int32 = 10 + obj1, obj2, obj3 := "my-obj-1", "my-obj-2", "my-obj-3" + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutObjectLockConfiguration(ctx, &s3.PutObjectLockConfigurationInput{ + Bucket: &bucket, + ObjectLockConfiguration: &types.ObjectLockConfiguration{ + ObjectLockEnabled: types.ObjectLockEnabledEnabled, + Rule: &types.ObjectLockRule{ + DefaultRetention: &types.DefaultRetention{ + Mode: types.ObjectLockRetentionModeGovernance, + Days: &days, + }, + }, + }, + }) + cancel() + if err != nil { + return err + } + + if err := putObjects(s3client, []string{obj1, obj2, obj3}, bucket); err != nil { + return err + } + + policy := genPolicyDoc("Allow", `"*"`, `["s3:BypassGovernanceRetention"]`, fmt.Sprintf(`"arn:aws:s3:::%v/*"`, bucket)) + bypass := true + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{ + Bucket: &bucket, + Policy: &policy, + }) + cancel() + if err != nil { + return err + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.DeleteObjects(ctx, &s3.DeleteObjectsInput{ + Bucket: &bucket, + BypassGovernanceRetention: &bypass, + Delete: &types.Delete{ + Objects: []types.ObjectIdentifier{ + { + Key: &obj1, + }, + { + Key: &obj2, + }, + { + Key: &obj3, + }, + }, + }, + }) + cancel() + if err != nil { + return err + } + + if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil { + return err + } + + return nil + }, withLock()) +} + +func WORMProtection_object_lock_retention_compliance_locked(s *S3Conf) error { + testName := "WORMProtection_object_lock_retention_compliance_locked" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { object := "my-obj" if err := putObjects(s3client, []string{object}, bucket); err != nil { @@ -7726,25 +8039,23 @@ func WORMProtection_object_lock_retention_compliance_root_access_denied(s *S3Con }, withLock()) } -func WORMProtection_object_lock_legal_hold_user_access_denied(s *S3Conf) error { - testName := "WORMProtection_object_lock_legal_hold_user_access_denied" +func WORMProtection_object_lock_retention_governance_locked(s *S3Conf) error { + testName := "WORMProtection_object_lock_retention_governance_locked" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { - if err := changeBucketObjectLockStatus(s3client, bucket, true); err != nil { - return err - } - object := "my-obj" if err := putObjects(s3client, []string{object}, bucket); err != nil { return err } + date := time.Now().Add(time.Hour * 3) ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) - _, err := s3client.PutObjectLegalHold(ctx, &s3.PutObjectLegalHoldInput{ + _, err := s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{ Bucket: &bucket, Key: &object, - LegalHold: &types.ObjectLockLegalHold{ - Status: types.ObjectLockLegalHoldStatusOn, + Retention: &types.ObjectLockRetention{ + Mode: types.ObjectLockRetentionModeGovernance, + RetainUntilDate: &date, }, }) cancel() @@ -7752,24 +8063,60 @@ func WORMProtection_object_lock_legal_hold_user_access_denied(s *S3Conf) error { return err } - usr := user{ - access: "grt1", - secret: "grt1secret", - role: "user", - } - if err := createUsers(s, []user{usr}); err != nil { + if err := checkWORMProtection(s3client, bucket, object); err != nil { return err } - if err := changeBucketsOwner(s, []string{bucket}, usr.access); err != nil { + if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil { return err } - cfg := *s - cfg.awsID = usr.access - cfg.awsSecret = usr.secret + return nil + }, withLock()) +} - err = putObjects(s3.NewFromConfig(cfg.Config()), []string{object}, bucket) - if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLocked)); err != nil { +func WORMProtection_object_lock_retention_governance_bypass_overwrite(s *S3Conf) error { + testName := "WORMProtection_object_lock_retention_governance_bypass_overwrite" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + object := "my-obj" + + if err := putObjects(s3client, []string{object}, bucket); err != nil { + return err + } + + date := time.Now().Add(time.Hour * 3) + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{ + Bucket: &bucket, + Key: &object, + Retention: &types.ObjectLockRetention{ + Mode: types.ObjectLockRetentionModeGovernance, + RetainUntilDate: &date, + }, + }) + cancel() + if err != nil { + return err + } + + policy := genPolicyDoc("Allow", `"*"`, `["s3:BypassGovernanceRetention"]`, fmt.Sprintf(`"arn:aws:s3:::%v/*"`, bucket)) + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{ + Bucket: &bucket, + Policy: &policy, + }) + cancel() + if err != nil { + return err + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.PutObject(ctx, &s3.PutObjectInput{ + Bucket: &bucket, + Key: &object, + }) + cancel() + if err != nil { return err } @@ -7781,8 +8128,135 @@ func WORMProtection_object_lock_legal_hold_user_access_denied(s *S3Conf) error { }, withLock()) } -func WORMProtection_object_lock_legal_hold_root_overwrite(s *S3Conf) error { - testName := "WORMProtection_object_lock_legal_hold_root_overwrite" +func WORMProtection_object_lock_retention_governance_bypass_delete(s *S3Conf) error { + testName := "WORMProtection_object_lock_retention_governance_bypass_delete" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + object := "my-obj" + + if err := putObjects(s3client, []string{object}, bucket); err != nil { + return err + } + + date := time.Now().Add(time.Hour * 3) + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{ + Bucket: &bucket, + Key: &object, + Retention: &types.ObjectLockRetention{ + Mode: types.ObjectLockRetentionModeGovernance, + RetainUntilDate: &date, + }, + }) + cancel() + if err != nil { + return err + } + + policy := genPolicyDoc("Allow", `"*"`, `["s3:BypassGovernanceRetention"]`, fmt.Sprintf(`"arn:aws:s3:::%v/*"`, bucket)) + bypass := true + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{ + Bucket: &bucket, + Policy: &policy, + }) + cancel() + if err != nil { + return err + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{ + Bucket: &bucket, + Key: &object, + BypassGovernanceRetention: &bypass, + }) + cancel() + if err != nil { + return err + } + + if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil { + return err + } + + return nil + }, withLock()) +} + +func WORMProtection_object_lock_retention_governance_bypass_delete_mul(s *S3Conf) error { + testName := "WORMProtection_object_lock_retention_governance_bypass_delete_mul" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + objs := []string{"my-obj-1", "my-obj2", "my-obj-3"} + + if err := putObjects(s3client, objs, bucket); err != nil { + return err + } + + for _, obj := range objs { + o := obj + date := time.Now().Add(time.Hour * 3) + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{ + Bucket: &bucket, + Key: &o, + Retention: &types.ObjectLockRetention{ + Mode: types.ObjectLockRetentionModeGovernance, + RetainUntilDate: &date, + }, + }) + cancel() + if err != nil { + return err + } + } + + policy := genPolicyDoc("Allow", `"*"`, `["s3:BypassGovernanceRetention"]`, fmt.Sprintf(`"arn:aws:s3:::%v/*"`, bucket)) + bypass := true + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{ + Bucket: &bucket, + Policy: &policy, + }) + cancel() + if err != nil { + return err + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.DeleteObjects(ctx, &s3.DeleteObjectsInput{ + Bucket: &bucket, + BypassGovernanceRetention: &bypass, + Delete: &types.Delete{ + Objects: []types.ObjectIdentifier{ + { + Key: &objs[0], + }, + { + Key: &objs[1], + }, + { + Key: &objs[2], + }, + }, + }, + }) + cancel() + if err != nil { + return err + } + + if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil { + return err + } + + return nil + }, withLock()) +} + +func WORMProtection_object_lock_legal_hold_locked(s *S3Conf) error { + testName := "WORMProtection_object_lock_legal_hold_locked" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { if err := changeBucketObjectLockStatus(s3client, bucket, true); err != nil { return err