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.
206 lines
6.4 KiB
206 lines
6.4 KiB
package s3api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"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
|
|
}
|