From bea0f8eda0eef4ad7b20c45b8b1cfc294d1f7735 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 28 Jan 2026 17:36:53 -0800 Subject: [PATCH] s3tables: Use policy framework for table creation authorization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace strict ownership check in CreateTable with policy-based authorization. Now checks both namespace and bucket policies for CreateTable permission, allowing delegation via resource policies while still respecting owner bypass. Authorization logic: - Namespace policy grants CreateTable → allowed - Bucket policy grants CreateTable → allowed - Otherwise → denied (even if same owner) This enables cross-principal table creation via policies while maintaining security through explicit allow/deny semantics. --- weed/s3api/s3tables/handler_table.go | 38 ++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/weed/s3api/s3tables/handler_table.go b/weed/s3api/s3tables/handler_table.go index e9a5b657a..75403642f 100644 --- a/weed/s3api/s3tables/handler_table.go +++ b/weed/s3api/s3tables/handler_table.go @@ -85,8 +85,42 @@ func (h *S3TablesHandler) handleCreateTable(w http.ResponseWriter, r *http.Reque return err } - // Check ownership - if accountID := h.getAccountID(r); accountID != namespaceMetadata.OwnerAccountID { + // Authorize table creation using policy framework (namespace + bucket policies) + accountID := h.getAccountID(r) + bucketPath := getTableBucketPath(bucketName) + namespacePolicy := "" + bucketPolicy := "" + + err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { + // Fetch namespace policy if it exists + policyData, err := h.getExtendedAttribute(r.Context(), client, namespacePath, ExtendedKeyPolicy) + if err == nil { + namespacePolicy = string(policyData) + } else if !errors.Is(err, ErrAttributeNotFound) { + return fmt.Errorf("failed to fetch namespace policy: %v", err) + } + + // Fetch bucket policy if it exists + policyData, err = h.getExtendedAttribute(r.Context(), client, bucketPath, ExtendedKeyPolicy) + if err == nil { + bucketPolicy = string(policyData) + } else if !errors.Is(err, ErrAttributeNotFound) { + return fmt.Errorf("failed to fetch bucket policy: %v", err) + } + + return nil + }) + + if err != nil { + h.writeError(w, http.StatusInternalServerError, ErrCodeInternalError, fmt.Sprintf("failed to fetch policies: %v", err)) + return err + } + + // Check authorization: namespace policy OR bucket policy OR ownership + nsAllowed := CanCreateTable(accountID, namespaceMetadata.OwnerAccountID, namespacePolicy) + bucketAllowed := CanCreateTable(accountID, namespaceMetadata.OwnerAccountID, bucketPolicy) + + if !nsAllowed && !bucketAllowed { h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to create table in this namespace") return ErrAccessDenied }