|
|
|
@ -15,13 +15,6 @@ import ( |
|
|
|
|
|
|
|
// handleCreateTable creates a new table in a namespace
|
|
|
|
func (h *S3TablesHandler) handleCreateTable(w http.ResponseWriter, r *http.Request, filerClient FilerClient) error { |
|
|
|
// Check permission
|
|
|
|
principal := h.getPrincipalFromRequest(r) |
|
|
|
accountID := h.getAccountID(r) |
|
|
|
if !CanCreateTable(principal, accountID) { |
|
|
|
h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to create table") |
|
|
|
return NewAuthError("CreateTable", principal, "not authorized to create table") |
|
|
|
} |
|
|
|
|
|
|
|
var req CreateTableRequest |
|
|
|
if err := h.readRequestBody(r, &req); err != nil { |
|
|
|
@ -71,9 +64,16 @@ func (h *S3TablesHandler) handleCreateTable(w http.ResponseWriter, r *http.Reque |
|
|
|
|
|
|
|
// Check if namespace exists
|
|
|
|
namespacePath := getNamespacePath(bucketName, namespaceName) |
|
|
|
var namespaceMetadata namespaceMetadata |
|
|
|
err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { |
|
|
|
_, err := h.getExtendedAttribute(r.Context(), client, namespacePath, ExtendedKeyMetadata) |
|
|
|
return err |
|
|
|
data, err := h.getExtendedAttribute(r.Context(), client, namespacePath, ExtendedKeyMetadata) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
if err := json.Unmarshal(data, &namespaceMetadata); err != nil { |
|
|
|
return fmt.Errorf("failed to unmarshal namespace metadata: %w", err) |
|
|
|
} |
|
|
|
return nil |
|
|
|
}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
@ -85,6 +85,13 @@ func (h *S3TablesHandler) handleCreateTable(w http.ResponseWriter, r *http.Reque |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
// Check permission
|
|
|
|
principal := h.getPrincipalFromRequest(r) |
|
|
|
if !CanCreateTable(principal, namespaceMetadata.OwnerID) { |
|
|
|
h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to create table") |
|
|
|
return NewAuthError("CreateTable", principal, "not authorized to create table") |
|
|
|
} |
|
|
|
|
|
|
|
tablePath := getTablePath(bucketName, namespaceName, tableName) |
|
|
|
|
|
|
|
// Check if table already exists
|
|
|
|
@ -171,13 +178,6 @@ func (h *S3TablesHandler) handleCreateTable(w http.ResponseWriter, r *http.Reque |
|
|
|
|
|
|
|
// handleGetTable gets details of a table
|
|
|
|
func (h *S3TablesHandler) handleGetTable(w http.ResponseWriter, r *http.Request, filerClient FilerClient) error { |
|
|
|
// Check permission
|
|
|
|
principal := h.getPrincipalFromRequest(r) |
|
|
|
accountID := h.getAccountID(r) |
|
|
|
if !CanGetTable(principal, accountID) { |
|
|
|
h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to get table") |
|
|
|
return NewAuthError("GetTable", principal, "not authorized to get table") |
|
|
|
} |
|
|
|
|
|
|
|
var req GetTableRequest |
|
|
|
if err := h.readRequestBody(r, &req); err != nil { |
|
|
|
@ -224,7 +224,10 @@ func (h *S3TablesHandler) handleGetTable(w http.ResponseWriter, r *http.Request, |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
return json.Unmarshal(data, &metadata) |
|
|
|
if err := json.Unmarshal(data, &metadata); err != nil { |
|
|
|
return fmt.Errorf("failed to unmarshal table metadata: %w", err) |
|
|
|
} |
|
|
|
return nil |
|
|
|
}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
@ -236,6 +239,13 @@ func (h *S3TablesHandler) handleGetTable(w http.ResponseWriter, r *http.Request, |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
// Check permission
|
|
|
|
principal := h.getPrincipalFromRequest(r) |
|
|
|
if !CanGetTable(principal, metadata.OwnerID) { |
|
|
|
h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to get table") |
|
|
|
return NewAuthError("GetTable", principal, "not authorized to get table") |
|
|
|
} |
|
|
|
|
|
|
|
tableARN := h.generateTableARN(r, bucketName, namespace+"/"+tableName) |
|
|
|
|
|
|
|
resp := &GetTableResponse{ |
|
|
|
@ -256,13 +266,6 @@ func (h *S3TablesHandler) handleGetTable(w http.ResponseWriter, r *http.Request, |
|
|
|
|
|
|
|
// handleListTables lists all tables in a namespace or bucket
|
|
|
|
func (h *S3TablesHandler) handleListTables(w http.ResponseWriter, r *http.Request, filerClient FilerClient) error { |
|
|
|
// Check permission
|
|
|
|
principal := h.getPrincipalFromRequest(r) |
|
|
|
accountID := h.getAccountID(r) |
|
|
|
if !CanListTables(principal, accountID) { |
|
|
|
h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to list tables") |
|
|
|
return NewAuthError("ListTables", principal, "not authorized to list tables") |
|
|
|
} |
|
|
|
|
|
|
|
var req ListTablesRequest |
|
|
|
if err := h.readRequestBody(r, &req); err != nil { |
|
|
|
@ -296,15 +299,55 @@ func (h *S3TablesHandler) handleListTables(w http.ResponseWriter, r *http.Reques |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
// Check permission (check namespace ownership)
|
|
|
|
namespacePath := getNamespacePath(bucketName, namespaceName) |
|
|
|
var nsMeta namespaceMetadata |
|
|
|
data, err := h.getExtendedAttribute(r.Context(), client, namespacePath, ExtendedKeyMetadata) |
|
|
|
if err != nil { |
|
|
|
return err // Not Found handled by caller
|
|
|
|
} |
|
|
|
if err := json.Unmarshal(data, &nsMeta); err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
principal := h.getPrincipalFromRequest(r) |
|
|
|
if !CanListTables(principal, nsMeta.OwnerID) { |
|
|
|
return NewAuthError("ListTables", principal, "not authorized to list tables") |
|
|
|
} |
|
|
|
|
|
|
|
tables, paginationToken, err = h.listTablesInNamespaceWithClient(r, client, bucketName, namespaceName, req.Prefix, req.ContinuationToken, maxTables) |
|
|
|
} else { |
|
|
|
// Check permission (check bucket ownership)
|
|
|
|
bucketPath := getTableBucketPath(bucketName) |
|
|
|
var bucketMeta tableBucketMetadata |
|
|
|
data, err := h.getExtendedAttribute(r.Context(), client, bucketPath, ExtendedKeyMetadata) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
if err := json.Unmarshal(data, &bucketMeta); err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
principal := h.getPrincipalFromRequest(r) |
|
|
|
if !CanListTables(principal, bucketMeta.OwnerID) { |
|
|
|
return NewAuthError("ListTables", principal, "not authorized to list tables") |
|
|
|
} |
|
|
|
|
|
|
|
tables, paginationToken, err = h.listTablesInAllNamespaces(r, client, bucketName, req.Prefix, req.ContinuationToken, maxTables) |
|
|
|
} |
|
|
|
return err |
|
|
|
}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
h.writeError(w, http.StatusInternalServerError, ErrCodeInternalError, fmt.Sprintf("failed to list tables: %v", err)) |
|
|
|
if errors.Is(err, filer_pb.ErrNotFound) { |
|
|
|
h.writeError(w, http.StatusNotFound, ErrCodeNoSuchBucket, "resource not found") |
|
|
|
} else { |
|
|
|
var authErr *AuthError |
|
|
|
if errors.As(err, &authErr) { |
|
|
|
h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, err.Error()) |
|
|
|
} else { |
|
|
|
h.writeError(w, http.StatusInternalServerError, ErrCodeInternalError, fmt.Sprintf("failed to list tables: %v", err)) |
|
|
|
} |
|
|
|
} |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
@ -495,13 +538,6 @@ func (h *S3TablesHandler) listTablesInAllNamespaces(r *http.Request, client file |
|
|
|
|
|
|
|
// handleDeleteTable deletes a table from a namespace
|
|
|
|
func (h *S3TablesHandler) handleDeleteTable(w http.ResponseWriter, r *http.Request, filerClient FilerClient) error { |
|
|
|
// Check permission
|
|
|
|
principal := h.getPrincipalFromRequest(r) |
|
|
|
accountID := h.getAccountID(r) |
|
|
|
if !CanDeleteTable(principal, accountID) { |
|
|
|
h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to delete table") |
|
|
|
return NewAuthError("DeleteTable", principal, "not authorized to delete table") |
|
|
|
} |
|
|
|
|
|
|
|
var req DeleteTableRequest |
|
|
|
if err := h.readRequestBody(r, &req); err != nil { |
|
|
|
@ -535,17 +571,18 @@ func (h *S3TablesHandler) handleDeleteTable(w http.ResponseWriter, r *http.Reque |
|
|
|
tablePath := getTablePath(bucketName, namespaceName, tableName) |
|
|
|
|
|
|
|
// Check if table exists and enforce VersionToken if provided
|
|
|
|
var metadata tableMetadataInternal |
|
|
|
err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { |
|
|
|
data, err := h.getExtendedAttribute(r.Context(), client, tablePath, ExtendedKeyMetadata) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
if err := json.Unmarshal(data, &metadata); err != nil { |
|
|
|
return fmt.Errorf("failed to unmarshal table metadata: %w", err) |
|
|
|
} |
|
|
|
|
|
|
|
if req.VersionToken != "" { |
|
|
|
var metadata tableMetadataInternal |
|
|
|
if err := json.Unmarshal(data, &metadata); err != nil { |
|
|
|
return fmt.Errorf("failed to unmarshal table metadata: %w", err) |
|
|
|
} |
|
|
|
if metadata.VersionToken != req.VersionToken { |
|
|
|
return ErrVersionTokenMismatch |
|
|
|
} |
|
|
|
@ -564,6 +601,13 @@ func (h *S3TablesHandler) handleDeleteTable(w http.ResponseWriter, r *http.Reque |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
// Check permission
|
|
|
|
principal := h.getPrincipalFromRequest(r) |
|
|
|
if !CanDeleteTable(principal, metadata.OwnerID) { |
|
|
|
h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to delete table") |
|
|
|
return NewAuthError("DeleteTable", principal, "not authorized to delete table") |
|
|
|
} |
|
|
|
|
|
|
|
// Delete the table
|
|
|
|
err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { |
|
|
|
return h.deleteDirectory(r.Context(), client, tablePath) |
|
|
|
|