Browse Source

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
pull/8147/head
Chris Lu 5 days ago
parent
commit
3aace37cf6
  1. 4
      weed/s3api/s3tables/filer_ops.go
  2. 15
      weed/s3api/s3tables/permissions.go
  3. 20
      weed/s3api/s3tables/utils.go

4
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) 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] data, ok := resp.Entry.Extended[key]
if !ok { if !ok {
return nil, fmt.Errorf("%w: %s", ErrAttributeNotFound, key) return nil, fmt.Errorf("%w: %s", ErrAttributeNotFound, key)

15
weed/s3api/s3tables/permissions.go

@ -3,6 +3,8 @@ package s3tables
import ( import (
"fmt" "fmt"
"strings" "strings"
"github.com/seaweedfs/seaweedfs/weed/iam/utils"
) )
// Permission represents a specific action permission // 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 // CheckPermission checks if a principal has permission to perform an operation
func CheckPermission(operation, principal, owner string) bool { func CheckPermission(operation, principal, owner string) bool {
// Deny access if identities are empty
if principal == "" || owner == "" {
return false
}
// Owner always has permission // Owner always has permission
if principal == owner { if principal == owner {
return true return true
@ -164,6 +171,14 @@ func CanManageTags(principal, owner string) bool {
// ExtractPrincipalFromContext extracts the principal (account ID) from request context // 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 // For now, this returns the owner/creator, but can be extended to parse from request headers/certs
func ExtractPrincipalFromContext(contextID string) string { 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" // Extract from context, e.g., "user123" or "account-id"
// This is a simplified version - in production, this would parse AWS auth headers // This is a simplified version - in production, this would parse AWS auth headers
if strings.Contains(contextID, ":") { if strings.Contains(contextID, ":") {

20
weed/s3api/s3tables/utils.go

@ -2,8 +2,8 @@ package s3tables
import ( import (
"fmt" "fmt"
"net/url"
"path" "path"
"path/filepath"
"regexp" "regexp"
"strings" "strings"
"time" "time"
@ -25,12 +25,20 @@ func parseBucketNameFromARN(arn string) (string, error) {
// parseTableFromARN extracts bucket name, namespace, and table name from ARN // parseTableFromARN extracts bucket name, namespace, and table name from ARN
// ARN format: arn:aws:s3tables:{region}:{account}:bucket/{bucket-name}/table/{namespace}/{table-name} // ARN format: arn:aws:s3tables:{region}:{account}:bucket/{bucket-name}/table/{namespace}/{table-name}
func parseTableFromARN(arn string) (bucketName, namespace, tableName string, err error) { 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) matches := pattern.FindStringSubmatch(arn)
if len(matches) != 4 { if len(matches) != 4 {
return "", "", "", fmt.Errorf("invalid table ARN: %s", arn) 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 // Path helpers
@ -94,9 +102,9 @@ func generateVersionToken() string {
} }
// splitPath splits a path into directory and name components using stdlib // 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 return
} }

Loading…
Cancel
Save