s3_bucket_policy_simple_test.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. package s3api
  2. import (
  3. "encoding/json"
  4. "testing"
  5. "github.com/seaweedfs/seaweedfs/weed/iam/policy"
  6. "github.com/stretchr/testify/assert"
  7. "github.com/stretchr/testify/require"
  8. )
  9. // TestBucketPolicyValidationBasics tests the core validation logic
  10. func TestBucketPolicyValidationBasics(t *testing.T) {
  11. s3Server := &S3ApiServer{}
  12. tests := []struct {
  13. name string
  14. policy *policy.PolicyDocument
  15. bucket string
  16. expectedValid bool
  17. expectedError string
  18. }{
  19. {
  20. name: "Valid bucket policy",
  21. policy: &policy.PolicyDocument{
  22. Version: "2012-10-17",
  23. Statement: []policy.Statement{
  24. {
  25. Sid: "TestStatement",
  26. Effect: "Allow",
  27. Principal: map[string]interface{}{
  28. "AWS": "*",
  29. },
  30. Action: []string{"s3:GetObject"},
  31. Resource: []string{
  32. "arn:seaweed:s3:::test-bucket/*",
  33. },
  34. },
  35. },
  36. },
  37. bucket: "test-bucket",
  38. expectedValid: true,
  39. },
  40. {
  41. name: "Policy without Principal (invalid)",
  42. policy: &policy.PolicyDocument{
  43. Version: "2012-10-17",
  44. Statement: []policy.Statement{
  45. {
  46. Effect: "Allow",
  47. Action: []string{"s3:GetObject"},
  48. Resource: []string{"arn:seaweed:s3:::test-bucket/*"},
  49. // Principal is missing
  50. },
  51. },
  52. },
  53. bucket: "test-bucket",
  54. expectedValid: false,
  55. expectedError: "bucket policies must specify a Principal",
  56. },
  57. {
  58. name: "Invalid version",
  59. policy: &policy.PolicyDocument{
  60. Version: "2008-10-17", // Wrong version
  61. Statement: []policy.Statement{
  62. {
  63. Effect: "Allow",
  64. Principal: map[string]interface{}{
  65. "AWS": "*",
  66. },
  67. Action: []string{"s3:GetObject"},
  68. Resource: []string{"arn:seaweed:s3:::test-bucket/*"},
  69. },
  70. },
  71. },
  72. bucket: "test-bucket",
  73. expectedValid: false,
  74. expectedError: "unsupported policy version",
  75. },
  76. {
  77. name: "Resource not matching bucket",
  78. policy: &policy.PolicyDocument{
  79. Version: "2012-10-17",
  80. Statement: []policy.Statement{
  81. {
  82. Effect: "Allow",
  83. Principal: map[string]interface{}{
  84. "AWS": "*",
  85. },
  86. Action: []string{"s3:GetObject"},
  87. Resource: []string{"arn:seaweed:s3:::other-bucket/*"}, // Wrong bucket
  88. },
  89. },
  90. },
  91. bucket: "test-bucket",
  92. expectedValid: false,
  93. expectedError: "does not match bucket",
  94. },
  95. {
  96. name: "Non-S3 action",
  97. policy: &policy.PolicyDocument{
  98. Version: "2012-10-17",
  99. Statement: []policy.Statement{
  100. {
  101. Effect: "Allow",
  102. Principal: map[string]interface{}{
  103. "AWS": "*",
  104. },
  105. Action: []string{"iam:GetUser"}, // Non-S3 action
  106. Resource: []string{"arn:seaweed:s3:::test-bucket/*"},
  107. },
  108. },
  109. },
  110. bucket: "test-bucket",
  111. expectedValid: false,
  112. expectedError: "bucket policies only support S3 actions",
  113. },
  114. }
  115. for _, tt := range tests {
  116. t.Run(tt.name, func(t *testing.T) {
  117. err := s3Server.validateBucketPolicy(tt.policy, tt.bucket)
  118. if tt.expectedValid {
  119. assert.NoError(t, err, "Policy should be valid")
  120. } else {
  121. assert.Error(t, err, "Policy should be invalid")
  122. if tt.expectedError != "" {
  123. assert.Contains(t, err.Error(), tt.expectedError, "Error message should contain expected text")
  124. }
  125. }
  126. })
  127. }
  128. }
  129. // TestBucketResourceValidation tests the resource ARN validation
  130. func TestBucketResourceValidation(t *testing.T) {
  131. s3Server := &S3ApiServer{}
  132. tests := []struct {
  133. name string
  134. resource string
  135. bucket string
  136. valid bool
  137. }{
  138. {
  139. name: "Exact bucket ARN",
  140. resource: "arn:seaweed:s3:::test-bucket",
  141. bucket: "test-bucket",
  142. valid: true,
  143. },
  144. {
  145. name: "Bucket wildcard ARN",
  146. resource: "arn:seaweed:s3:::test-bucket/*",
  147. bucket: "test-bucket",
  148. valid: true,
  149. },
  150. {
  151. name: "Specific object ARN",
  152. resource: "arn:seaweed:s3:::test-bucket/path/to/object.txt",
  153. bucket: "test-bucket",
  154. valid: true,
  155. },
  156. {
  157. name: "Different bucket ARN",
  158. resource: "arn:seaweed:s3:::other-bucket/*",
  159. bucket: "test-bucket",
  160. valid: false,
  161. },
  162. {
  163. name: "Global S3 wildcard",
  164. resource: "arn:seaweed:s3:::*",
  165. bucket: "test-bucket",
  166. valid: false,
  167. },
  168. {
  169. name: "Invalid ARN format",
  170. resource: "invalid-arn",
  171. bucket: "test-bucket",
  172. valid: false,
  173. },
  174. }
  175. for _, tt := range tests {
  176. t.Run(tt.name, func(t *testing.T) {
  177. result := s3Server.validateResourceForBucket(tt.resource, tt.bucket)
  178. assert.Equal(t, tt.valid, result, "Resource validation result should match expected")
  179. })
  180. }
  181. }
  182. // TestBucketPolicyJSONSerialization tests policy JSON handling
  183. func TestBucketPolicyJSONSerialization(t *testing.T) {
  184. policy := &policy.PolicyDocument{
  185. Version: "2012-10-17",
  186. Statement: []policy.Statement{
  187. {
  188. Sid: "PublicReadGetObject",
  189. Effect: "Allow",
  190. Principal: map[string]interface{}{
  191. "AWS": "*",
  192. },
  193. Action: []string{"s3:GetObject"},
  194. Resource: []string{
  195. "arn:seaweed:s3:::public-bucket/*",
  196. },
  197. },
  198. },
  199. }
  200. // Test that policy can be marshaled and unmarshaled correctly
  201. jsonData := marshalPolicy(t, policy)
  202. assert.NotEmpty(t, jsonData, "JSON data should not be empty")
  203. // Verify the JSON contains expected elements
  204. jsonStr := string(jsonData)
  205. assert.Contains(t, jsonStr, "2012-10-17", "JSON should contain version")
  206. assert.Contains(t, jsonStr, "s3:GetObject", "JSON should contain action")
  207. assert.Contains(t, jsonStr, "arn:seaweed:s3:::public-bucket/*", "JSON should contain resource")
  208. assert.Contains(t, jsonStr, "PublicReadGetObject", "JSON should contain statement ID")
  209. }
  210. // Helper function for marshaling policies
  211. func marshalPolicy(t *testing.T, policyDoc *policy.PolicyDocument) []byte {
  212. data, err := json.Marshal(policyDoc)
  213. require.NoError(t, err)
  214. return data
  215. }