mirror of
https://github.com/versity/versitygw.git
synced 2026-01-07 04:06:23 +00:00
fix: Changed WORM protection implementation to prevent root/admin users to overwrite objects in governance mode or if legal hold is set up
This commit is contained in:
@@ -135,7 +135,7 @@ func ParseObjectLegalHoldOutput(status *bool) *types.ObjectLockLegalHold {
|
||||
}
|
||||
}
|
||||
|
||||
func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects []string, isAdminOrRoot bool, be backend.Backend) error {
|
||||
func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects []string, be backend.Backend) error {
|
||||
data, err := be.GetObjectLockConfiguration(ctx, bucket)
|
||||
if err != nil {
|
||||
if errors.Is(err, s3err.GetAPIError(s3err.ErrObjectLockConfigurationNotFound)) {
|
||||
@@ -180,18 +180,16 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [
|
||||
if retention.RetainUntilDate.After(time.Now()) {
|
||||
switch retention.Mode {
|
||||
case types.ObjectLockRetentionModeGovernance:
|
||||
if !isAdminOrRoot {
|
||||
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 {
|
||||
return s3err.GetAPIError(s3err.ErrObjectLocked)
|
||||
}
|
||||
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 {
|
||||
return s3err.GetAPIError(s3err.ErrObjectLocked)
|
||||
}
|
||||
case types.ObjectLockRetentionModeCompliance:
|
||||
return s3err.GetAPIError(s3err.ErrObjectLocked)
|
||||
@@ -208,7 +206,7 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [
|
||||
return err
|
||||
}
|
||||
|
||||
if *status && !isAdminOrRoot {
|
||||
if *status {
|
||||
return s3err.GetAPIError(s3err.ErrObjectLocked)
|
||||
}
|
||||
}
|
||||
@@ -225,15 +223,13 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [
|
||||
if expirationDate.After(time.Now()) {
|
||||
switch bucketLockConfig.DefaultRetention.Mode {
|
||||
case types.ObjectLockRetentionModeGovernance:
|
||||
if !isAdminOrRoot {
|
||||
policy, err := be.GetBucketPolicy(ctx, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = verifyBucketPolicy(policy, userAccess, bucket, "", BypassGovernanceRetentionAction)
|
||||
if err != nil {
|
||||
return s3err.GetAPIError(s3err.ErrObjectLocked)
|
||||
}
|
||||
policy, err := be.GetBucketPolicy(ctx, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = verifyBucketPolicy(policy, userAccess, bucket, "", BypassGovernanceRetentionAction)
|
||||
if err != nil {
|
||||
return s3err.GetAPIError(s3err.ErrObjectLocked)
|
||||
}
|
||||
case types.ObjectLockRetentionModeCompliance:
|
||||
return s3err.GetAPIError(s3err.ErrObjectLocked)
|
||||
|
||||
@@ -1797,7 +1797,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []string{keyStart}, isRoot || acct.Role == auth.RoleAdmin, c.be)
|
||||
err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []string{keyStart}, c.be)
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -2002,7 +2002,7 @@ func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, utils.ParseDeleteObjects(dObj.Objects), isRoot || acct.Role == auth.RoleAdmin, c.be)
|
||||
err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, utils.ParseDeleteObjects(dObj.Objects), c.be)
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -2137,7 +2137,7 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []string{key}, isRoot || acct.Role == auth.RoleAdmin, c.be)
|
||||
err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []string{key}, c.be)
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
|
||||
@@ -372,9 +372,7 @@ func TestGetObjectLegalHold(s *S3Conf) {
|
||||
|
||||
func TestWORMProtection(s *S3Conf) {
|
||||
WORMProtection_bucket_object_lock_configuration_compliance_mode(s)
|
||||
WORMProtection_bucket_object_lock_governance_root_overwrite(s)
|
||||
WORMProtection_object_lock_retention_compliance_root_access_denied(s)
|
||||
WORMProtection_object_lock_retention_governance_root_overwrite(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)
|
||||
@@ -690,9 +688,7 @@ func GetIntTests() IntTests {
|
||||
"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_governance_root_overwrite": WORMProtection_bucket_object_lock_governance_root_overwrite,
|
||||
"WORMProtection_object_lock_retention_compliance_root_access_denied": WORMProtection_object_lock_retention_compliance_root_access_denied,
|
||||
"WORMProtection_object_lock_retention_governance_root_overwrite": WORMProtection_object_lock_retention_governance_root_overwrite,
|
||||
"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,
|
||||
|
||||
@@ -4465,6 +4465,10 @@ func CreateMultipartUpload_with_object_lock(s *S3Conf) error {
|
||||
return fmt.Errorf("expected uploaded object lock mode to be %v, instead got %v", types.ObjectLockModeGovernance, resp.ObjectLockMode)
|
||||
}
|
||||
|
||||
if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}, withLock())
|
||||
}
|
||||
@@ -7682,47 +7686,6 @@ func WORMProtection_bucket_object_lock_configuration_compliance_mode(s *S3Conf)
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
func WORMProtection_bucket_object_lock_governance_root_overwrite(s *S3Conf) error {
|
||||
testName := "WORMProtection_bucket_object_lock_governance_root_overwrite"
|
||||
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
|
||||
}
|
||||
|
||||
// create an object
|
||||
if err := putObjects(s3client, []string{object}, bucket); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// overwrite the object
|
||||
if err := putObjects(s3client, []string{object}, bucket); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
func WORMProtection_object_lock_retention_compliance_root_access_denied(s *S3Conf) error {
|
||||
testName := "WORMProtection_object_lock_retention_compliance_root_access_denied"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -7762,46 +7725,6 @@ func WORMProtection_object_lock_retention_compliance_root_access_denied(s *S3Con
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
func WORMProtection_object_lock_retention_governance_root_overwrite(s *S3Conf) error {
|
||||
testName := "WORMProtection_object_lock_retention_governance_root_overwrite"
|
||||
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
|
||||
}
|
||||
|
||||
if err := putObjects(s3client, []string{object}, bucket); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}, 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 {
|
||||
@@ -7940,7 +7863,8 @@ func WORMProtection_object_lock_legal_hold_root_overwrite(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := putObjects(s3client, []string{object}, bucket); err != nil {
|
||||
err = putObjects(s3client, []string{object}, bucket)
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLocked)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user