You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							504 lines
						
					
					
						
							16 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							504 lines
						
					
					
						
							16 KiB
						
					
					
				| package s3api | |
| 
 | |
| import ( | |
| 	"fmt" | |
| 	"testing" | |
| 
 | |
| 	"github.com/stretchr/testify/assert" | |
| 	"github.com/stretchr/testify/require" | |
| ) | |
| 
 | |
| func TestS3PolicyTemplates(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 
 | |
| 	t.Run("S3ReadOnlyPolicy", func(t *testing.T) { | |
| 		policy := templates.GetS3ReadOnlyPolicy() | |
| 
 | |
| 		require.NotNil(t, policy) | |
| 		assert.Equal(t, "2012-10-17", policy.Version) | |
| 		assert.Len(t, policy.Statement, 1) | |
| 
 | |
| 		stmt := policy.Statement[0] | |
| 		assert.Equal(t, "Allow", stmt.Effect) | |
| 		assert.Equal(t, "S3ReadOnlyAccess", stmt.Sid) | |
| 		assert.Contains(t, stmt.Action, "s3:GetObject") | |
| 		assert.Contains(t, stmt.Action, "s3:ListBucket") | |
| 		assert.NotContains(t, stmt.Action, "s3:PutObject") | |
| 		assert.NotContains(t, stmt.Action, "s3:DeleteObject") | |
| 
 | |
| 		assert.Contains(t, stmt.Resource, "arn:seaweed:s3:::*") | |
| 		assert.Contains(t, stmt.Resource, "arn:seaweed:s3:::*/*") | |
| 	}) | |
| 
 | |
| 	t.Run("S3WriteOnlyPolicy", func(t *testing.T) { | |
| 		policy := templates.GetS3WriteOnlyPolicy() | |
| 
 | |
| 		require.NotNil(t, policy) | |
| 		assert.Equal(t, "2012-10-17", policy.Version) | |
| 		assert.Len(t, policy.Statement, 1) | |
| 
 | |
| 		stmt := policy.Statement[0] | |
| 		assert.Equal(t, "Allow", stmt.Effect) | |
| 		assert.Equal(t, "S3WriteOnlyAccess", stmt.Sid) | |
| 		assert.Contains(t, stmt.Action, "s3:PutObject") | |
| 		assert.Contains(t, stmt.Action, "s3:CreateMultipartUpload") | |
| 		assert.NotContains(t, stmt.Action, "s3:GetObject") | |
| 		assert.NotContains(t, stmt.Action, "s3:DeleteObject") | |
| 
 | |
| 		assert.Contains(t, stmt.Resource, "arn:seaweed:s3:::*") | |
| 		assert.Contains(t, stmt.Resource, "arn:seaweed:s3:::*/*") | |
| 	}) | |
| 
 | |
| 	t.Run("S3AdminPolicy", func(t *testing.T) { | |
| 		policy := templates.GetS3AdminPolicy() | |
| 
 | |
| 		require.NotNil(t, policy) | |
| 		assert.Equal(t, "2012-10-17", policy.Version) | |
| 		assert.Len(t, policy.Statement, 1) | |
| 
 | |
| 		stmt := policy.Statement[0] | |
| 		assert.Equal(t, "Allow", stmt.Effect) | |
| 		assert.Equal(t, "S3FullAccess", stmt.Sid) | |
| 		assert.Contains(t, stmt.Action, "s3:*") | |
| 
 | |
| 		assert.Contains(t, stmt.Resource, "arn:seaweed:s3:::*") | |
| 		assert.Contains(t, stmt.Resource, "arn:seaweed:s3:::*/*") | |
| 	}) | |
| } | |
| 
 | |
| func TestBucketSpecificPolicies(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 	bucketName := "test-bucket" | |
| 
 | |
| 	t.Run("BucketSpecificReadPolicy", func(t *testing.T) { | |
| 		policy := templates.GetBucketSpecificReadPolicy(bucketName) | |
| 
 | |
| 		require.NotNil(t, policy) | |
| 		assert.Equal(t, "2012-10-17", policy.Version) | |
| 		assert.Len(t, policy.Statement, 1) | |
| 
 | |
| 		stmt := policy.Statement[0] | |
| 		assert.Equal(t, "Allow", stmt.Effect) | |
| 		assert.Equal(t, "BucketSpecificReadAccess", stmt.Sid) | |
| 		assert.Contains(t, stmt.Action, "s3:GetObject") | |
| 		assert.Contains(t, stmt.Action, "s3:ListBucket") | |
| 		assert.NotContains(t, stmt.Action, "s3:PutObject") | |
| 
 | |
| 		expectedBucketArn := "arn:seaweed:s3:::" + bucketName | |
| 		expectedObjectArn := "arn:seaweed:s3:::" + bucketName + "/*" | |
| 		assert.Contains(t, stmt.Resource, expectedBucketArn) | |
| 		assert.Contains(t, stmt.Resource, expectedObjectArn) | |
| 	}) | |
| 
 | |
| 	t.Run("BucketSpecificWritePolicy", func(t *testing.T) { | |
| 		policy := templates.GetBucketSpecificWritePolicy(bucketName) | |
| 
 | |
| 		require.NotNil(t, policy) | |
| 		assert.Equal(t, "2012-10-17", policy.Version) | |
| 		assert.Len(t, policy.Statement, 1) | |
| 
 | |
| 		stmt := policy.Statement[0] | |
| 		assert.Equal(t, "Allow", stmt.Effect) | |
| 		assert.Equal(t, "BucketSpecificWriteAccess", stmt.Sid) | |
| 		assert.Contains(t, stmt.Action, "s3:PutObject") | |
| 		assert.Contains(t, stmt.Action, "s3:CreateMultipartUpload") | |
| 		assert.NotContains(t, stmt.Action, "s3:GetObject") | |
| 
 | |
| 		expectedBucketArn := "arn:seaweed:s3:::" + bucketName | |
| 		expectedObjectArn := "arn:seaweed:s3:::" + bucketName + "/*" | |
| 		assert.Contains(t, stmt.Resource, expectedBucketArn) | |
| 		assert.Contains(t, stmt.Resource, expectedObjectArn) | |
| 	}) | |
| } | |
| 
 | |
| func TestPathBasedAccessPolicy(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 	bucketName := "shared-bucket" | |
| 	pathPrefix := "user123/documents" | |
| 
 | |
| 	policy := templates.GetPathBasedAccessPolicy(bucketName, pathPrefix) | |
| 
 | |
| 	require.NotNil(t, policy) | |
| 	assert.Equal(t, "2012-10-17", policy.Version) | |
| 	assert.Len(t, policy.Statement, 2) | |
| 
 | |
| 	// First statement: List bucket with prefix condition | |
| 	listStmt := policy.Statement[0] | |
| 	assert.Equal(t, "Allow", listStmt.Effect) | |
| 	assert.Equal(t, "ListBucketPermission", listStmt.Sid) | |
| 	assert.Contains(t, listStmt.Action, "s3:ListBucket") | |
| 	assert.Contains(t, listStmt.Resource, "arn:seaweed:s3:::"+bucketName) | |
| 	assert.NotNil(t, listStmt.Condition) | |
| 
 | |
| 	// Second statement: Object operations on path | |
| 	objectStmt := policy.Statement[1] | |
| 	assert.Equal(t, "Allow", objectStmt.Effect) | |
| 	assert.Equal(t, "PathBasedObjectAccess", objectStmt.Sid) | |
| 	assert.Contains(t, objectStmt.Action, "s3:GetObject") | |
| 	assert.Contains(t, objectStmt.Action, "s3:PutObject") | |
| 	assert.Contains(t, objectStmt.Action, "s3:DeleteObject") | |
| 
 | |
| 	expectedObjectArn := "arn:seaweed:s3:::" + bucketName + "/" + pathPrefix + "/*" | |
| 	assert.Contains(t, objectStmt.Resource, expectedObjectArn) | |
| } | |
| 
 | |
| func TestIPRestrictedPolicy(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 	allowedCIDRs := []string{"192.168.1.0/24", "10.0.0.0/8"} | |
| 
 | |
| 	policy := templates.GetIPRestrictedPolicy(allowedCIDRs) | |
| 
 | |
| 	require.NotNil(t, policy) | |
| 	assert.Equal(t, "2012-10-17", policy.Version) | |
| 	assert.Len(t, policy.Statement, 1) | |
| 
 | |
| 	stmt := policy.Statement[0] | |
| 	assert.Equal(t, "Allow", stmt.Effect) | |
| 	assert.Equal(t, "IPRestrictedS3Access", stmt.Sid) | |
| 	assert.Contains(t, stmt.Action, "s3:*") | |
| 	assert.NotNil(t, stmt.Condition) | |
| 
 | |
| 	// Check IP condition structure | |
| 	condition := stmt.Condition | |
| 	ipAddress, exists := condition["IpAddress"] | |
| 	assert.True(t, exists) | |
| 
 | |
| 	sourceIp, exists := ipAddress["aws:SourceIp"] | |
| 	assert.True(t, exists) | |
| 	assert.Equal(t, allowedCIDRs, sourceIp) | |
| } | |
| 
 | |
| func TestTimeBasedAccessPolicy(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 	startHour := 9 // 9 AM | |
| 	endHour := 17  // 5 PM | |
|  | |
| 	policy := templates.GetTimeBasedAccessPolicy(startHour, endHour) | |
| 
 | |
| 	require.NotNil(t, policy) | |
| 	assert.Equal(t, "2012-10-17", policy.Version) | |
| 	assert.Len(t, policy.Statement, 1) | |
| 
 | |
| 	stmt := policy.Statement[0] | |
| 	assert.Equal(t, "Allow", stmt.Effect) | |
| 	assert.Equal(t, "TimeBasedS3Access", stmt.Sid) | |
| 	assert.Contains(t, stmt.Action, "s3:GetObject") | |
| 	assert.Contains(t, stmt.Action, "s3:PutObject") | |
| 	assert.Contains(t, stmt.Action, "s3:ListBucket") | |
| 	assert.NotNil(t, stmt.Condition) | |
| 
 | |
| 	// Check time condition structure | |
| 	condition := stmt.Condition | |
| 	_, hasGreater := condition["DateGreaterThan"] | |
| 	_, hasLess := condition["DateLessThan"] | |
| 	assert.True(t, hasGreater) | |
| 	assert.True(t, hasLess) | |
| } | |
| 
 | |
| func TestMultipartUploadPolicyTemplate(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 	bucketName := "large-files" | |
| 
 | |
| 	policy := templates.GetMultipartUploadPolicy(bucketName) | |
| 
 | |
| 	require.NotNil(t, policy) | |
| 	assert.Equal(t, "2012-10-17", policy.Version) | |
| 	assert.Len(t, policy.Statement, 2) | |
| 
 | |
| 	// First statement: Multipart operations | |
| 	multipartStmt := policy.Statement[0] | |
| 	assert.Equal(t, "Allow", multipartStmt.Effect) | |
| 	assert.Equal(t, "MultipartUploadOperations", multipartStmt.Sid) | |
| 	assert.Contains(t, multipartStmt.Action, "s3:CreateMultipartUpload") | |
| 	assert.Contains(t, multipartStmt.Action, "s3:UploadPart") | |
| 	assert.Contains(t, multipartStmt.Action, "s3:CompleteMultipartUpload") | |
| 	assert.Contains(t, multipartStmt.Action, "s3:AbortMultipartUpload") | |
| 	assert.Contains(t, multipartStmt.Action, "s3:ListMultipartUploads") | |
| 	assert.Contains(t, multipartStmt.Action, "s3:ListParts") | |
| 
 | |
| 	expectedObjectArn := "arn:seaweed:s3:::" + bucketName + "/*" | |
| 	assert.Contains(t, multipartStmt.Resource, expectedObjectArn) | |
| 
 | |
| 	// Second statement: List bucket | |
| 	listStmt := policy.Statement[1] | |
| 	assert.Equal(t, "Allow", listStmt.Effect) | |
| 	assert.Equal(t, "ListBucketForMultipart", listStmt.Sid) | |
| 	assert.Contains(t, listStmt.Action, "s3:ListBucket") | |
| 
 | |
| 	expectedBucketArn := "arn:seaweed:s3:::" + bucketName | |
| 	assert.Contains(t, listStmt.Resource, expectedBucketArn) | |
| } | |
| 
 | |
| func TestPresignedURLPolicy(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 	bucketName := "shared-files" | |
| 
 | |
| 	policy := templates.GetPresignedURLPolicy(bucketName) | |
| 
 | |
| 	require.NotNil(t, policy) | |
| 	assert.Equal(t, "2012-10-17", policy.Version) | |
| 	assert.Len(t, policy.Statement, 1) | |
| 
 | |
| 	stmt := policy.Statement[0] | |
| 	assert.Equal(t, "Allow", stmt.Effect) | |
| 	assert.Equal(t, "PresignedURLAccess", stmt.Sid) | |
| 	assert.Contains(t, stmt.Action, "s3:GetObject") | |
| 	assert.Contains(t, stmt.Action, "s3:PutObject") | |
| 	assert.NotNil(t, stmt.Condition) | |
| 
 | |
| 	expectedObjectArn := "arn:seaweed:s3:::" + bucketName + "/*" | |
| 	assert.Contains(t, stmt.Resource, expectedObjectArn) | |
| 
 | |
| 	// Check signature version condition | |
| 	condition := stmt.Condition | |
| 	stringEquals, exists := condition["StringEquals"] | |
| 	assert.True(t, exists) | |
| 
 | |
| 	signatureVersion, exists := stringEquals["s3:x-amz-signature-version"] | |
| 	assert.True(t, exists) | |
| 	assert.Equal(t, "AWS4-HMAC-SHA256", signatureVersion) | |
| } | |
| 
 | |
| func TestTemporaryAccessPolicy(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 	bucketName := "temp-bucket" | |
| 	expirationHours := 24 | |
| 
 | |
| 	policy := templates.GetTemporaryAccessPolicy(bucketName, expirationHours) | |
| 
 | |
| 	require.NotNil(t, policy) | |
| 	assert.Equal(t, "2012-10-17", policy.Version) | |
| 	assert.Len(t, policy.Statement, 1) | |
| 
 | |
| 	stmt := policy.Statement[0] | |
| 	assert.Equal(t, "Allow", stmt.Effect) | |
| 	assert.Equal(t, "TemporaryS3Access", stmt.Sid) | |
| 	assert.Contains(t, stmt.Action, "s3:GetObject") | |
| 	assert.Contains(t, stmt.Action, "s3:PutObject") | |
| 	assert.Contains(t, stmt.Action, "s3:ListBucket") | |
| 	assert.NotNil(t, stmt.Condition) | |
| 
 | |
| 	// Check expiration condition | |
| 	condition := stmt.Condition | |
| 	dateLessThan, exists := condition["DateLessThan"] | |
| 	assert.True(t, exists) | |
| 
 | |
| 	currentTime, exists := dateLessThan["aws:CurrentTime"] | |
| 	assert.True(t, exists) | |
| 	assert.IsType(t, "", currentTime) // Should be a string timestamp | |
| } | |
| 
 | |
| func TestContentTypeRestrictedPolicy(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 	bucketName := "media-bucket" | |
| 	allowedTypes := []string{"image/jpeg", "image/png", "video/mp4"} | |
| 
 | |
| 	policy := templates.GetContentTypeRestrictedPolicy(bucketName, allowedTypes) | |
| 
 | |
| 	require.NotNil(t, policy) | |
| 	assert.Equal(t, "2012-10-17", policy.Version) | |
| 	assert.Len(t, policy.Statement, 2) | |
| 
 | |
| 	// First statement: Upload with content type restriction | |
| 	uploadStmt := policy.Statement[0] | |
| 	assert.Equal(t, "Allow", uploadStmt.Effect) | |
| 	assert.Equal(t, "ContentTypeRestrictedUpload", uploadStmt.Sid) | |
| 	assert.Contains(t, uploadStmt.Action, "s3:PutObject") | |
| 	assert.Contains(t, uploadStmt.Action, "s3:CreateMultipartUpload") | |
| 	assert.NotNil(t, uploadStmt.Condition) | |
| 
 | |
| 	// Check content type condition | |
| 	condition := uploadStmt.Condition | |
| 	stringEquals, exists := condition["StringEquals"] | |
| 	assert.True(t, exists) | |
| 
 | |
| 	contentType, exists := stringEquals["s3:content-type"] | |
| 	assert.True(t, exists) | |
| 	assert.Equal(t, allowedTypes, contentType) | |
| 
 | |
| 	// Second statement: Read access without restrictions | |
| 	readStmt := policy.Statement[1] | |
| 	assert.Equal(t, "Allow", readStmt.Effect) | |
| 	assert.Equal(t, "ReadAccess", readStmt.Sid) | |
| 	assert.Contains(t, readStmt.Action, "s3:GetObject") | |
| 	assert.Contains(t, readStmt.Action, "s3:ListBucket") | |
| 	assert.Nil(t, readStmt.Condition) // No conditions for read access | |
| } | |
| 
 | |
| func TestDenyDeletePolicy(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 
 | |
| 	policy := templates.GetDenyDeletePolicy() | |
| 
 | |
| 	require.NotNil(t, policy) | |
| 	assert.Equal(t, "2012-10-17", policy.Version) | |
| 	assert.Len(t, policy.Statement, 2) | |
| 
 | |
| 	// First statement: Allow everything except delete | |
| 	allowStmt := policy.Statement[0] | |
| 	assert.Equal(t, "Allow", allowStmt.Effect) | |
| 	assert.Equal(t, "AllowAllExceptDelete", allowStmt.Sid) | |
| 	assert.Contains(t, allowStmt.Action, "s3:GetObject") | |
| 	assert.Contains(t, allowStmt.Action, "s3:PutObject") | |
| 	assert.Contains(t, allowStmt.Action, "s3:ListBucket") | |
| 	assert.NotContains(t, allowStmt.Action, "s3:DeleteObject") | |
| 	assert.NotContains(t, allowStmt.Action, "s3:DeleteBucket") | |
| 
 | |
| 	// Second statement: Explicitly deny delete operations | |
| 	denyStmt := policy.Statement[1] | |
| 	assert.Equal(t, "Deny", denyStmt.Effect) | |
| 	assert.Equal(t, "DenyDeleteOperations", denyStmt.Sid) | |
| 	assert.Contains(t, denyStmt.Action, "s3:DeleteObject") | |
| 	assert.Contains(t, denyStmt.Action, "s3:DeleteObjectVersion") | |
| 	assert.Contains(t, denyStmt.Action, "s3:DeleteBucket") | |
| } | |
| 
 | |
| func TestPolicyTemplateMetadata(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 
 | |
| 	t.Run("GetAllPolicyTemplates", func(t *testing.T) { | |
| 		allTemplates := templates.GetAllPolicyTemplates() | |
| 
 | |
| 		assert.Greater(t, len(allTemplates), 10) // Should have many templates | |
|  | |
| 		// Check that each template has required fields | |
| 		for _, template := range allTemplates { | |
| 			assert.NotEmpty(t, template.Name) | |
| 			assert.NotEmpty(t, template.Description) | |
| 			assert.NotEmpty(t, template.Category) | |
| 			assert.NotEmpty(t, template.UseCase) | |
| 			assert.NotNil(t, template.Policy) | |
| 			assert.Equal(t, "2012-10-17", template.Policy.Version) | |
| 		} | |
| 	}) | |
| 
 | |
| 	t.Run("GetPolicyTemplateByName", func(t *testing.T) { | |
| 		// Test existing template | |
| 		template := templates.GetPolicyTemplateByName("S3ReadOnlyAccess") | |
| 		require.NotNil(t, template) | |
| 		assert.Equal(t, "S3ReadOnlyAccess", template.Name) | |
| 		assert.Equal(t, "Basic Access", template.Category) | |
| 
 | |
| 		// Test non-existing template | |
| 		nonExistent := templates.GetPolicyTemplateByName("NonExistentTemplate") | |
| 		assert.Nil(t, nonExistent) | |
| 	}) | |
| 
 | |
| 	t.Run("GetPolicyTemplatesByCategory", func(t *testing.T) { | |
| 		basicAccessTemplates := templates.GetPolicyTemplatesByCategory("Basic Access") | |
| 		assert.GreaterOrEqual(t, len(basicAccessTemplates), 2) | |
| 
 | |
| 		for _, template := range basicAccessTemplates { | |
| 			assert.Equal(t, "Basic Access", template.Category) | |
| 		} | |
| 
 | |
| 		// Test non-existing category | |
| 		emptyCategory := templates.GetPolicyTemplatesByCategory("NonExistentCategory") | |
| 		assert.Empty(t, emptyCategory) | |
| 	}) | |
| 
 | |
| 	t.Run("PolicyTemplateParameters", func(t *testing.T) { | |
| 		allTemplates := templates.GetAllPolicyTemplates() | |
| 
 | |
| 		// Find a template with parameters (like BucketSpecificRead) | |
| 		var templateWithParams *PolicyTemplateDefinition | |
| 		for _, template := range allTemplates { | |
| 			if template.Name == "BucketSpecificRead" { | |
| 				templateWithParams = &template | |
| 				break | |
| 			} | |
| 		} | |
| 
 | |
| 		require.NotNil(t, templateWithParams) | |
| 		assert.Greater(t, len(templateWithParams.Parameters), 0) | |
| 
 | |
| 		param := templateWithParams.Parameters[0] | |
| 		assert.Equal(t, "bucketName", param.Name) | |
| 		assert.Equal(t, "string", param.Type) | |
| 		assert.True(t, param.Required) | |
| 		assert.NotEmpty(t, param.Description) | |
| 		assert.NotEmpty(t, param.Example) | |
| 	}) | |
| } | |
| 
 | |
| func TestFormatHourHelper(t *testing.T) { | |
| 	tests := []struct { | |
| 		hour     int | |
| 		expected string | |
| 	}{ | |
| 		{0, "00"}, | |
| 		{5, "05"}, | |
| 		{9, "09"}, | |
| 		{10, "10"}, | |
| 		{15, "15"}, | |
| 		{23, "23"}, | |
| 	} | |
| 
 | |
| 	for _, tt := range tests { | |
| 		t.Run(fmt.Sprintf("Hour_%d", tt.hour), func(t *testing.T) { | |
| 			result := formatHour(tt.hour) | |
| 			assert.Equal(t, tt.expected, result) | |
| 		}) | |
| 	} | |
| } | |
| 
 | |
| func TestPolicyTemplateCategories(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 	allTemplates := templates.GetAllPolicyTemplates() | |
| 
 | |
| 	// Extract all categories | |
| 	categoryMap := make(map[string]int) | |
| 	for _, template := range allTemplates { | |
| 		categoryMap[template.Category]++ | |
| 	} | |
| 
 | |
| 	// Expected categories | |
| 	expectedCategories := []string{ | |
| 		"Basic Access", | |
| 		"Administrative", | |
| 		"Bucket-Specific", | |
| 		"Path-Restricted", | |
| 		"Security", | |
| 		"Upload-Specific", | |
| 		"Presigned URLs", | |
| 		"Content Control", | |
| 		"Data Protection", | |
| 	} | |
| 
 | |
| 	for _, expectedCategory := range expectedCategories { | |
| 		count, exists := categoryMap[expectedCategory] | |
| 		assert.True(t, exists, "Category %s should exist", expectedCategory) | |
| 		assert.Greater(t, count, 0, "Category %s should have at least one template", expectedCategory) | |
| 	} | |
| } | |
| 
 | |
| func TestPolicyValidation(t *testing.T) { | |
| 	templates := NewS3PolicyTemplates() | |
| 	allTemplates := templates.GetAllPolicyTemplates() | |
| 
 | |
| 	// Test that all policies have valid structure | |
| 	for _, template := range allTemplates { | |
| 		t.Run("Policy_"+template.Name, func(t *testing.T) { | |
| 			policy := template.Policy | |
| 
 | |
| 			// Basic validation | |
| 			assert.Equal(t, "2012-10-17", policy.Version) | |
| 			assert.Greater(t, len(policy.Statement), 0) | |
| 
 | |
| 			// Validate each statement | |
| 			for i, stmt := range policy.Statement { | |
| 				assert.NotEmpty(t, stmt.Effect, "Statement %d should have effect", i) | |
| 				assert.Contains(t, []string{"Allow", "Deny"}, stmt.Effect, "Statement %d effect should be Allow or Deny", i) | |
| 				assert.Greater(t, len(stmt.Action), 0, "Statement %d should have actions", i) | |
| 				assert.Greater(t, len(stmt.Resource), 0, "Statement %d should have resources", i) | |
| 
 | |
| 				// Check resource format | |
| 				for _, resource := range stmt.Resource { | |
| 					if resource != "*" { | |
| 						assert.Contains(t, resource, "arn:seaweed:s3:::", "Resource should be valid SeaweedFS S3 ARN: %s", resource) | |
| 					} | |
| 				} | |
| 			} | |
| 		}) | |
| 	} | |
| }
 |