object_lock_validation_test.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package retention
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "testing"
  7. "time"
  8. "github.com/aws/aws-sdk-go-v2/aws"
  9. "github.com/aws/aws-sdk-go-v2/service/s3"
  10. "github.com/aws/aws-sdk-go-v2/service/s3/types"
  11. "github.com/stretchr/testify/require"
  12. )
  13. // TestObjectLockValidation tests that S3 Object Lock functionality works end-to-end
  14. // This test focuses on the complete Object Lock workflow that S3 clients expect
  15. func TestObjectLockValidation(t *testing.T) {
  16. client := getS3Client(t)
  17. bucketName := fmt.Sprintf("object-lock-test-%d", time.Now().UnixNano())
  18. t.Logf("=== Validating S3 Object Lock Functionality ===")
  19. t.Logf("Bucket: %s", bucketName)
  20. // Step 1: Create bucket with Object Lock header
  21. t.Log("\n1. Creating bucket with x-amz-bucket-object-lock-enabled: true")
  22. _, err := client.CreateBucket(context.TODO(), &s3.CreateBucketInput{
  23. Bucket: aws.String(bucketName),
  24. ObjectLockEnabledForBucket: aws.Bool(true), // This sends x-amz-bucket-object-lock-enabled: true
  25. })
  26. require.NoError(t, err, "Bucket creation should succeed")
  27. defer client.DeleteBucket(context.TODO(), &s3.DeleteBucketInput{Bucket: aws.String(bucketName)})
  28. t.Log(" ✅ Bucket created successfully")
  29. // Step 2: Check if Object Lock is supported (standard S3 client behavior)
  30. t.Log("\n2. Testing Object Lock support detection")
  31. _, err = client.GetObjectLockConfiguration(context.TODO(), &s3.GetObjectLockConfigurationInput{
  32. Bucket: aws.String(bucketName),
  33. })
  34. require.NoError(t, err, "GetObjectLockConfiguration should succeed for Object Lock enabled bucket")
  35. t.Log(" ✅ GetObjectLockConfiguration succeeded - Object Lock is properly enabled")
  36. // Step 3: Verify versioning is enabled (required for Object Lock)
  37. t.Log("\n3. Verifying versioning is automatically enabled")
  38. versioningResp, err := client.GetBucketVersioning(context.TODO(), &s3.GetBucketVersioningInput{
  39. Bucket: aws.String(bucketName),
  40. })
  41. require.NoError(t, err)
  42. require.Equal(t, types.BucketVersioningStatusEnabled, versioningResp.Status, "Versioning should be automatically enabled")
  43. t.Log(" ✅ Versioning automatically enabled")
  44. // Step 4: Test actual Object Lock functionality
  45. t.Log("\n4. Testing Object Lock retention functionality")
  46. // Create an object
  47. key := "protected-object.dat"
  48. content := "Important data that needs immutable protection"
  49. putResp, err := client.PutObject(context.TODO(), &s3.PutObjectInput{
  50. Bucket: aws.String(bucketName),
  51. Key: aws.String(key),
  52. Body: strings.NewReader(content),
  53. })
  54. require.NoError(t, err)
  55. require.NotNil(t, putResp.VersionId, "Object should have a version ID")
  56. t.Log(" ✅ Object created with versioning")
  57. // Apply Object Lock retention
  58. retentionUntil := time.Now().Add(24 * time.Hour)
  59. _, err = client.PutObjectRetention(context.TODO(), &s3.PutObjectRetentionInput{
  60. Bucket: aws.String(bucketName),
  61. Key: aws.String(key),
  62. Retention: &types.ObjectLockRetention{
  63. Mode: types.ObjectLockRetentionModeCompliance,
  64. RetainUntilDate: aws.Time(retentionUntil),
  65. },
  66. })
  67. require.NoError(t, err, "Setting Object Lock retention should succeed")
  68. t.Log(" ✅ Object Lock retention applied successfully")
  69. // Verify retention allows simple DELETE (creates delete marker) but blocks version deletion
  70. // AWS S3 behavior: Simple DELETE (without version ID) is ALWAYS allowed and creates delete marker
  71. _, err = client.DeleteObject(context.TODO(), &s3.DeleteObjectInput{
  72. Bucket: aws.String(bucketName),
  73. Key: aws.String(key),
  74. })
  75. require.NoError(t, err, "Simple DELETE should succeed and create delete marker (AWS S3 behavior)")
  76. t.Log(" ✅ Simple DELETE succeeded (creates delete marker - correct AWS behavior)")
  77. // Now verify that DELETE with version ID is properly blocked by retention
  78. _, err = client.DeleteObject(context.TODO(), &s3.DeleteObjectInput{
  79. Bucket: aws.String(bucketName),
  80. Key: aws.String(key),
  81. VersionId: putResp.VersionId,
  82. })
  83. require.Error(t, err, "DELETE with version ID should be blocked by COMPLIANCE retention")
  84. t.Log(" ✅ Object version is properly protected by retention policy")
  85. // Verify we can read the object version (should still work)
  86. // Note: Need to specify version ID since latest version is now a delete marker
  87. getResp, err := client.GetObject(context.TODO(), &s3.GetObjectInput{
  88. Bucket: aws.String(bucketName),
  89. Key: aws.String(key),
  90. VersionId: putResp.VersionId,
  91. })
  92. require.NoError(t, err, "Reading protected object version should still work")
  93. defer getResp.Body.Close()
  94. t.Log(" ✅ Protected object can still be read")
  95. t.Log("\n🎉 S3 OBJECT LOCK VALIDATION SUCCESSFUL!")
  96. t.Log(" - Bucket creation with Object Lock header works")
  97. t.Log(" - Object Lock support detection works (GetObjectLockConfiguration succeeds)")
  98. t.Log(" - Versioning is automatically enabled")
  99. t.Log(" - Object Lock retention functionality works")
  100. t.Log(" - Objects are properly protected from deletion")
  101. t.Log("")
  102. t.Log("✅ S3 clients will now recognize SeaweedFS as supporting Object Lock!")
  103. }