mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-08 15:41:15 +02:00
* s3: resolve a bucket owner recorded as an identity The admin UI and weed shell record a bucket's owner as an identity name in s3-identity-id and never write the account id the S3 API stores alongside it, so such a bucket looked unowned: its ACL owner fell back to the default admin account, and under the default BucketOwnerEnforced ownership every object uploaded to it was stamped with that account instead of the bucket owner. Resolve the identity to its account when no account id is recorded, in the one place both the bucket metadata and the bucket config derive the owner from. * s3: drop the recorded account when the bucket owner is reassigned Changing the owner of a bucket created through the S3 API left its old account id behind, and that outranks the identity when the owner is resolved, so the new owner never took effect for object ownership or the bucket ACL.
87 lines
2.8 KiB
Go
87 lines
2.8 KiB
Go
package s3api
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// ownerIdentityIAM loads an account-less identity and one scoped to an explicit
|
|
// account, the two shapes a bucket owner name can resolve through.
|
|
func ownerIdentityIAM(t *testing.T) *IdentityAccessManagement {
|
|
t.Helper()
|
|
iam := &IdentityAccessManagement{}
|
|
require.NoError(t, iam.loadS3ApiConfiguration(&iam_pb.S3ApiConfiguration{
|
|
Identities: []*iam_pb.Identity{
|
|
{Name: "test", Actions: []string{"Read", "Write"}},
|
|
{
|
|
Name: "scoped",
|
|
Actions: []string{"Read", "Write"},
|
|
Account: &iam_pb.Account{Id: "100000000001", DisplayName: "Scoped"},
|
|
},
|
|
},
|
|
}))
|
|
return iam
|
|
}
|
|
|
|
// A bucket created outside the S3 API — the admin UI or weed shell, which record
|
|
// the owner as an identity and never write the account id the S3 API stores — is
|
|
// owned by that identity, not by the default admin account. Getting this wrong
|
|
// stamps every object uploaded to the bucket with the admin account, since the
|
|
// default BucketOwnerEnforced ownership hands objects to the bucket owner.
|
|
func TestBucketOwnerFromIdentityId(t *testing.T) {
|
|
iam := ownerIdentityIAM(t)
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
identityId string
|
|
expectOwnerId string
|
|
}{
|
|
{"account-less identity", "test", "test"},
|
|
{"identity scoped to an account", "scoped", "100000000001"},
|
|
{"unknown identity", "ghost", AccountAdmin.Id},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
entry := &filer_pb.Entry{
|
|
Name: "bucket",
|
|
Extended: map[string][]byte{s3_constants.AmzIdentityId: []byte(tc.identityId)},
|
|
}
|
|
|
|
metadata := buildBucketMetadata(iam, entry)
|
|
require.NotNil(t, metadata.Owner)
|
|
assert.Equal(t, tc.expectOwnerId, *metadata.Owner.ID)
|
|
|
|
s3a := &S3ApiServer{iam: iam}
|
|
config := s3a.newBucketConfigFromEntry("bucket", entry)
|
|
assert.Equal(t, tc.identityId, config.IdentityId)
|
|
if tc.expectOwnerId != AccountAdmin.Id {
|
|
assert.Equal(t, tc.expectOwnerId, config.Owner,
|
|
"the bucket config owner must match the metadata owner")
|
|
} else {
|
|
assert.Empty(t, config.Owner, "an unresolvable owner leaves the config unowned")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// The account id recorded by the S3 API stays authoritative when both are present.
|
|
func TestBucketOwnerPrefersAccountId(t *testing.T) {
|
|
iam := ownerIdentityIAM(t)
|
|
|
|
entry := &filer_pb.Entry{
|
|
Name: "bucket",
|
|
Extended: map[string][]byte{
|
|
s3_constants.AmzIdentityId: []byte("scoped"),
|
|
s3_constants.ExtAmzOwnerKey: []byte("test"),
|
|
},
|
|
}
|
|
|
|
metadata := buildBucketMetadata(iam, entry)
|
|
require.NotNil(t, metadata.Owner)
|
|
assert.Equal(t, "test", *metadata.Owner.ID)
|
|
}
|