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.
		
		
		
		
		
			
		
			
				
					
					
						
							401 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							401 lines
						
					
					
						
							12 KiB
						
					
					
				
								package s3api
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"fmt"
							 | 
						|
									"strings"
							 | 
						|
									"testing"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb/s3_pb"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								// TestBucketDefaultSSEKMSEnforcement tests bucket default encryption enforcement
							 | 
						|
								func TestBucketDefaultSSEKMSEnforcement(t *testing.T) {
							 | 
						|
									kmsKey := SetupTestKMS(t)
							 | 
						|
									defer kmsKey.Cleanup()
							 | 
						|
								
							 | 
						|
									// Create bucket encryption configuration
							 | 
						|
									config := &s3_pb.EncryptionConfiguration{
							 | 
						|
										SseAlgorithm:     "aws:kms",
							 | 
						|
										KmsKeyId:         kmsKey.KeyID,
							 | 
						|
										BucketKeyEnabled: false,
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									t.Run("Bucket with SSE-KMS default encryption", func(t *testing.T) {
							 | 
						|
										// Test that default encryption config is properly stored and retrieved
							 | 
						|
										if config.SseAlgorithm != "aws:kms" {
							 | 
						|
											t.Errorf("Expected SSE algorithm aws:kms, got %s", config.SseAlgorithm)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if config.KmsKeyId != kmsKey.KeyID {
							 | 
						|
											t.Errorf("Expected KMS key ID %s, got %s", kmsKey.KeyID, config.KmsKeyId)
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									t.Run("Default encryption headers generation", func(t *testing.T) {
							 | 
						|
										// Test generating default encryption headers for objects
							 | 
						|
										headers := GetDefaultEncryptionHeaders(config)
							 | 
						|
								
							 | 
						|
										if headers == nil {
							 | 
						|
											t.Fatal("Expected default headers, got nil")
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										expectedAlgorithm := headers["X-Amz-Server-Side-Encryption"]
							 | 
						|
										if expectedAlgorithm != "aws:kms" {
							 | 
						|
											t.Errorf("Expected X-Amz-Server-Side-Encryption header aws:kms, got %s", expectedAlgorithm)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										expectedKeyID := headers["X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id"]
							 | 
						|
										if expectedKeyID != kmsKey.KeyID {
							 | 
						|
											t.Errorf("Expected X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id header %s, got %s", kmsKey.KeyID, expectedKeyID)
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									t.Run("Default encryption detection", func(t *testing.T) {
							 | 
						|
										// Test IsDefaultEncryptionEnabled
							 | 
						|
										enabled := IsDefaultEncryptionEnabled(config)
							 | 
						|
										if !enabled {
							 | 
						|
											t.Error("Should detect default encryption as enabled")
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Test with nil config
							 | 
						|
										enabled = IsDefaultEncryptionEnabled(nil)
							 | 
						|
										if enabled {
							 | 
						|
											t.Error("Should detect default encryption as disabled for nil config")
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Test with empty config
							 | 
						|
										emptyConfig := &s3_pb.EncryptionConfiguration{}
							 | 
						|
										enabled = IsDefaultEncryptionEnabled(emptyConfig)
							 | 
						|
										if enabled {
							 | 
						|
											t.Error("Should detect default encryption as disabled for empty config")
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// TestBucketEncryptionConfigValidation tests XML validation of bucket encryption configurations
							 | 
						|
								func TestBucketEncryptionConfigValidation(t *testing.T) {
							 | 
						|
									testCases := []struct {
							 | 
						|
										name        string
							 | 
						|
										xml         string
							 | 
						|
										expectError bool
							 | 
						|
										description string
							 | 
						|
									}{
							 | 
						|
										{
							 | 
						|
											name: "Valid SSE-S3 configuration",
							 | 
						|
											xml: `<ServerSideEncryptionConfiguration>
							 | 
						|
												<Rule>
							 | 
						|
													<ApplyServerSideEncryptionByDefault>
							 | 
						|
														<SSEAlgorithm>AES256</SSEAlgorithm>
							 | 
						|
													</ApplyServerSideEncryptionByDefault>
							 | 
						|
												</Rule>
							 | 
						|
											</ServerSideEncryptionConfiguration>`,
							 | 
						|
											expectError: false,
							 | 
						|
											description: "Basic SSE-S3 configuration should be valid",
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name: "Valid SSE-KMS configuration",
							 | 
						|
											xml: `<ServerSideEncryptionConfiguration>
							 | 
						|
												<Rule>
							 | 
						|
													<ApplyServerSideEncryptionByDefault>
							 | 
						|
														<SSEAlgorithm>aws:kms</SSEAlgorithm>
							 | 
						|
														<KMSMasterKeyID>test-key-id</KMSMasterKeyID>
							 | 
						|
													</ApplyServerSideEncryptionByDefault>
							 | 
						|
												</Rule>
							 | 
						|
											</ServerSideEncryptionConfiguration>`,
							 | 
						|
											expectError: false,
							 | 
						|
											description: "SSE-KMS configuration with key ID should be valid",
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name: "Valid SSE-KMS without key ID",
							 | 
						|
											xml: `<ServerSideEncryptionConfiguration>
							 | 
						|
												<Rule>
							 | 
						|
													<ApplyServerSideEncryptionByDefault>
							 | 
						|
														<SSEAlgorithm>aws:kms</SSEAlgorithm>
							 | 
						|
													</ApplyServerSideEncryptionByDefault>
							 | 
						|
												</Rule>
							 | 
						|
											</ServerSideEncryptionConfiguration>`,
							 | 
						|
											expectError: false,
							 | 
						|
											description: "SSE-KMS without key ID should use default key",
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name: "Invalid XML structure",
							 | 
						|
											xml: `<ServerSideEncryptionConfiguration>
							 | 
						|
												<InvalidRule>
							 | 
						|
													<SSEAlgorithm>AES256</SSEAlgorithm>
							 | 
						|
												</InvalidRule>
							 | 
						|
											</ServerSideEncryptionConfiguration>`,
							 | 
						|
											expectError: true,
							 | 
						|
											description: "Invalid XML structure should be rejected",
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name: "Empty configuration",
							 | 
						|
											xml: `<ServerSideEncryptionConfiguration>
							 | 
						|
											</ServerSideEncryptionConfiguration>`,
							 | 
						|
											expectError: true,
							 | 
						|
											description: "Empty configuration should be rejected",
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name: "Invalid algorithm",
							 | 
						|
											xml: `<ServerSideEncryptionConfiguration>
							 | 
						|
												<Rule>
							 | 
						|
													<ApplyServerSideEncryptionByDefault>
							 | 
						|
														<SSEAlgorithm>INVALID</SSEAlgorithm>
							 | 
						|
													</ApplyServerSideEncryptionByDefault>
							 | 
						|
												</Rule>
							 | 
						|
											</ServerSideEncryptionConfiguration>`,
							 | 
						|
											expectError: true,
							 | 
						|
											description: "Invalid algorithm should be rejected",
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									for _, tc := range testCases {
							 | 
						|
										t.Run(tc.name, func(t *testing.T) {
							 | 
						|
											config, err := encryptionConfigFromXMLBytes([]byte(tc.xml))
							 | 
						|
								
							 | 
						|
											if tc.expectError && err == nil {
							 | 
						|
												t.Errorf("Expected error for %s, but got none. %s", tc.name, tc.description)
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											if !tc.expectError && err != nil {
							 | 
						|
												t.Errorf("Expected no error for %s, but got: %v. %s", tc.name, err, tc.description)
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											if !tc.expectError && config != nil {
							 | 
						|
												// Validate the parsed configuration
							 | 
						|
												t.Logf("Successfully parsed config: Algorithm=%s, KeyID=%s",
							 | 
						|
													config.SseAlgorithm, config.KmsKeyId)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// TestBucketEncryptionAPIOperations tests the bucket encryption API operations
							 | 
						|
								func TestBucketEncryptionAPIOperations(t *testing.T) {
							 | 
						|
									// Note: These tests would normally require a full S3 API server setup
							 | 
						|
									// For now, we test the individual components
							 | 
						|
								
							 | 
						|
									t.Run("PUT bucket encryption", func(t *testing.T) {
							 | 
						|
										xml := `<ServerSideEncryptionConfiguration>
							 | 
						|
											<Rule>
							 | 
						|
												<ApplyServerSideEncryptionByDefault>
							 | 
						|
													<SSEAlgorithm>aws:kms</SSEAlgorithm>
							 | 
						|
													<KMSMasterKeyID>test-key-id</KMSMasterKeyID>
							 | 
						|
												</ApplyServerSideEncryptionByDefault>
							 | 
						|
											</Rule>
							 | 
						|
										</ServerSideEncryptionConfiguration>`
							 | 
						|
								
							 | 
						|
										// Parse the XML to protobuf
							 | 
						|
										config, err := encryptionConfigFromXMLBytes([]byte(xml))
							 | 
						|
										if err != nil {
							 | 
						|
											t.Fatalf("Failed to parse encryption config: %v", err)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Verify the parsed configuration
							 | 
						|
										if config.SseAlgorithm != "aws:kms" {
							 | 
						|
											t.Errorf("Expected algorithm aws:kms, got %s", config.SseAlgorithm)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if config.KmsKeyId != "test-key-id" {
							 | 
						|
											t.Errorf("Expected key ID test-key-id, got %s", config.KmsKeyId)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Convert back to XML
							 | 
						|
										xmlBytes, err := encryptionConfigToXMLBytes(config)
							 | 
						|
										if err != nil {
							 | 
						|
											t.Fatalf("Failed to convert config to XML: %v", err)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Verify round-trip
							 | 
						|
										if len(xmlBytes) == 0 {
							 | 
						|
											t.Error("Generated XML should not be empty")
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Parse again to verify
							 | 
						|
										roundTripConfig, err := encryptionConfigFromXMLBytes(xmlBytes)
							 | 
						|
										if err != nil {
							 | 
						|
											t.Fatalf("Failed to parse round-trip XML: %v", err)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if roundTripConfig.SseAlgorithm != config.SseAlgorithm {
							 | 
						|
											t.Error("Round-trip algorithm doesn't match")
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if roundTripConfig.KmsKeyId != config.KmsKeyId {
							 | 
						|
											t.Error("Round-trip key ID doesn't match")
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									t.Run("GET bucket encryption", func(t *testing.T) {
							 | 
						|
										// Test getting encryption configuration
							 | 
						|
										config := &s3_pb.EncryptionConfiguration{
							 | 
						|
											SseAlgorithm:     "AES256",
							 | 
						|
											KmsKeyId:         "",
							 | 
						|
											BucketKeyEnabled: false,
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Convert to XML for GET response
							 | 
						|
										xmlBytes, err := encryptionConfigToXMLBytes(config)
							 | 
						|
										if err != nil {
							 | 
						|
											t.Fatalf("Failed to convert config to XML: %v", err)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if len(xmlBytes) == 0 {
							 | 
						|
											t.Error("Generated XML should not be empty")
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Verify XML contains expected elements
							 | 
						|
										xmlStr := string(xmlBytes)
							 | 
						|
										if !strings.Contains(xmlStr, "AES256") {
							 | 
						|
											t.Error("XML should contain AES256 algorithm")
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									t.Run("DELETE bucket encryption", func(t *testing.T) {
							 | 
						|
										// Test deleting encryption configuration
							 | 
						|
										// This would typically involve removing the configuration from metadata
							 | 
						|
								
							 | 
						|
										// Simulate checking if encryption is enabled after deletion
							 | 
						|
										enabled := IsDefaultEncryptionEnabled(nil)
							 | 
						|
										if enabled {
							 | 
						|
											t.Error("Encryption should be disabled after deletion")
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// TestBucketEncryptionEdgeCases tests edge cases in bucket encryption
							 | 
						|
								func TestBucketEncryptionEdgeCases(t *testing.T) {
							 | 
						|
									t.Run("Large XML configuration", func(t *testing.T) {
							 | 
						|
										// Test with a large but valid XML
							 | 
						|
										largeXML := `<ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
							 | 
						|
											<Rule>
							 | 
						|
												<ApplyServerSideEncryptionByDefault>
							 | 
						|
													<SSEAlgorithm>aws:kms</SSEAlgorithm>
							 | 
						|
													<KMSMasterKeyID>arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012</KMSMasterKeyID>
							 | 
						|
												</ApplyServerSideEncryptionByDefault>
							 | 
						|
												<BucketKeyEnabled>true</BucketKeyEnabled>
							 | 
						|
											</Rule>
							 | 
						|
										</ServerSideEncryptionConfiguration>`
							 | 
						|
								
							 | 
						|
										config, err := encryptionConfigFromXMLBytes([]byte(largeXML))
							 | 
						|
										if err != nil {
							 | 
						|
											t.Fatalf("Failed to parse large XML: %v", err)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if config.SseAlgorithm != "aws:kms" {
							 | 
						|
											t.Error("Should parse large XML correctly")
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									t.Run("XML with namespaces", func(t *testing.T) {
							 | 
						|
										// Test XML with namespaces
							 | 
						|
										namespacedXML := `<ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
							 | 
						|
											<Rule>
							 | 
						|
												<ApplyServerSideEncryptionByDefault>
							 | 
						|
													<SSEAlgorithm>AES256</SSEAlgorithm>
							 | 
						|
												</ApplyServerSideEncryptionByDefault>
							 | 
						|
											</Rule>
							 | 
						|
										</ServerSideEncryptionConfiguration>`
							 | 
						|
								
							 | 
						|
										config, err := encryptionConfigFromXMLBytes([]byte(namespacedXML))
							 | 
						|
										if err != nil {
							 | 
						|
											t.Fatalf("Failed to parse namespaced XML: %v", err)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if config.SseAlgorithm != "AES256" {
							 | 
						|
											t.Error("Should parse namespaced XML correctly")
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									t.Run("Malformed XML", func(t *testing.T) {
							 | 
						|
										malformedXMLs := []string{
							 | 
						|
											`<ServerSideEncryptionConfiguration><Rule><SSEAlgorithm>AES256</Rule>`,                 // Unclosed tags
							 | 
						|
											`<ServerSideEncryptionConfiguration><Rule></Rule></ServerSideEncryptionConfiguration>`, // Empty rule
							 | 
						|
											`not-xml-at-all`, // Not XML
							 | 
						|
											`<ServerSideEncryptionConfiguration xmlns="invalid-namespace"><Rule><ApplyServerSideEncryptionByDefault><SSEAlgorithm>AES256</SSEAlgorithm></ApplyServerSideEncryptionByDefault></Rule></ServerSideEncryptionConfiguration>`, // Invalid namespace
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										for i, malformedXML := range malformedXMLs {
							 | 
						|
											t.Run(fmt.Sprintf("Malformed XML %d", i), func(t *testing.T) {
							 | 
						|
												_, err := encryptionConfigFromXMLBytes([]byte(malformedXML))
							 | 
						|
												if err == nil {
							 | 
						|
													t.Errorf("Expected error for malformed XML %d, but got none", i)
							 | 
						|
												}
							 | 
						|
											})
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// TestGetDefaultEncryptionHeaders tests generation of default encryption headers
							 | 
						|
								func TestGetDefaultEncryptionHeaders(t *testing.T) {
							 | 
						|
									testCases := []struct {
							 | 
						|
										name            string
							 | 
						|
										config          *s3_pb.EncryptionConfiguration
							 | 
						|
										expectedHeaders map[string]string
							 | 
						|
									}{
							 | 
						|
										{
							 | 
						|
											name:            "Nil configuration",
							 | 
						|
											config:          nil,
							 | 
						|
											expectedHeaders: nil,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name: "SSE-S3 configuration",
							 | 
						|
											config: &s3_pb.EncryptionConfiguration{
							 | 
						|
												SseAlgorithm: "AES256",
							 | 
						|
											},
							 | 
						|
											expectedHeaders: map[string]string{
							 | 
						|
												"X-Amz-Server-Side-Encryption": "AES256",
							 | 
						|
											},
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name: "SSE-KMS configuration with key",
							 | 
						|
											config: &s3_pb.EncryptionConfiguration{
							 | 
						|
												SseAlgorithm: "aws:kms",
							 | 
						|
												KmsKeyId:     "test-key-id",
							 | 
						|
											},
							 | 
						|
											expectedHeaders: map[string]string{
							 | 
						|
												"X-Amz-Server-Side-Encryption":                "aws:kms",
							 | 
						|
												"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": "test-key-id",
							 | 
						|
											},
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name: "SSE-KMS configuration without key",
							 | 
						|
											config: &s3_pb.EncryptionConfiguration{
							 | 
						|
												SseAlgorithm: "aws:kms",
							 | 
						|
											},
							 | 
						|
											expectedHeaders: map[string]string{
							 | 
						|
												"X-Amz-Server-Side-Encryption": "aws:kms",
							 | 
						|
											},
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									for _, tc := range testCases {
							 | 
						|
										t.Run(tc.name, func(t *testing.T) {
							 | 
						|
											headers := GetDefaultEncryptionHeaders(tc.config)
							 | 
						|
								
							 | 
						|
											if tc.expectedHeaders == nil && headers != nil {
							 | 
						|
												t.Error("Expected nil headers but got some")
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											if tc.expectedHeaders != nil && headers == nil {
							 | 
						|
												t.Error("Expected headers but got nil")
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											if tc.expectedHeaders != nil && headers != nil {
							 | 
						|
												for key, expectedValue := range tc.expectedHeaders {
							 | 
						|
													if actualValue, exists := headers[key]; !exists {
							 | 
						|
														t.Errorf("Expected header %s not found", key)
							 | 
						|
													} else if actualValue != expectedValue {
							 | 
						|
														t.Errorf("Header %s: expected %s, got %s", key, expectedValue, actualValue)
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
								
							 | 
						|
												// Check for unexpected headers
							 | 
						|
												for key := range headers {
							 | 
						|
													if _, expected := tc.expectedHeaders[key]; !expected {
							 | 
						|
														t.Errorf("Unexpected header found: %s", key)
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									}
							 | 
						|
								}
							 |