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

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)
}
}
}
})
}
}