diff --git a/test/s3tables/table-buckets/s3tables_integration_test.go b/test/s3tables/table-buckets/s3tables_integration_test.go index 753012283..6f84c3ca8 100644 --- a/test/s3tables/table-buckets/s3tables_integration_test.go +++ b/test/s3tables/table-buckets/s3tables_integration_test.go @@ -2,13 +2,16 @@ package s3tables import ( "context" + "encoding/json" "fmt" "net" "net/http" "os" "path/filepath" "strconv" + "strings" "testing" + "text/template" "time" cryptorand "crypto/rand" diff --git a/weed/s3api/s3tables/handler_bucket_create.go b/weed/s3api/s3tables/handler_bucket_create.go index 75d44062e..77e5fe6ef 100644 --- a/weed/s3api/s3tables/handler_bucket_create.go +++ b/weed/s3api/s3tables/handler_bucket_create.go @@ -106,7 +106,7 @@ func (h *S3TablesHandler) handleCreateTableBucket(w http.ResponseWriter, r *http metadata := &tableBucketMetadata{ Name: req.Name, CreatedAt: now, - OwnerAccountID: h.getAccountID(r), + OwnerAccountID: principal, } metadataBytes, err := json.Marshal(metadata) diff --git a/weed/s3api/s3tables/iam.go b/weed/s3api/s3tables/iam.go index 214e51efe..765787b44 100644 --- a/weed/s3api/s3tables/iam.go +++ b/weed/s3api/s3tables/iam.go @@ -26,6 +26,11 @@ func (h *S3TablesHandler) shouldUseIAM(r *http.Request, identityActions, identit if h.iamAuthorizer == nil || r == nil { return false } + // An empty inline `identityActions` slice doesn't mean the identity has no + // permissions—it just means authorization lives in IAM policies or session + // tokens instead of static action lists. We therefore prefer the IAM path + // whenever inline actions are absent and fall back to default policy names + // or session tokens. if hasSessionToken(r) { return true } @@ -51,7 +56,9 @@ func extractSessionToken(r *http.Request) string { func (h *S3TablesHandler) authorizeIAMAction(r *http.Request, identityPolicyNames []string, action string, resources ...string) (bool, error) { if h.iamAuthorizer == nil { - return false, nil + err := fmt.Errorf("nil iamAuthorizer in authorizeIAMAction") + glog.V(2).Infof("S3Tables: %v", err) + return false, err } principal := r.Header.Get("X-SeaweedFS-Principal") if principal == "" { @@ -73,7 +80,9 @@ func (h *S3TablesHandler) authorizeIAMAction(r *http.Request, identityPolicyName policyNames = getIdentityPolicyNames(r) } - var lastErr error + if len(resources) == 0 { + return false, fmt.Errorf("no resources provided to authorizeIAMAction") + } for _, resource := range resources { if resource == "" { continue @@ -87,15 +96,15 @@ func (h *S3TablesHandler) authorizeIAMAction(r *http.Request, identityPolicyName PolicyNames: policyNames, }) if err != nil { - lastErr = err glog.V(2).Infof("S3Tables: IAM authorization error action=%s resource=%s principal=%s: %v", action, resource, principal, err) - continue + return false, err } - if allowed { - return true, nil + if !allowed { + err := fmt.Errorf("access denied by IAM for resource %s", resource) + return false, err } } - return false, lastErr + return true, nil } func getIdentityPrincipalArn(r *http.Request) string { @@ -202,3 +211,8 @@ func getIdentityStructValue(r *http.Request) (reflect.Value, bool) { } return val, true } + +// The identity structure is expected to be a pointer to a struct with the +// reflection fields used below (PrincipalArn string, PolicyNames []string, +// Claims map[string]interface{}). This helper centralizes the nil-check / ptr-deref +// logic so the callers can focus on reading the proper field.