| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- package s3api
- import (
- "encoding/json"
- "encoding/xml"
- "net/http/httptest"
- "testing"
- "time"
- "github.com/aws/aws-sdk-go/service/s3"
- "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
- "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
- "github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- )
- func TestPutBucketAclCannedAclSupport(t *testing.T) {
- // Test that the ExtractAcl function can handle various canned ACLs
- // This tests the core functionality without requiring a fully initialized S3ApiServer
- testCases := []struct {
- name string
- cannedAcl string
- shouldWork bool
- description string
- }{
- {
- name: "private",
- cannedAcl: s3_constants.CannedAclPrivate,
- shouldWork: true,
- description: "private ACL should be accepted",
- },
- {
- name: "public-read",
- cannedAcl: s3_constants.CannedAclPublicRead,
- shouldWork: true,
- description: "public-read ACL should be accepted",
- },
- {
- name: "public-read-write",
- cannedAcl: s3_constants.CannedAclPublicReadWrite,
- shouldWork: true,
- description: "public-read-write ACL should be accepted",
- },
- {
- name: "authenticated-read",
- cannedAcl: s3_constants.CannedAclAuthenticatedRead,
- shouldWork: true,
- description: "authenticated-read ACL should be accepted",
- },
- {
- name: "bucket-owner-read",
- cannedAcl: s3_constants.CannedAclBucketOwnerRead,
- shouldWork: true,
- description: "bucket-owner-read ACL should be accepted",
- },
- {
- name: "bucket-owner-full-control",
- cannedAcl: s3_constants.CannedAclBucketOwnerFullControl,
- shouldWork: true,
- description: "bucket-owner-full-control ACL should be accepted",
- },
- {
- name: "invalid-acl",
- cannedAcl: "invalid-acl-value",
- shouldWork: false,
- description: "invalid ACL should be rejected",
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- // Create a request with the specified canned ACL
- req := httptest.NewRequest("PUT", "/bucket?acl", nil)
- req.Header.Set(s3_constants.AmzCannedAcl, tc.cannedAcl)
- req.Header.Set(s3_constants.AmzAccountId, "test-account-123")
- // Create a mock IAM for testing
- mockIam := &mockIamInterface{}
- // Test the ACL extraction directly
- grants, errCode := ExtractAcl(req, mockIam, "", "test-account-123", "test-account-123", "test-account-123")
- if tc.shouldWork {
- assert.Equal(t, s3err.ErrNone, errCode, "Expected ACL parsing to succeed for %s", tc.cannedAcl)
- assert.NotEmpty(t, grants, "Expected grants to be generated for valid ACL %s", tc.cannedAcl)
- t.Logf("✓ PASS: %s - %s", tc.name, tc.description)
- } else {
- assert.NotEqual(t, s3err.ErrNone, errCode, "Expected ACL parsing to fail for invalid ACL %s", tc.cannedAcl)
- t.Logf("✓ PASS: %s - %s", tc.name, tc.description)
- }
- })
- }
- }
- // TestBucketWithoutACLIsNotPublicRead tests that buckets without ACLs are not public-read
- func TestBucketWithoutACLIsNotPublicRead(t *testing.T) {
- // Create a bucket config without ACL (like a freshly created bucket)
- config := &BucketConfig{
- Name: "test-bucket",
- IsPublicRead: false, // Should be explicitly false
- }
- // Verify that buckets without ACL are not public-read
- assert.False(t, config.IsPublicRead, "Bucket without ACL should not be public-read")
- }
- func TestBucketConfigInitialization(t *testing.T) {
- // Test that BucketConfig properly initializes IsPublicRead field
- config := &BucketConfig{
- Name: "test-bucket",
- IsPublicRead: false, // Explicitly set to false for private buckets
- }
- // Verify proper initialization
- assert.False(t, config.IsPublicRead, "Newly created bucket should not be public-read by default")
- }
- // TestUpdateBucketConfigCacheConsistency tests that updateBucketConfigCacheFromEntry
- // properly handles the IsPublicRead flag consistently with getBucketConfig
- func TestUpdateBucketConfigCacheConsistency(t *testing.T) {
- t.Run("bucket without ACL should have IsPublicRead=false", func(t *testing.T) {
- // Simulate an entry without ACL (like a freshly created bucket)
- entry := &filer_pb.Entry{
- Name: "test-bucket",
- Attributes: &filer_pb.FuseAttributes{
- FileMode: 0755,
- },
- // Extended is nil or doesn't contain ACL
- }
- // Test what updateBucketConfigCacheFromEntry would create
- config := &BucketConfig{
- Name: entry.Name,
- Entry: entry,
- IsPublicRead: false, // Should be explicitly false
- }
- // When Extended is nil, IsPublicRead should be false
- assert.False(t, config.IsPublicRead, "Bucket without Extended metadata should not be public-read")
- // When Extended exists but has no ACL key, IsPublicRead should also be false
- entry.Extended = make(map[string][]byte)
- entry.Extended["some-other-key"] = []byte("some-value")
- config = &BucketConfig{
- Name: entry.Name,
- Entry: entry,
- IsPublicRead: false, // Should be explicitly false
- }
- // Simulate the else branch: no ACL means private bucket
- if _, exists := entry.Extended[s3_constants.ExtAmzAclKey]; !exists {
- config.IsPublicRead = false
- }
- assert.False(t, config.IsPublicRead, "Bucket with Extended but no ACL should not be public-read")
- })
- t.Run("bucket with public-read ACL should have IsPublicRead=true", func(t *testing.T) {
- // Create a mock public-read ACL using AWS S3 SDK types
- publicReadGrants := []*s3.Grant{
- {
- Grantee: &s3.Grantee{
- Type: &s3_constants.GrantTypeGroup,
- URI: &s3_constants.GranteeGroupAllUsers,
- },
- Permission: &s3_constants.PermissionRead,
- },
- }
- aclBytes, err := json.Marshal(publicReadGrants)
- require.NoError(t, err)
- entry := &filer_pb.Entry{
- Name: "public-bucket",
- Extended: map[string][]byte{
- s3_constants.ExtAmzAclKey: aclBytes,
- },
- }
- config := &BucketConfig{
- Name: entry.Name,
- Entry: entry,
- IsPublicRead: false, // Start with false
- }
- // Simulate what updateBucketConfigCacheFromEntry would do
- if acl, exists := entry.Extended[s3_constants.ExtAmzAclKey]; exists {
- config.ACL = acl
- config.IsPublicRead = parseAndCachePublicReadStatus(acl)
- }
- assert.True(t, config.IsPublicRead, "Bucket with public-read ACL should be public-read")
- })
- }
- // mockIamInterface is a simple mock for testing
- type mockIamInterface struct{}
- func (m *mockIamInterface) GetAccountNameById(canonicalId string) string {
- return "test-user-" + canonicalId
- }
- func (m *mockIamInterface) GetAccountIdByEmail(email string) string {
- return "account-for-" + email
- }
- // TestListAllMyBucketsResultNamespace verifies that the ListAllMyBucketsResult
- // XML response includes the proper S3 namespace URI
- func TestListAllMyBucketsResultNamespace(t *testing.T) {
- // Create a sample ListAllMyBucketsResult response
- response := ListAllMyBucketsResult{
- Owner: CanonicalUser{
- ID: "test-owner-id",
- DisplayName: "test-owner",
- },
- Buckets: ListAllMyBucketsList{
- Bucket: []ListAllMyBucketsEntry{
- {
- Name: "test-bucket",
- CreationDate: time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC),
- },
- },
- },
- }
- // Marshal the response to XML
- xmlData, err := xml.Marshal(response)
- require.NoError(t, err, "Failed to marshal XML response")
- xmlString := string(xmlData)
- // Verify that the XML contains the proper namespace
- assert.Contains(t, xmlString, `xmlns="http://s3.amazonaws.com/doc/2006-03-01/"`,
- "XML response should contain the S3 namespace URI")
- // Verify the root element has the correct name
- assert.Contains(t, xmlString, "<ListAllMyBucketsResult",
- "XML response should have ListAllMyBucketsResult root element")
- // Verify structure contains expected elements
- assert.Contains(t, xmlString, "<Owner>", "XML should contain Owner element")
- assert.Contains(t, xmlString, "<Buckets>", "XML should contain Buckets element")
- assert.Contains(t, xmlString, "<Bucket>", "XML should contain Bucket element")
- assert.Contains(t, xmlString, "<Name>test-bucket</Name>", "XML should contain bucket name")
- t.Logf("Generated XML:\n%s", xmlString)
- }
|