s3_bucket_object_lock_test.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package retention
  2. import (
  3. "context"
  4. "strings"
  5. "testing"
  6. "time"
  7. "github.com/aws/aws-sdk-go-v2/aws"
  8. "github.com/aws/aws-sdk-go-v2/service/s3"
  9. "github.com/aws/aws-sdk-go-v2/service/s3/types"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/stretchr/testify/require"
  12. )
  13. // TestBucketCreationWithObjectLockEnabled tests creating a bucket with the
  14. // x-amz-bucket-object-lock-enabled header, which is required for S3 Object Lock compatibility
  15. func TestBucketCreationWithObjectLockEnabled(t *testing.T) {
  16. // This test verifies that bucket creation with
  17. // x-amz-bucket-object-lock-enabled header should automatically enable Object Lock
  18. client := getS3Client(t)
  19. bucketName := getNewBucketName()
  20. defer func() {
  21. // Best effort cleanup
  22. deleteBucket(t, client, bucketName)
  23. }()
  24. // Test 1: Create bucket with Object Lock enabled header using custom HTTP client
  25. t.Run("CreateBucketWithObjectLockHeader", func(t *testing.T) {
  26. // Create bucket with x-amz-bucket-object-lock-enabled header
  27. // This simulates what S3 clients do when testing Object Lock support
  28. createResp, err := client.CreateBucket(context.TODO(), &s3.CreateBucketInput{
  29. Bucket: aws.String(bucketName),
  30. ObjectLockEnabledForBucket: aws.Bool(true), // This should set x-amz-bucket-object-lock-enabled header
  31. })
  32. require.NoError(t, err)
  33. require.NotNil(t, createResp)
  34. // Verify bucket was created
  35. _, err = client.HeadBucket(context.TODO(), &s3.HeadBucketInput{
  36. Bucket: aws.String(bucketName),
  37. })
  38. require.NoError(t, err)
  39. })
  40. // Test 2: Verify that Object Lock is automatically enabled for the bucket
  41. t.Run("VerifyObjectLockAutoEnabled", func(t *testing.T) {
  42. // Try to get the Object Lock configuration
  43. // If the header was processed correctly, this should return an enabled configuration
  44. configResp, err := client.GetObjectLockConfiguration(context.TODO(), &s3.GetObjectLockConfigurationInput{
  45. Bucket: aws.String(bucketName),
  46. })
  47. require.NoError(t, err, "GetObjectLockConfiguration should not fail if Object Lock is enabled")
  48. require.NotNil(t, configResp.ObjectLockConfiguration, "ObjectLockConfiguration should not be nil")
  49. assert.Equal(t, types.ObjectLockEnabledEnabled, configResp.ObjectLockConfiguration.ObjectLockEnabled, "Object Lock should be enabled")
  50. })
  51. // Test 3: Verify versioning is automatically enabled (required for Object Lock)
  52. t.Run("VerifyVersioningAutoEnabled", func(t *testing.T) {
  53. // Object Lock requires versioning to be enabled
  54. // When Object Lock is enabled via header, versioning should also be enabled automatically
  55. versioningResp, err := client.GetBucketVersioning(context.TODO(), &s3.GetBucketVersioningInput{
  56. Bucket: aws.String(bucketName),
  57. })
  58. require.NoError(t, err)
  59. // Versioning should be automatically enabled for Object Lock
  60. assert.Equal(t, types.BucketVersioningStatusEnabled, versioningResp.Status, "Versioning should be automatically enabled for Object Lock")
  61. })
  62. }
  63. // TestBucketCreationWithoutObjectLockHeader tests normal bucket creation
  64. // to ensure we don't break existing functionality
  65. func TestBucketCreationWithoutObjectLockHeader(t *testing.T) {
  66. client := getS3Client(t)
  67. bucketName := getNewBucketName()
  68. defer deleteBucket(t, client, bucketName)
  69. // Create bucket without Object Lock header
  70. _, err := client.CreateBucket(context.TODO(), &s3.CreateBucketInput{
  71. Bucket: aws.String(bucketName),
  72. })
  73. require.NoError(t, err)
  74. // Verify bucket was created
  75. _, err = client.HeadBucket(context.TODO(), &s3.HeadBucketInput{
  76. Bucket: aws.String(bucketName),
  77. })
  78. require.NoError(t, err)
  79. // Object Lock should NOT be enabled
  80. _, err = client.GetObjectLockConfiguration(context.TODO(), &s3.GetObjectLockConfigurationInput{
  81. Bucket: aws.String(bucketName),
  82. })
  83. // This should fail since Object Lock is not enabled
  84. require.Error(t, err)
  85. t.Logf("GetObjectLockConfiguration correctly failed for bucket without Object Lock: %v", err)
  86. // Versioning should not be enabled by default
  87. versioningResp, err := client.GetBucketVersioning(context.TODO(), &s3.GetBucketVersioningInput{
  88. Bucket: aws.String(bucketName),
  89. })
  90. require.NoError(t, err)
  91. // Should be either empty/unset or Suspended, but not Enabled
  92. if versioningResp.Status != types.BucketVersioningStatusEnabled {
  93. t.Logf("Versioning correctly not enabled: %v", versioningResp.Status)
  94. } else {
  95. t.Errorf("Versioning should not be enabled for bucket without Object Lock header")
  96. }
  97. }
  98. // TestS3ObjectLockWorkflow tests the complete Object Lock workflow that S3 clients would use
  99. func TestS3ObjectLockWorkflow(t *testing.T) {
  100. client := getS3Client(t)
  101. bucketName := getNewBucketName()
  102. defer deleteBucket(t, client, bucketName)
  103. // Step 1: Client creates bucket with Object Lock enabled
  104. t.Run("ClientCreatesBucket", func(t *testing.T) {
  105. _, err := client.CreateBucket(context.TODO(), &s3.CreateBucketInput{
  106. Bucket: aws.String(bucketName),
  107. ObjectLockEnabledForBucket: aws.Bool(true),
  108. })
  109. require.NoError(t, err)
  110. })
  111. // Step 2: Client checks if Object Lock is supported by getting the configuration
  112. t.Run("ClientChecksObjectLockSupport", func(t *testing.T) {
  113. configResp, err := client.GetObjectLockConfiguration(context.TODO(), &s3.GetObjectLockConfigurationInput{
  114. Bucket: aws.String(bucketName),
  115. })
  116. require.NoError(t, err, "Object Lock configuration check should succeed")
  117. // S3 clients should see Object Lock is enabled
  118. require.NotNil(t, configResp.ObjectLockConfiguration)
  119. assert.Equal(t, types.ObjectLockEnabledEnabled, configResp.ObjectLockConfiguration.ObjectLockEnabled)
  120. t.Log("Object Lock configuration retrieved successfully - S3 clients would see this as supported")
  121. })
  122. // Step 3: Client would then configure retention policies and use Object Lock
  123. t.Run("ClientConfiguresRetention", func(t *testing.T) {
  124. // Verify versioning is automatically enabled (required for Object Lock)
  125. versioningResp, err := client.GetBucketVersioning(context.TODO(), &s3.GetBucketVersioningInput{
  126. Bucket: aws.String(bucketName),
  127. })
  128. require.NoError(t, err)
  129. require.Equal(t, types.BucketVersioningStatusEnabled, versioningResp.Status, "Versioning should be automatically enabled")
  130. // Create an object
  131. key := "protected-backup-object"
  132. content := "Backup data with Object Lock protection"
  133. putResp, err := client.PutObject(context.TODO(), &s3.PutObjectInput{
  134. Bucket: aws.String(bucketName),
  135. Key: aws.String(key),
  136. Body: strings.NewReader(content),
  137. })
  138. require.NoError(t, err)
  139. require.NotNil(t, putResp.VersionId)
  140. // Set Object Lock retention (what backup clients do to protect data)
  141. retentionUntil := time.Now().Add(24 * time.Hour)
  142. _, err = client.PutObjectRetention(context.TODO(), &s3.PutObjectRetentionInput{
  143. Bucket: aws.String(bucketName),
  144. Key: aws.String(key),
  145. Retention: &types.ObjectLockRetention{
  146. Mode: types.ObjectLockRetentionModeCompliance,
  147. RetainUntilDate: aws.Time(retentionUntil),
  148. },
  149. })
  150. require.NoError(t, err)
  151. // Verify object is protected
  152. _, err = client.DeleteObject(context.TODO(), &s3.DeleteObjectInput{
  153. Bucket: aws.String(bucketName),
  154. Key: aws.String(key),
  155. })
  156. require.Error(t, err, "Object should be protected by retention policy")
  157. t.Log("Object Lock retention successfully applied - data is immutable")
  158. })
  159. }