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.
		
		
		
		
		
			
		
			
				
					
					
						
							228 lines
						
					
					
						
							5.7 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							228 lines
						
					
					
						
							5.7 KiB
						
					
					
				
								package s3api
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"encoding/json"
							 | 
						|
									"testing"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/iam/policy"
							 | 
						|
									"github.com/stretchr/testify/assert"
							 | 
						|
									"github.com/stretchr/testify/require"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								// TestBucketPolicyValidationBasics tests the core validation logic
							 | 
						|
								func TestBucketPolicyValidationBasics(t *testing.T) {
							 | 
						|
									s3Server := &S3ApiServer{}
							 | 
						|
								
							 | 
						|
									tests := []struct {
							 | 
						|
										name          string
							 | 
						|
										policy        *policy.PolicyDocument
							 | 
						|
										bucket        string
							 | 
						|
										expectedValid bool
							 | 
						|
										expectedError string
							 | 
						|
									}{
							 | 
						|
										{
							 | 
						|
											name: "Valid bucket policy",
							 | 
						|
											policy: &policy.PolicyDocument{
							 | 
						|
												Version: "2012-10-17",
							 | 
						|
												Statement: []policy.Statement{
							 | 
						|
													{
							 | 
						|
														Sid:    "TestStatement",
							 | 
						|
														Effect: "Allow",
							 | 
						|
														Principal: map[string]interface{}{
							 | 
						|
															"AWS": "*",
							 | 
						|
														},
							 | 
						|
														Action: []string{"s3:GetObject"},
							 | 
						|
														Resource: []string{
							 | 
						|
															"arn:seaweed:s3:::test-bucket/*",
							 | 
						|
														},
							 | 
						|
													},
							 | 
						|
												},
							 | 
						|
											},
							 | 
						|
											bucket:        "test-bucket",
							 | 
						|
											expectedValid: true,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name: "Policy without Principal (invalid)",
							 | 
						|
											policy: &policy.PolicyDocument{
							 | 
						|
												Version: "2012-10-17",
							 | 
						|
												Statement: []policy.Statement{
							 | 
						|
													{
							 | 
						|
														Effect:   "Allow",
							 | 
						|
														Action:   []string{"s3:GetObject"},
							 | 
						|
														Resource: []string{"arn:seaweed:s3:::test-bucket/*"},
							 | 
						|
														// Principal is missing
							 | 
						|
													},
							 | 
						|
												},
							 | 
						|
											},
							 | 
						|
											bucket:        "test-bucket",
							 | 
						|
											expectedValid: false,
							 | 
						|
											expectedError: "bucket policies must specify a Principal",
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name: "Invalid version",
							 | 
						|
											policy: &policy.PolicyDocument{
							 | 
						|
												Version: "2008-10-17", // Wrong version
							 | 
						|
												Statement: []policy.Statement{
							 | 
						|
													{
							 | 
						|
														Effect: "Allow",
							 | 
						|
														Principal: map[string]interface{}{
							 | 
						|
															"AWS": "*",
							 | 
						|
														},
							 | 
						|
														Action:   []string{"s3:GetObject"},
							 | 
						|
														Resource: []string{"arn:seaweed:s3:::test-bucket/*"},
							 | 
						|
													},
							 | 
						|
												},
							 | 
						|
											},
							 | 
						|
											bucket:        "test-bucket",
							 | 
						|
											expectedValid: false,
							 | 
						|
											expectedError: "unsupported policy version",
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name: "Resource not matching bucket",
							 | 
						|
											policy: &policy.PolicyDocument{
							 | 
						|
												Version: "2012-10-17",
							 | 
						|
												Statement: []policy.Statement{
							 | 
						|
													{
							 | 
						|
														Effect: "Allow",
							 | 
						|
														Principal: map[string]interface{}{
							 | 
						|
															"AWS": "*",
							 | 
						|
														},
							 | 
						|
														Action:   []string{"s3:GetObject"},
							 | 
						|
														Resource: []string{"arn:seaweed:s3:::other-bucket/*"}, // Wrong bucket
							 | 
						|
													},
							 | 
						|
												},
							 | 
						|
											},
							 | 
						|
											bucket:        "test-bucket",
							 | 
						|
											expectedValid: false,
							 | 
						|
											expectedError: "does not match bucket",
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name: "Non-S3 action",
							 | 
						|
											policy: &policy.PolicyDocument{
							 | 
						|
												Version: "2012-10-17",
							 | 
						|
												Statement: []policy.Statement{
							 | 
						|
													{
							 | 
						|
														Effect: "Allow",
							 | 
						|
														Principal: map[string]interface{}{
							 | 
						|
															"AWS": "*",
							 | 
						|
														},
							 | 
						|
														Action:   []string{"iam:GetUser"}, // Non-S3 action
							 | 
						|
														Resource: []string{"arn:seaweed:s3:::test-bucket/*"},
							 | 
						|
													},
							 | 
						|
												},
							 | 
						|
											},
							 | 
						|
											bucket:        "test-bucket",
							 | 
						|
											expectedValid: false,
							 | 
						|
											expectedError: "bucket policies only support S3 actions",
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									for _, tt := range tests {
							 | 
						|
										t.Run(tt.name, func(t *testing.T) {
							 | 
						|
											err := s3Server.validateBucketPolicy(tt.policy, tt.bucket)
							 | 
						|
								
							 | 
						|
											if tt.expectedValid {
							 | 
						|
												assert.NoError(t, err, "Policy should be valid")
							 | 
						|
											} else {
							 | 
						|
												assert.Error(t, err, "Policy should be invalid")
							 | 
						|
												if tt.expectedError != "" {
							 | 
						|
													assert.Contains(t, err.Error(), tt.expectedError, "Error message should contain expected text")
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// TestBucketResourceValidation tests the resource ARN validation
							 | 
						|
								func TestBucketResourceValidation(t *testing.T) {
							 | 
						|
									s3Server := &S3ApiServer{}
							 | 
						|
								
							 | 
						|
									tests := []struct {
							 | 
						|
										name     string
							 | 
						|
										resource string
							 | 
						|
										bucket   string
							 | 
						|
										valid    bool
							 | 
						|
									}{
							 | 
						|
										{
							 | 
						|
											name:     "Exact bucket ARN",
							 | 
						|
											resource: "arn:seaweed:s3:::test-bucket",
							 | 
						|
											bucket:   "test-bucket",
							 | 
						|
											valid:    true,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name:     "Bucket wildcard ARN",
							 | 
						|
											resource: "arn:seaweed:s3:::test-bucket/*",
							 | 
						|
											bucket:   "test-bucket",
							 | 
						|
											valid:    true,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name:     "Specific object ARN",
							 | 
						|
											resource: "arn:seaweed:s3:::test-bucket/path/to/object.txt",
							 | 
						|
											bucket:   "test-bucket",
							 | 
						|
											valid:    true,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name:     "Different bucket ARN",
							 | 
						|
											resource: "arn:seaweed:s3:::other-bucket/*",
							 | 
						|
											bucket:   "test-bucket",
							 | 
						|
											valid:    false,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name:     "Global S3 wildcard",
							 | 
						|
											resource: "arn:seaweed:s3:::*",
							 | 
						|
											bucket:   "test-bucket",
							 | 
						|
											valid:    false,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name:     "Invalid ARN format",
							 | 
						|
											resource: "invalid-arn",
							 | 
						|
											bucket:   "test-bucket",
							 | 
						|
											valid:    false,
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									for _, tt := range tests {
							 | 
						|
										t.Run(tt.name, func(t *testing.T) {
							 | 
						|
											result := s3Server.validateResourceForBucket(tt.resource, tt.bucket)
							 | 
						|
											assert.Equal(t, tt.valid, result, "Resource validation result should match expected")
							 | 
						|
										})
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// TestBucketPolicyJSONSerialization tests policy JSON handling
							 | 
						|
								func TestBucketPolicyJSONSerialization(t *testing.T) {
							 | 
						|
									policy := &policy.PolicyDocument{
							 | 
						|
										Version: "2012-10-17",
							 | 
						|
										Statement: []policy.Statement{
							 | 
						|
											{
							 | 
						|
												Sid:    "PublicReadGetObject",
							 | 
						|
												Effect: "Allow",
							 | 
						|
												Principal: map[string]interface{}{
							 | 
						|
													"AWS": "*",
							 | 
						|
												},
							 | 
						|
												Action: []string{"s3:GetObject"},
							 | 
						|
												Resource: []string{
							 | 
						|
													"arn:seaweed:s3:::public-bucket/*",
							 | 
						|
												},
							 | 
						|
											},
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Test that policy can be marshaled and unmarshaled correctly
							 | 
						|
									jsonData := marshalPolicy(t, policy)
							 | 
						|
									assert.NotEmpty(t, jsonData, "JSON data should not be empty")
							 | 
						|
								
							 | 
						|
									// Verify the JSON contains expected elements
							 | 
						|
									jsonStr := string(jsonData)
							 | 
						|
									assert.Contains(t, jsonStr, "2012-10-17", "JSON should contain version")
							 | 
						|
									assert.Contains(t, jsonStr, "s3:GetObject", "JSON should contain action")
							 | 
						|
									assert.Contains(t, jsonStr, "arn:seaweed:s3:::public-bucket/*", "JSON should contain resource")
							 | 
						|
									assert.Contains(t, jsonStr, "PublicReadGetObject", "JSON should contain statement ID")
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Helper function for marshaling policies
							 | 
						|
								func marshalPolicy(t *testing.T, policyDoc *policy.PolicyDocument) []byte {
							 | 
						|
									data, err := json.Marshal(policyDoc)
							 | 
						|
									require.NoError(t, err)
							 | 
						|
									return data
							 | 
						|
								}
							 |