mirror of
https://github.com/versity/versitygw.git
synced 2026-07-29 03:22:57 +00:00
Merge pull request #587 from versity/fix/worm-admin-users
WORM protection root/admin users
This commit is contained in:
+19
-23
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+43
-43
@@ -226,49 +226,49 @@ source ./tests/commands/select_object_content.sh
|
||||
delete_bucket_recursive "s3api" "$BUCKET_ONE_NAME"
|
||||
}
|
||||
|
||||
@test "test_get_put_object_retention" {
|
||||
# bucket must be created with lock for legal hold
|
||||
if [[ $RECREATE_BUCKETS == false ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
bucket_file="bucket_file"
|
||||
username="ABCDEFG"
|
||||
secret_key="HIJKLMN"
|
||||
|
||||
legal_hold_retention_setup "$username" "$secret_key" "$bucket_file"
|
||||
|
||||
get_object_lock_configuration "$BUCKET_ONE_NAME" || fail "error getting lock configuration"
|
||||
log 5 "$lock_config"
|
||||
enabled=$(echo "$lock_config" | jq -r ".ObjectLockConfiguration.ObjectLockEnabled")
|
||||
[[ $enabled == "Enabled" ]] || fail "ObjectLockEnabled should be 'Enabled', is '$enabled'"
|
||||
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
retention_date=$(date -v+2d +"%Y-%m-%dT%H:%M:%S")
|
||||
else
|
||||
retention_date=$(date -d "+2 days" +"%Y-%m-%dT%H:%M:%S")
|
||||
fi
|
||||
put_object_retention "$BUCKET_ONE_NAME" "$bucket_file" "GOVERNANCE" "$retention_date" || fail "failed to add object retention"
|
||||
get_object_retention "$BUCKET_ONE_NAME" "$bucket_file" || fail "failed to get object retention"
|
||||
log 5 "$retention"
|
||||
retention=$(echo "$retention" | grep -v "InsecureRequestWarning")
|
||||
mode=$(echo "$retention" | jq -r ".Retention.Mode")
|
||||
retain_until_date=$(echo "$retention" | jq -r ".Retention.RetainUntilDate")
|
||||
[[ $mode == "GOVERNANCE" ]] || fail "retention mode should be governance, is $mode"
|
||||
[[ $retain_until_date == "$retention_date"* ]] || fail "retain until date should be $retention_date, is $retain_until_date"
|
||||
|
||||
echo "fdkljafajkfs" > "$test_file_folder/$bucket_file"
|
||||
put_object_with_user "s3api" "$test_file_folder/$bucket_file" "$BUCKET_ONE_NAME" "$bucket_file" "$username" "$secret_key" || local put_result=$?
|
||||
[[ $put_result -ne 0 ]] || fail "able to overwrite object with hold"
|
||||
[[ $error == *"Object is WORM protected and cannot be overwritten"* ]] || fail "unexpected error message: $error"
|
||||
|
||||
delete_object_with_user "s3api" "$BUCKET_ONE_NAME" "$bucket_file" "$username" "$secret_key" || local delete_result=$?
|
||||
[[ $delete_result -ne 0 ]] || fail "able to delete object with hold"
|
||||
[[ $error == *"Object is WORM protected and cannot be overwritten"* ]] || fail "unexpected error message: $error"
|
||||
|
||||
delete_object "s3api" "$BUCKET_ONE_NAME" "$bucket_file" || fail "error deleting object"
|
||||
delete_bucket_recursive "s3api" "$BUCKET_ONE_NAME"
|
||||
}
|
||||
#@test "test_get_put_object_retention" {
|
||||
# # bucket must be created with lock for legal hold
|
||||
# if [[ $RECREATE_BUCKETS == false ]]; then
|
||||
# return
|
||||
# fi
|
||||
#
|
||||
# bucket_file="bucket_file"
|
||||
# username="ABCDEFG"
|
||||
# secret_key="HIJKLMN"
|
||||
#
|
||||
# legal_hold_retention_setup "$username" "$secret_key" "$bucket_file"
|
||||
#
|
||||
# get_object_lock_configuration "$BUCKET_ONE_NAME" || fail "error getting lock configuration"
|
||||
# log 5 "$lock_config"
|
||||
# enabled=$(echo "$lock_config" | jq -r ".ObjectLockConfiguration.ObjectLockEnabled")
|
||||
# [[ $enabled == "Enabled" ]] || fail "ObjectLockEnabled should be 'Enabled', is '$enabled'"
|
||||
#
|
||||
# if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# retention_date=$(date -v+2d +"%Y-%m-%dT%H:%M:%S")
|
||||
# else
|
||||
# retention_date=$(date -d "+2 days" +"%Y-%m-%dT%H:%M:%S")
|
||||
# fi
|
||||
# put_object_retention "$BUCKET_ONE_NAME" "$bucket_file" "GOVERNANCE" "$retention_date" || fail "failed to add object retention"
|
||||
# get_object_retention "$BUCKET_ONE_NAME" "$bucket_file" || fail "failed to get object retention"
|
||||
# log 5 "$retention"
|
||||
# retention=$(echo "$retention" | grep -v "InsecureRequestWarning")
|
||||
# mode=$(echo "$retention" | jq -r ".Retention.Mode")
|
||||
# retain_until_date=$(echo "$retention" | jq -r ".Retention.RetainUntilDate")
|
||||
# [[ $mode == "GOVERNANCE" ]] || fail "retention mode should be governance, is $mode"
|
||||
# [[ $retain_until_date == "$retention_date"* ]] || fail "retain until date should be $retention_date, is $retain_until_date"
|
||||
#
|
||||
# echo "fdkljafajkfs" > "$test_file_folder/$bucket_file"
|
||||
# put_object_with_user "s3api" "$test_file_folder/$bucket_file" "$BUCKET_ONE_NAME" "$bucket_file" "$username" "$secret_key" || local put_result=$?
|
||||
# [[ $put_result -ne 0 ]] || fail "able to overwrite object with hold"
|
||||
# [[ $error == *"Object is WORM protected and cannot be overwritten"* ]] || fail "unexpected error message: $error"
|
||||
#
|
||||
# delete_object_with_user "s3api" "$BUCKET_ONE_NAME" "$bucket_file" "$username" "$secret_key" || local delete_result=$?
|
||||
# [[ $delete_result -ne 0 ]] || fail "able to delete object with hold"
|
||||
# [[ $error == *"Object is WORM protected and cannot be overwritten"* ]] || fail "unexpected error message: $error"
|
||||
#
|
||||
# delete_object "s3api" "$BUCKET_ONE_NAME" "$bucket_file" || fail "error deleting object"
|
||||
# delete_bucket_recursive "s3api" "$BUCKET_ONE_NAME"
|
||||
#}
|
||||
|
||||
legal_hold_retention_setup() {
|
||||
if [[ $# -ne 3 ]]; then
|
||||
|
||||
Reference in New Issue
Block a user