aboutsummaryrefslogtreecommitdiff
path: root/weed/admin/dash
diff options
context:
space:
mode:
authorChris Lu <chrislusf@users.noreply.github.com>2025-07-12 01:13:11 -0700
committerGitHub <noreply@github.com>2025-07-12 01:13:11 -0700
commit687a6a6c1de0fb67b51ec9bfd1781a6c255ff695 (patch)
tree3ee2890c890e67a170cec2692425528aa9cd795f /weed/admin/dash
parent49d43003e1f5063c57cd1b122469c0cb68d0cd79 (diff)
downloadseaweedfs-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.go1
-rw-r--r--weed/admin/dash/file_browser_data.go80
-rw-r--r--weed/admin/dash/file_mode_utils.go85
-rw-r--r--weed/admin/dash/policies_management.go225
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
+}