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