mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-29 16:30:44 +01:00
* s3: allow PutBucketAcl when auth is off * s3: resolve the PutBucketAcl owner against the recorded owner The unauthenticated path now resolves the account as anonymous, but XML ACLs were still checked against it while GetBucketAcl reported an empty owner for ownerless buckets, so a read-modify-write ACL update failed with AccessDenied. GetBucketAcl and the XML check now both use the recorded owner when the bucket has one and the resolved account otherwise. The canned-ACL grantee moves to the same owner, and a request carrying no account no longer erases a stored owner. * s3: cover PutBucketAcl without auth for XML bodies and stored owners The XML case posts the anonymous owner GetBucketAcl reports for an ownerless bucket; the canned case asserts a recorded owner survives an unauthenticated update and receives the full-control grant. Ambient AWS credentials are cleared so the memory store never turns auth on. * s3: keep the recorded owner through a PutBucketAcl update The grantee and the stored owner were resolved differently: grants named the recorded owner while config.Owner still took the caller's account, so an admin updating another account's bucket rewrote the owner and broke an XML retry naming the original one. An ownerless bucket adopts the ACL's owner; an owned bucket keeps it. * s3: fall back to the admin owner for an ownerless bucket in ACL handlers An unauthenticated PutBucketAcl resolved the owner as anonymous and stored it on the bucket, but the registry reports an ownerless bucket as admin-owned: BucketOwnerEnforced uploads then stamped anonymous instead of admin, and ownership checks after enabling auth compared against the wrong owner. Both ACL handlers now resolve an anonymous fallback to the admin account, so GetBucketAcl reports the same effective owner the registry does and the stored owner matches it. * s3: resolve the admin owner for bucket grants when the caller is anonymous or untrusted Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * s3: skip recording anonymous identity or account as bucket owner Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * s3: report stored bucket ACL grants verbatim Explicitly saved anonymous full-control grants must survive a GET-to-PUT round trip unchanged, so the read path no longer rewrites them to the reported owner. Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * s3: expect the stored anonymous grant verbatim in GetBucketAcl Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: user.email <chrislusf@users.noreply.github.com> Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
58 lines
1.7 KiB
Go
58 lines
1.7 KiB
Go
package s3api
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
|
|
)
|
|
|
|
func getAccountId(r *http.Request) string {
|
|
id := r.Header.Get(s3_constants.AmzAccountId)
|
|
if len(id) == 0 {
|
|
return AccountAnonymous.Id
|
|
} else {
|
|
return id
|
|
}
|
|
}
|
|
|
|
// requestAccountId returns the authenticated caller's account; with auth off
|
|
// the header is client-supplied, so the admin account stands in instead.
|
|
func (s3a *S3ApiServer) requestAccountId(r *http.Request) string {
|
|
if s3a.iam != nil && !s3a.iam.isEnabled() {
|
|
return AccountAdmin.Id
|
|
}
|
|
return r.Header.Get(s3_constants.AmzAccountId)
|
|
}
|
|
|
|
// ownerAccountId resolves the account that owns grants and ownerless buckets:
|
|
// the requester's account when one is trusted, else the admin account — with
|
|
// auth off the account header is client-supplied and cannot claim ownership.
|
|
func (s3a *S3ApiServer) ownerAccountId(r *http.Request) string {
|
|
accountId := getAccountId(r)
|
|
if accountId == AccountAnonymous.Id || (s3a.iam != nil && !s3a.iam.isEnabled()) {
|
|
return AccountAdmin.Id
|
|
}
|
|
return accountId
|
|
}
|
|
|
|
func (s3a *S3ApiServer) checkAccessByOwnership(r *http.Request, bucket string) s3err.ErrorCode {
|
|
metadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket)
|
|
if errCode != s3err.ErrNone {
|
|
return errCode
|
|
}
|
|
// Without auth there is no owner to compare against.
|
|
if s3a.iam != nil && !s3a.iam.isEnabled() {
|
|
return s3err.ErrNone
|
|
}
|
|
// Admin by capability, not account id: account-less identities share the "admin" id.
|
|
if s3a.isUserAdmin(r) {
|
|
return s3err.ErrNone
|
|
}
|
|
accountId := getAccountId(r)
|
|
if metadata.Owner != nil && metadata.Owner.ID != nil && accountId == *metadata.Owner.ID {
|
|
return s3err.ErrNone
|
|
}
|
|
return s3err.ErrAccessDenied
|
|
}
|