* Add shared s3tables manager * Add s3tables shell commands * Add s3tables admin API * Add s3tables admin UI * Fix admin s3tables namespace create * Rename table buckets menu * Centralize s3tables tag validation * Reuse s3tables manager in admin * Extract s3tables list limit * Add s3tables bucket ARN helper * Remove write middleware from s3tables APIs * Fix bucket link and policy hint * Fix table tag parsing and nav link * Disable namespace table link on invalid ARN * Improve s3tables error decode * Return flag parse errors for s3tables tag * Accept query params for namespace create * Bind namespace create form data * Read s3tables JS data from DOM * s3tables: allow empty region ARN * shell: pass s3tables account id * shell: require account for table buckets * shell: use bucket name for namespaces * shell: use bucket name for tables * shell: use bucket name for tags * admin: add table buckets links in file browser * s3api: reuse s3tables tag validation * admin: harden s3tables UI handlers * fix admin list table buckets * allow admin s3tables access * validate s3tables bucket tags * log s3tables bucket metadata errors * rollback table bucket on owner failure * show s3tables bucket owner * add s3tables iam conditions * Add s3tables user permissions UI * Authorize s3tables using identity actions * Add s3tables permissions to user modal * Disambiguate bucket scope in user permissions * Block table bucket names that match S3 buckets * Pretty-print IAM identity JSON * Include tags in s3tables permission context * admin: refactor S3 Tables inline JavaScript into a separate file * s3tables: extend IAM policy condition operators support * shell: use LookupEntry wrapper for s3tables bucket conflict check * admin: handle buildBucketPermissions validation in create/update flows
99 lines
2.8 KiB
Go
99 lines
2.8 KiB
Go
package s3tables
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
|
)
|
|
|
|
// Manager provides reusable S3 Tables operations for shell/admin without HTTP routing.
|
|
type Manager struct {
|
|
handler *S3TablesHandler
|
|
}
|
|
|
|
// NewManager creates a new Manager.
|
|
func NewManager() *Manager {
|
|
return &Manager{handler: NewS3TablesHandler()}
|
|
}
|
|
|
|
// SetRegion sets the AWS region for ARN generation.
|
|
func (m *Manager) SetRegion(region string) {
|
|
m.handler.SetRegion(region)
|
|
}
|
|
|
|
// SetAccountID sets the AWS account ID for ARN generation.
|
|
func (m *Manager) SetAccountID(accountID string) {
|
|
m.handler.SetAccountID(accountID)
|
|
}
|
|
|
|
// Execute runs an S3 Tables operation and decodes the response into resp (if provided).
|
|
func (m *Manager) Execute(ctx context.Context, filerClient FilerClient, operation string, req interface{}, resp interface{}, identity string) error {
|
|
body, err := json.Marshal(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, "/", bytes.NewReader(body))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
httpReq.Header.Set("Content-Type", "application/x-amz-json-1.1")
|
|
httpReq.Header.Set("X-Amz-Target", "S3Tables."+operation)
|
|
if identity != "" {
|
|
httpReq.Header.Set(s3_constants.AmzAccountId, identity)
|
|
httpReq = httpReq.WithContext(s3_constants.SetIdentityNameInContext(httpReq.Context(), identity))
|
|
}
|
|
recorder := httptest.NewRecorder()
|
|
m.handler.HandleRequest(recorder, httpReq, filerClient)
|
|
return decodeS3TablesHTTPResponse(recorder, resp)
|
|
}
|
|
|
|
func decodeS3TablesHTTPResponse(recorder *httptest.ResponseRecorder, resp interface{}) error {
|
|
result := recorder.Result()
|
|
defer result.Body.Close()
|
|
data, err := io.ReadAll(result.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if result.StatusCode >= http.StatusBadRequest {
|
|
var errResp S3TablesError
|
|
if len(data) > 0 {
|
|
if jsonErr := json.Unmarshal(data, &errResp); jsonErr == nil && (errResp.Type != "" || errResp.Message != "") {
|
|
return &errResp
|
|
}
|
|
}
|
|
return &S3TablesError{Type: ErrCodeInternalError, Message: string(bytes.TrimSpace(data))}
|
|
}
|
|
if resp == nil || len(data) == 0 {
|
|
return nil
|
|
}
|
|
if err := json.Unmarshal(data, resp); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ManagerClient adapts a SeaweedFilerClient to the FilerClient interface.
|
|
type ManagerClient struct {
|
|
client filer_pb.SeaweedFilerClient
|
|
}
|
|
|
|
// NewManagerClient wraps a filer client.
|
|
func NewManagerClient(client filer_pb.SeaweedFilerClient) *ManagerClient {
|
|
return &ManagerClient{client: client}
|
|
}
|
|
|
|
// WithFilerClient implements FilerClient.
|
|
func (m *ManagerClient) WithFilerClient(streamingMode bool, fn func(client filer_pb.SeaweedFilerClient) error) error {
|
|
if m.client == nil {
|
|
return errors.New("nil filer client")
|
|
}
|
|
return fn(m.client)
|
|
}
|