diff options
| author | Chris Lu <chrislusf@users.noreply.github.com> | 2025-07-12 01:13:11 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-12 01:13:11 -0700 |
| commit | 687a6a6c1de0fb67b51ec9bfd1781a6c255ff695 (patch) | |
| tree | 3ee2890c890e67a170cec2692425528aa9cd795f /weed/admin/dash | |
| parent | 49d43003e1f5063c57cd1b122469c0cb68d0cd79 (diff) | |
| download | seaweedfs-687a6a6c1de0fb67b51ec9bfd1781a6c255ff695.tar.xz seaweedfs-687a6a6c1de0fb67b51ec9bfd1781a6c255ff695.zip | |
Admin UI: Add policies (#6968)
* add policies to UI, accessing filer directly
* view, edit policies
* add back buttons for "users" page
* remove unused
* fix ui dark mode when modal is closed
* bucket view details button
* fix browser buttons
* filer action button works
* clean up masters page
* fix volume servers action buttons
* fix collections page action button
* fix properties page
* more obvious
* fix directory creation file mode
* Update file_browser_handlers.go
* directory permission
Diffstat (limited to 'weed/admin/dash')
| -rw-r--r-- | weed/admin/dash/admin_server.go | 1 | ||||
| -rw-r--r-- | weed/admin/dash/file_browser_data.go | 80 | ||||
| -rw-r--r-- | weed/admin/dash/file_mode_utils.go | 85 | ||||
| -rw-r--r-- | weed/admin/dash/policies_management.go | 225 |
4 files changed, 312 insertions, 79 deletions
diff --git a/weed/admin/dash/admin_server.go b/weed/admin/dash/admin_server.go index 95bff6deb..9ae5c6ebd 100644 --- a/weed/admin/dash/admin_server.go +++ b/weed/admin/dash/admin_server.go @@ -94,6 +94,7 @@ func NewAdminServer(masterAddress string, templateFS http.FileSystem, dataDir st glog.V(1).Infof("Set filer client for credential manager: %s", filerAddr) break } + glog.V(1).Infof("Waiting for filer discovery for credential manager...") time.Sleep(5 * time.Second) // Retry every 5 seconds } }() diff --git a/weed/admin/dash/file_browser_data.go b/weed/admin/dash/file_browser_data.go index 3cb878718..6bb30c469 100644 --- a/weed/admin/dash/file_browser_data.go +++ b/weed/admin/dash/file_browser_data.go @@ -99,7 +99,7 @@ func (s *AdminServer) GetFileBrowser(path string) (*FileBrowserData, error) { var ttlSec int32 if entry.Attributes != nil { - mode = formatFileMode(entry.Attributes.FileMode) + mode = FormatFileMode(entry.Attributes.FileMode) uid = entry.Attributes.Uid gid = entry.Attributes.Gid size = int64(entry.Attributes.FileSize) @@ -270,81 +270,3 @@ func (s *AdminServer) generateBreadcrumbs(path string) []BreadcrumbItem { return breadcrumbs } - -// formatFileMode converts file mode to Unix-style string representation (e.g., "drwxr-xr-x") -func formatFileMode(mode uint32) string { - var result []byte = make([]byte, 10) - - // File type - switch mode & 0170000 { // S_IFMT mask - case 0040000: // S_IFDIR - result[0] = 'd' - case 0100000: // S_IFREG - result[0] = '-' - case 0120000: // S_IFLNK - result[0] = 'l' - case 0020000: // S_IFCHR - result[0] = 'c' - case 0060000: // S_IFBLK - result[0] = 'b' - case 0010000: // S_IFIFO - result[0] = 'p' - case 0140000: // S_IFSOCK - result[0] = 's' - default: - result[0] = '-' // S_IFREG is default - } - - // Owner permissions - if mode&0400 != 0 { // S_IRUSR - result[1] = 'r' - } else { - result[1] = '-' - } - if mode&0200 != 0 { // S_IWUSR - result[2] = 'w' - } else { - result[2] = '-' - } - if mode&0100 != 0 { // S_IXUSR - result[3] = 'x' - } else { - result[3] = '-' - } - - // Group permissions - if mode&0040 != 0 { // S_IRGRP - result[4] = 'r' - } else { - result[4] = '-' - } - if mode&0020 != 0 { // S_IWGRP - result[5] = 'w' - } else { - result[5] = '-' - } - if mode&0010 != 0 { // S_IXGRP - result[6] = 'x' - } else { - result[6] = '-' - } - - // Other permissions - if mode&0004 != 0 { // S_IROTH - result[7] = 'r' - } else { - result[7] = '-' - } - if mode&0002 != 0 { // S_IWOTH - result[8] = 'w' - } else { - result[8] = '-' - } - if mode&0001 != 0 { // S_IXOTH - result[9] = 'x' - } else { - result[9] = '-' - } - - return string(result) -} diff --git a/weed/admin/dash/file_mode_utils.go b/weed/admin/dash/file_mode_utils.go new file mode 100644 index 000000000..19c5b2f49 --- /dev/null +++ b/weed/admin/dash/file_mode_utils.go @@ -0,0 +1,85 @@ +package dash + +// FormatFileMode converts file mode to Unix-style string representation (e.g., "drwxr-xr-x") +// Handles both Go's os.ModeDir format and standard Unix file type bits +func FormatFileMode(mode uint32) string { + var result []byte = make([]byte, 10) + + // File type - handle Go's os.ModeDir first, then standard Unix file type bits + if mode&0x80000000 != 0 { // Go's os.ModeDir (0x80000000 = 2147483648) + result[0] = 'd' + } else { + switch mode & 0170000 { // S_IFMT mask + case 0040000: // S_IFDIR + result[0] = 'd' + case 0100000: // S_IFREG + result[0] = '-' + case 0120000: // S_IFLNK + result[0] = 'l' + case 0020000: // S_IFCHR + result[0] = 'c' + case 0060000: // S_IFBLK + result[0] = 'b' + case 0010000: // S_IFIFO + result[0] = 'p' + case 0140000: // S_IFSOCK + result[0] = 's' + default: + result[0] = '-' // S_IFREG is default + } + } + + // Permission bits (always use the lower 12 bits regardless of file type format) + // Owner permissions + if mode&0400 != 0 { // S_IRUSR + result[1] = 'r' + } else { + result[1] = '-' + } + if mode&0200 != 0 { // S_IWUSR + result[2] = 'w' + } else { + result[2] = '-' + } + if mode&0100 != 0 { // S_IXUSR + result[3] = 'x' + } else { + result[3] = '-' + } + + // Group permissions + if mode&0040 != 0 { // S_IRGRP + result[4] = 'r' + } else { + result[4] = '-' + } + if mode&0020 != 0 { // S_IWGRP + result[5] = 'w' + } else { + result[5] = '-' + } + if mode&0010 != 0 { // S_IXGRP + result[6] = 'x' + } else { + result[6] = '-' + } + + // Other permissions + if mode&0004 != 0 { // S_IROTH + result[7] = 'r' + } else { + result[7] = '-' + } + if mode&0002 != 0 { // S_IWOTH + result[8] = 'w' + } else { + result[8] = '-' + } + if mode&0001 != 0 { // S_IXOTH + result[9] = 'x' + } else { + result[9] = '-' + } + + return string(result) +} diff --git a/weed/admin/dash/policies_management.go b/weed/admin/dash/policies_management.go new file mode 100644 index 000000000..8853bbb54 --- /dev/null +++ b/weed/admin/dash/policies_management.go @@ -0,0 +1,225 @@ +package dash + +import ( + "context" + "fmt" + "time" + + "github.com/seaweedfs/seaweedfs/weed/credential" + "github.com/seaweedfs/seaweedfs/weed/glog" +) + +type IAMPolicy struct { + Name string `json:"name"` + Document credential.PolicyDocument `json:"document"` + DocumentJSON string `json:"document_json"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type PoliciesCollection struct { + Policies map[string]credential.PolicyDocument `json:"policies"` +} + +type PoliciesData struct { + Username string `json:"username"` + Policies []IAMPolicy `json:"policies"` + TotalPolicies int `json:"total_policies"` + LastUpdated time.Time `json:"last_updated"` +} + +// Policy management request structures +type CreatePolicyRequest struct { + Name string `json:"name" binding:"required"` + Document credential.PolicyDocument `json:"document" binding:"required"` + DocumentJSON string `json:"document_json"` +} + +type UpdatePolicyRequest struct { + Document credential.PolicyDocument `json:"document" binding:"required"` + DocumentJSON string `json:"document_json"` +} + +// PolicyManager interface is now in the credential package + +// CredentialStorePolicyManager implements credential.PolicyManager by delegating to the credential store +type CredentialStorePolicyManager struct { + credentialManager *credential.CredentialManager +} + +// NewCredentialStorePolicyManager creates a new CredentialStorePolicyManager +func NewCredentialStorePolicyManager(credentialManager *credential.CredentialManager) *CredentialStorePolicyManager { + return &CredentialStorePolicyManager{ + credentialManager: credentialManager, + } +} + +// GetPolicies retrieves all IAM policies via credential store +func (cspm *CredentialStorePolicyManager) GetPolicies(ctx context.Context) (map[string]credential.PolicyDocument, error) { + // Get policies from credential store + // We'll use the credential store to access the filer indirectly + // Since policies are stored separately, we need to access the underlying store + store := cspm.credentialManager.GetStore() + glog.V(1).Infof("Getting policies from credential store: %T", store) + + // Check if the store supports policy management + if policyStore, ok := store.(credential.PolicyManager); ok { + glog.V(1).Infof("Store supports policy management, calling GetPolicies") + policies, err := policyStore.GetPolicies(ctx) + if err != nil { + glog.Errorf("Error getting policies from store: %v", err) + return nil, err + } + glog.V(1).Infof("Got %d policies from store", len(policies)) + return policies, nil + } else { + // Fallback: use empty policies for stores that don't support policies + glog.V(1).Infof("Credential store doesn't support policy management, returning empty policies") + return make(map[string]credential.PolicyDocument), nil + } +} + +// CreatePolicy creates a new IAM policy via credential store +func (cspm *CredentialStorePolicyManager) CreatePolicy(ctx context.Context, name string, document credential.PolicyDocument) error { + store := cspm.credentialManager.GetStore() + + if policyStore, ok := store.(credential.PolicyManager); ok { + return policyStore.CreatePolicy(ctx, name, document) + } + + return fmt.Errorf("credential store doesn't support policy creation") +} + +// UpdatePolicy updates an existing IAM policy via credential store +func (cspm *CredentialStorePolicyManager) UpdatePolicy(ctx context.Context, name string, document credential.PolicyDocument) error { + store := cspm.credentialManager.GetStore() + + if policyStore, ok := store.(credential.PolicyManager); ok { + return policyStore.UpdatePolicy(ctx, name, document) + } + + return fmt.Errorf("credential store doesn't support policy updates") +} + +// DeletePolicy deletes an IAM policy via credential store +func (cspm *CredentialStorePolicyManager) DeletePolicy(ctx context.Context, name string) error { + store := cspm.credentialManager.GetStore() + + if policyStore, ok := store.(credential.PolicyManager); ok { + return policyStore.DeletePolicy(ctx, name) + } + + return fmt.Errorf("credential store doesn't support policy deletion") +} + +// GetPolicy retrieves a specific IAM policy via credential store +func (cspm *CredentialStorePolicyManager) GetPolicy(ctx context.Context, name string) (*credential.PolicyDocument, error) { + store := cspm.credentialManager.GetStore() + + if policyStore, ok := store.(credential.PolicyManager); ok { + return policyStore.GetPolicy(ctx, name) + } + + return nil, fmt.Errorf("credential store doesn't support policy retrieval") +} + +// AdminServer policy management methods using credential.PolicyManager +func (s *AdminServer) GetPolicyManager() credential.PolicyManager { + if s.credentialManager == nil { + glog.V(1).Infof("Credential manager is nil, policy management not available") + return nil + } + glog.V(1).Infof("Credential manager available, creating CredentialStorePolicyManager") + return NewCredentialStorePolicyManager(s.credentialManager) +} + +// GetPolicies retrieves all IAM policies +func (s *AdminServer) GetPolicies() ([]IAMPolicy, error) { + policyManager := s.GetPolicyManager() + if policyManager == nil { + return nil, fmt.Errorf("policy manager not available") + } + + ctx := context.Background() + policyMap, err := policyManager.GetPolicies(ctx) + if err != nil { + return nil, err + } + + // Convert map[string]PolicyDocument to []IAMPolicy + var policies []IAMPolicy + for name, doc := range policyMap { + policy := IAMPolicy{ + Name: name, + Document: doc, + DocumentJSON: "", // Will be populated if needed + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + policies = append(policies, policy) + } + + return policies, nil +} + +// CreatePolicy creates a new IAM policy +func (s *AdminServer) CreatePolicy(name string, document credential.PolicyDocument) error { + policyManager := s.GetPolicyManager() + if policyManager == nil { + return fmt.Errorf("policy manager not available") + } + + ctx := context.Background() + return policyManager.CreatePolicy(ctx, name, document) +} + +// UpdatePolicy updates an existing IAM policy +func (s *AdminServer) UpdatePolicy(name string, document credential.PolicyDocument) error { + policyManager := s.GetPolicyManager() + if policyManager == nil { + return fmt.Errorf("policy manager not available") + } + + ctx := context.Background() + return policyManager.UpdatePolicy(ctx, name, document) +} + +// DeletePolicy deletes an IAM policy +func (s *AdminServer) DeletePolicy(name string) error { + policyManager := s.GetPolicyManager() + if policyManager == nil { + return fmt.Errorf("policy manager not available") + } + + ctx := context.Background() + return policyManager.DeletePolicy(ctx, name) +} + +// GetPolicy retrieves a specific IAM policy +func (s *AdminServer) GetPolicy(name string) (*IAMPolicy, error) { + policyManager := s.GetPolicyManager() + if policyManager == nil { + return nil, fmt.Errorf("policy manager not available") + } + + ctx := context.Background() + policyDoc, err := policyManager.GetPolicy(ctx, name) + if err != nil { + return nil, err + } + + if policyDoc == nil { + return nil, nil + } + + // Convert PolicyDocument to IAMPolicy + policy := &IAMPolicy{ + Name: name, + Document: *policyDoc, + DocumentJSON: "", // Will be populated if needed + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + return policy, nil +} |
