From 3aace37cf65363c8e28a0dd61938dba51e81b2ba Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 28 Jan 2026 10:36:03 -0800 Subject: [PATCH] s3tables: further refinements to filer operations and utilities - Add multi-segment namespace support to ARN parsing - Refactor permission checking to use map lookup - Wrap lookup errors with ErrNotFound in filer operations - Standardize splitPath to use path package --- weed/s3api/s3tables/filer_ops.go | 4 ++++ weed/s3api/s3tables/permissions.go | 15 +++++++++++++++ weed/s3api/s3tables/utils.go | 20 ++++++++++++++------ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/weed/s3api/s3tables/filer_ops.go b/weed/s3api/s3tables/filer_ops.go index 393d7294e..645500af4 100644 --- a/weed/s3api/s3tables/filer_ops.go +++ b/weed/s3api/s3tables/filer_ops.go @@ -82,6 +82,10 @@ func (h *S3TablesHandler) getExtendedAttribute(ctx context.Context, client filer return nil, fmt.Errorf("%w: %s", ErrNotFound, path) } + if resp.Entry.Extended == nil { + return nil, fmt.Errorf("%w: %s", ErrAttributeNotFound, key) + } + data, ok := resp.Entry.Extended[key] if !ok { return nil, fmt.Errorf("%w: %s", ErrAttributeNotFound, key) diff --git a/weed/s3api/s3tables/permissions.go b/weed/s3api/s3tables/permissions.go index 780379687..47f9de374 100644 --- a/weed/s3api/s3tables/permissions.go +++ b/weed/s3api/s3tables/permissions.go @@ -3,6 +3,8 @@ package s3tables import ( "fmt" "strings" + + "github.com/seaweedfs/seaweedfs/weed/iam/utils" ) // Permission represents a specific action permission @@ -80,6 +82,11 @@ var OperationPermissions = map[string]Permission{ // CheckPermission checks if a principal has permission to perform an operation func CheckPermission(operation, principal, owner string) bool { + // Deny access if identities are empty + if principal == "" || owner == "" { + return false + } + // Owner always has permission if principal == owner { return true @@ -164,6 +171,14 @@ func CanManageTags(principal, owner string) bool { // ExtractPrincipalFromContext extracts the principal (account ID) from request context // For now, this returns the owner/creator, but can be extended to parse from request headers/certs func ExtractPrincipalFromContext(contextID string) string { + // Try to parse as ARN first + if strings.HasPrefix(contextID, "arn:") { + info := utils.ParsePrincipalARN(contextID) + if info.RoleName != "" { + return info.RoleName + } + } + // Extract from context, e.g., "user123" or "account-id" // This is a simplified version - in production, this would parse AWS auth headers if strings.Contains(contextID, ":") { diff --git a/weed/s3api/s3tables/utils.go b/weed/s3api/s3tables/utils.go index 3ceec3d19..d44038bd7 100644 --- a/weed/s3api/s3tables/utils.go +++ b/weed/s3api/s3tables/utils.go @@ -2,8 +2,8 @@ package s3tables import ( "fmt" + "net/url" "path" - "path/filepath" "regexp" "strings" "time" @@ -25,12 +25,20 @@ func parseBucketNameFromARN(arn string) (string, error) { // parseTableFromARN extracts bucket name, namespace, and table name from ARN // ARN format: arn:aws:s3tables:{region}:{account}:bucket/{bucket-name}/table/{namespace}/{table-name} func parseTableFromARN(arn string) (bucketName, namespace, tableName string, err error) { - pattern := regexp.MustCompile(`^arn:aws:s3tables:[^:]*:[^:]*:bucket/([a-z0-9_-]+)/table/([a-z0-9_]+)/([a-z0-9_]+)$`) + // Updated regex to capture multi-segment namespaces containing slashes + pattern := regexp.MustCompile(`^arn:aws:s3tables:[^:]*:[^:]*:bucket/([a-z0-9_-]+)/table/([^/]+(?:/[^/]+)*)/([a-z0-9_]+)$`) matches := pattern.FindStringSubmatch(arn) if len(matches) != 4 { return "", "", "", fmt.Errorf("invalid table ARN: %s", arn) } - return matches[1], matches[2], matches[3], nil + + // URL decode the namespace + namespaceUnescaped, err := url.QueryUnescape(matches[2]) + if err != nil { + return "", "", "", fmt.Errorf("invalid namespace encoding in ARN: %v", err) + } + + return matches[1], namespaceUnescaped, matches[3], nil } // Path helpers @@ -94,9 +102,9 @@ func generateVersionToken() string { } // splitPath splits a path into directory and name components using stdlib -func splitPath(path string) (dir, name string) { - dir = filepath.Dir(path) - name = filepath.Base(path) +func splitPath(p string) (dir, name string) { + dir = path.Dir(p) + name = path.Base(p) return }