diff --git a/weed/s3api/identity_reflection_test.go b/weed/s3api/identity_reflection_test.go new file mode 100644 index 000000000..b40cc31a7 --- /dev/null +++ b/weed/s3api/identity_reflection_test.go @@ -0,0 +1,39 @@ +package s3api + +import ( + "reflect" + "testing" +) + +// TestIdentityFieldsForS3TablesReflection ensures the identity struct keeps the +// fields relied on by s3tables.getIdentityPrincipalArn, getIdentityPolicyNames, +// and getIdentityClaims via reflection. +func TestIdentityFieldsForS3TablesReflection(t *testing.T) { + typ := reflect.TypeOf(Identity{}) + checkField(t, typ, "PrincipalArn", reflect.String) + field, ok := typ.FieldByName("PolicyNames") + if !ok { + t.Fatalf("Identity.PolicyNames missing") + } + if field.Type.Kind() != reflect.Slice { + t.Fatalf("Identity.PolicyNames must be a slice, got %s", field.Type.Kind()) + } + field, ok = typ.FieldByName("Claims") + if !ok { + t.Fatalf("Identity.Claims missing") + } + if field.Type.Kind() != reflect.Map || field.Type.Key().Kind() != reflect.String { + t.Fatalf("Identity.Claims must be map[string]..., got %s/%s", field.Type.Kind(), field.Type.Key().Kind()) + } +} + +func checkField(t *testing.T, typ reflect.Type, name string, kind reflect.Kind) { + t.Helper() + field, ok := typ.FieldByName(name) + if !ok { + t.Fatalf("Identity.%s missing", name) + } + if field.Type.Kind() != kind { + t.Fatalf("Identity.%s must be %s, got %s", name, kind, field.Type.Kind()) + } +} diff --git a/weed/s3api/s3tables/handler_bucket_create.go b/weed/s3api/s3tables/handler_bucket_create.go index 6c64d45ee..9e7f4e283 100644 --- a/weed/s3api/s3tables/handler_bucket_create.go +++ b/weed/s3api/s3tables/handler_bucket_create.go @@ -29,13 +29,24 @@ func (h *S3TablesHandler) handleCreateTableBucket(w http.ResponseWriter, r *http principal := h.getAccountID(r) identityActions := getIdentityActions(r) identityPolicyNames := getIdentityPolicyNames(r) - if h.shouldUseIAM(r, identityActions, identityPolicyNames) { + useIAM := h.shouldUseIAM(r, identityActions, identityPolicyNames) + useLegacy := !useIAM + if useIAM { allowed, err := h.authorizeIAMAction(r, identityPolicyNames, "CreateTableBucket", h.generateTableBucketARN(principal, req.Name), fmt.Sprintf("arn:aws:s3:::%s", req.Name)) - if err != nil || !allowed { + if err != nil { h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to create table buckets") return NewAuthError("CreateTableBucket", principal, "not authorized to create table buckets") } - } else { + if !allowed { + if h.defaultAllow && len(identityActions) == 0 && len(identityPolicyNames) == 0 { + useLegacy = true + } else { + h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to create table buckets") + return NewAuthError("CreateTableBucket", principal, "not authorized to create table buckets") + } + } + } + if useLegacy { owner := h.accountID if owner == "" { owner = DefaultAccountID