diff --git a/weed/admin/dash/s3tables_management.go b/weed/admin/dash/s3tables_management.go index 75ebfcae0..0c38c58a0 100644 --- a/weed/admin/dash/s3tables_management.go +++ b/weed/admin/dash/s3tables_management.go @@ -67,6 +67,15 @@ func parseNamespaceInput(namespace string) ([]string, error) { return s3tables.ParseNamespace(namespace) } +func (s *AdminServer) parseNamespaceFromGin(c *gin.Context, namespace string) ([]string, bool) { + parts, err := parseNamespaceInput(namespace) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid namespace: " + err.Error()}) + return nil, false + } + return parts, true +} + func newS3TablesManager() *s3tables.Manager { manager := s3tables.NewManager() manager.SetAccountID(s3_constants.AccountAdminId) @@ -698,9 +707,8 @@ func (s *AdminServer) CreateS3TablesNamespace(c *gin.Context) { c.JSON(400, gin.H{"error": "bucket_arn and name are required"}) return } - namespaceParts, err := parseNamespaceInput(req.Name) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid namespace: " + err.Error()}) + namespaceParts, ok := s.parseNamespaceFromGin(c, req.Name) + if !ok { return } createReq := &s3tables.CreateNamespaceRequest{TableBucketARN: req.BucketARN, Namespace: namespaceParts} @@ -722,9 +730,8 @@ func (s *AdminServer) DeleteS3TablesNamespace(c *gin.Context) { c.JSON(400, gin.H{"error": "bucket and name query parameters are required"}) return } - namespaceParts, err := parseNamespaceInput(namespace) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid namespace: " + err.Error()}) + namespaceParts, ok := s.parseNamespaceFromGin(c, namespace) + if !ok { return } req := &s3tables.DeleteNamespaceRequest{TableBucketARN: bucketArn, Namespace: namespaceParts} @@ -767,9 +774,8 @@ func (s *AdminServer) CreateS3TablesTable(c *gin.Context) { c.JSON(400, gin.H{"error": "bucket_arn, namespace, and name are required"}) return } - namespaceParts, err := parseNamespaceInput(req.Namespace) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid namespace: " + err.Error()}) + namespaceParts, ok := s.parseNamespaceFromGin(c, req.Namespace) + if !ok { return } format := req.Format @@ -807,9 +813,8 @@ func (s *AdminServer) DeleteS3TablesTable(c *gin.Context) { c.JSON(400, gin.H{"error": "bucket, namespace, and name query parameters are required"}) return } - namespaceParts, err := parseNamespaceInput(namespace) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid namespace: " + err.Error()}) + namespaceParts, ok := s.parseNamespaceFromGin(c, namespace) + if !ok { return } req := &s3tables.DeleteTableRequest{TableBucketARN: bucketArn, Namespace: namespaceParts, Name: name, VersionToken: version} @@ -885,9 +890,8 @@ func (s *AdminServer) PutS3TablesTablePolicy(c *gin.Context) { c.JSON(400, gin.H{"error": "bucket_arn, namespace, name, and policy are required"}) return } - namespaceParts, err := parseNamespaceInput(req.Namespace) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid namespace: " + err.Error()}) + namespaceParts, ok := s.parseNamespaceFromGin(c, req.Namespace) + if !ok { return } putReq := &s3tables.PutTablePolicyRequest{TableBucketARN: req.BucketARN, Namespace: namespaceParts, Name: req.Name, ResourcePolicy: req.Policy} @@ -906,9 +910,8 @@ func (s *AdminServer) GetS3TablesTablePolicy(c *gin.Context) { c.JSON(400, gin.H{"error": "bucket, namespace, and name query parameters are required"}) return } - namespaceParts, err := parseNamespaceInput(namespace) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid namespace: " + err.Error()}) + namespaceParts, ok := s.parseNamespaceFromGin(c, namespace) + if !ok { return } getReq := &s3tables.GetTablePolicyRequest{TableBucketARN: bucketArn, Namespace: namespaceParts, Name: name} @@ -928,9 +931,8 @@ func (s *AdminServer) DeleteS3TablesTablePolicy(c *gin.Context) { c.JSON(400, gin.H{"error": "bucket, namespace, and name query parameters are required"}) return } - namespaceParts, err := parseNamespaceInput(namespace) - if err != nil { - c.JSON(400, gin.H{"error": "Invalid namespace: " + err.Error()}) + namespaceParts, ok := s.parseNamespaceFromGin(c, namespace) + if !ok { return } deleteReq := &s3tables.DeleteTablePolicyRequest{TableBucketARN: bucketArn, Namespace: namespaceParts, Name: name} diff --git a/weed/admin/static/js/s3tables.js b/weed/admin/static/js/s3tables.js index 6ef89df07..aafee0ece 100644 --- a/weed/admin/static/js/s3tables.js +++ b/weed/admin/static/js/s3tables.js @@ -731,6 +731,9 @@ function s3TablesNamespaceNameError(name) { if (!part) { return 'namespace levels cannot be empty'; } + if (part === '.' || part === '..') { + return "namespace name parts cannot be '.' or '..'"; + } if (part.length < 1 || part.length > 255) { return 'Namespace name must be between 1 and 255 characters'; } diff --git a/weed/admin/view/app/s3tables_namespaces.templ b/weed/admin/view/app/s3tables_namespaces.templ index a9286ce3b..b35b23d98 100644 --- a/weed/admin/view/app/s3tables_namespaces.templ +++ b/weed/admin/view/app/s3tables_namespaces.templ @@ -245,11 +245,15 @@ templ S3TablesNamespaces(data dash.S3TablesNamespacesData) { } else if (name.includes('/')) { message = "namespace name cannot contain '/'"; } else { - for (const part of name.split('.')) { - if (!part) { - message = 'namespace levels cannot be empty'; - break; - } + for (const part of name.split('.')) { + if (!part) { + message = 'namespace levels cannot be empty'; + break; + } + if (part === '.' || part === '..') { + message = "namespace name parts cannot be '.' or '..'"; + break; + } const start = part[0]; const end = part[part.length - 1]; const isStartValid = (start >= 'a' && start <= 'z') || (start >= '0' && start <= '9'); diff --git a/weed/admin/view/app/s3tables_namespaces_templ.go b/weed/admin/view/app/s3tables_namespaces_templ.go index 7bc32bd80..6eb8c820e 100644 --- a/weed/admin/view/app/s3tables_namespaces_templ.go +++ b/weed/admin/view/app/s3tables_namespaces_templ.go @@ -242,7 +242,7 @@ func S3TablesNamespaces(data dash.S3TablesNamespacesData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\">
Use lowercase letters, numbers, and underscores. Use dots for nested namespaces (for example, analytics.daily).
Delete Namespace

Are you sure you want to delete the namespace ?

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\">
Use lowercase letters, numbers, and underscores. Use dots for nested namespaces (for example, analytics.daily).
Delete Namespace

Are you sure you want to delete the namespace ?

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }