diff options
Diffstat (limited to 'weed/credential/memory')
| -rw-r--r-- | weed/credential/memory/memory_identity.go | 302 | ||||
| -rw-r--r-- | weed/credential/memory/memory_policy.go | 77 | ||||
| -rw-r--r-- | weed/credential/memory/memory_store.go | 303 |
3 files changed, 384 insertions, 298 deletions
diff --git a/weed/credential/memory/memory_identity.go b/weed/credential/memory/memory_identity.go new file mode 100644 index 000000000..191aa5d16 --- /dev/null +++ b/weed/credential/memory/memory_identity.go @@ -0,0 +1,302 @@ +package memory + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/seaweedfs/seaweedfs/weed/credential" + "github.com/seaweedfs/seaweedfs/weed/pb/iam_pb" +) + +func (store *MemoryStore) LoadConfiguration(ctx context.Context) (*iam_pb.S3ApiConfiguration, error) { + store.mu.RLock() + defer store.mu.RUnlock() + + if !store.initialized { + return nil, fmt.Errorf("store not initialized") + } + + config := &iam_pb.S3ApiConfiguration{} + + // Convert all users to identities + for _, user := range store.users { + // Deep copy the identity to avoid mutation issues + identityCopy := store.deepCopyIdentity(user) + config.Identities = append(config.Identities, identityCopy) + } + + return config, nil +} + +func (store *MemoryStore) SaveConfiguration(ctx context.Context, config *iam_pb.S3ApiConfiguration) error { + store.mu.Lock() + defer store.mu.Unlock() + + if !store.initialized { + return fmt.Errorf("store not initialized") + } + + // Clear existing data + store.users = make(map[string]*iam_pb.Identity) + store.accessKeys = make(map[string]string) + + // Add all identities + for _, identity := range config.Identities { + // Deep copy to avoid mutation issues + identityCopy := store.deepCopyIdentity(identity) + store.users[identity.Name] = identityCopy + + // Index access keys + for _, credential := range identity.Credentials { + store.accessKeys[credential.AccessKey] = identity.Name + } + } + + return nil +} + +func (store *MemoryStore) CreateUser(ctx context.Context, identity *iam_pb.Identity) error { + store.mu.Lock() + defer store.mu.Unlock() + + if !store.initialized { + return fmt.Errorf("store not initialized") + } + + if _, exists := store.users[identity.Name]; exists { + return credential.ErrUserAlreadyExists + } + + // Check for duplicate access keys + for _, cred := range identity.Credentials { + if _, exists := store.accessKeys[cred.AccessKey]; exists { + return fmt.Errorf("access key %s already exists", cred.AccessKey) + } + } + + // Deep copy to avoid mutation issues + identityCopy := store.deepCopyIdentity(identity) + store.users[identity.Name] = identityCopy + + // Index access keys + for _, cred := range identity.Credentials { + store.accessKeys[cred.AccessKey] = identity.Name + } + + return nil +} + +func (store *MemoryStore) GetUser(ctx context.Context, username string) (*iam_pb.Identity, error) { + store.mu.RLock() + defer store.mu.RUnlock() + + if !store.initialized { + return nil, fmt.Errorf("store not initialized") + } + + user, exists := store.users[username] + if !exists { + return nil, credential.ErrUserNotFound + } + + // Return a deep copy to avoid mutation issues + return store.deepCopyIdentity(user), nil +} + +func (store *MemoryStore) UpdateUser(ctx context.Context, username string, identity *iam_pb.Identity) error { + store.mu.Lock() + defer store.mu.Unlock() + + if !store.initialized { + return fmt.Errorf("store not initialized") + } + + existingUser, exists := store.users[username] + if !exists { + return credential.ErrUserNotFound + } + + // Remove old access keys from index + for _, cred := range existingUser.Credentials { + delete(store.accessKeys, cred.AccessKey) + } + + // Check for duplicate access keys (excluding current user) + for _, cred := range identity.Credentials { + if existingUsername, exists := store.accessKeys[cred.AccessKey]; exists && existingUsername != username { + return fmt.Errorf("access key %s already exists", cred.AccessKey) + } + } + + // Deep copy to avoid mutation issues + identityCopy := store.deepCopyIdentity(identity) + store.users[username] = identityCopy + + // Re-index access keys + for _, cred := range identity.Credentials { + store.accessKeys[cred.AccessKey] = username + } + + return nil +} + +func (store *MemoryStore) DeleteUser(ctx context.Context, username string) error { + store.mu.Lock() + defer store.mu.Unlock() + + if !store.initialized { + return fmt.Errorf("store not initialized") + } + + user, exists := store.users[username] + if !exists { + return credential.ErrUserNotFound + } + + // Remove access keys from index + for _, cred := range user.Credentials { + delete(store.accessKeys, cred.AccessKey) + } + + // Remove user + delete(store.users, username) + + return nil +} + +func (store *MemoryStore) ListUsers(ctx context.Context) ([]string, error) { + store.mu.RLock() + defer store.mu.RUnlock() + + if !store.initialized { + return nil, fmt.Errorf("store not initialized") + } + + var usernames []string + for username := range store.users { + usernames = append(usernames, username) + } + + return usernames, nil +} + +func (store *MemoryStore) GetUserByAccessKey(ctx context.Context, accessKey string) (*iam_pb.Identity, error) { + store.mu.RLock() + defer store.mu.RUnlock() + + if !store.initialized { + return nil, fmt.Errorf("store not initialized") + } + + username, exists := store.accessKeys[accessKey] + if !exists { + return nil, credential.ErrAccessKeyNotFound + } + + user, exists := store.users[username] + if !exists { + // This should not happen, but handle it gracefully + return nil, credential.ErrUserNotFound + } + + // Return a deep copy to avoid mutation issues + return store.deepCopyIdentity(user), nil +} + +func (store *MemoryStore) CreateAccessKey(ctx context.Context, username string, cred *iam_pb.Credential) error { + store.mu.Lock() + defer store.mu.Unlock() + + if !store.initialized { + return fmt.Errorf("store not initialized") + } + + user, exists := store.users[username] + if !exists { + return credential.ErrUserNotFound + } + + // Check if access key already exists + if _, exists := store.accessKeys[cred.AccessKey]; exists { + return fmt.Errorf("access key %s already exists", cred.AccessKey) + } + + // Add credential to user + user.Credentials = append(user.Credentials, &iam_pb.Credential{ + AccessKey: cred.AccessKey, + SecretKey: cred.SecretKey, + }) + + // Index the access key + store.accessKeys[cred.AccessKey] = username + + return nil +} + +func (store *MemoryStore) DeleteAccessKey(ctx context.Context, username string, accessKey string) error { + store.mu.Lock() + defer store.mu.Unlock() + + if !store.initialized { + return fmt.Errorf("store not initialized") + } + + user, exists := store.users[username] + if !exists { + return credential.ErrUserNotFound + } + + // Find and remove the credential + var newCredentials []*iam_pb.Credential + found := false + for _, cred := range user.Credentials { + if cred.AccessKey == accessKey { + found = true + // Remove from access key index + delete(store.accessKeys, accessKey) + } else { + newCredentials = append(newCredentials, cred) + } + } + + if !found { + return credential.ErrAccessKeyNotFound + } + + user.Credentials = newCredentials + return nil +} + +// deepCopyIdentity creates a deep copy of an identity to avoid mutation issues +func (store *MemoryStore) deepCopyIdentity(identity *iam_pb.Identity) *iam_pb.Identity { + if identity == nil { + return nil + } + + // Use JSON marshaling/unmarshaling for deep copy + // This is simple and safe for protobuf messages + data, err := json.Marshal(identity) + if err != nil { + // Fallback to shallow copy if JSON fails + return &iam_pb.Identity{ + Name: identity.Name, + Account: identity.Account, + Credentials: identity.Credentials, + Actions: identity.Actions, + } + } + + var copy iam_pb.Identity + if err := json.Unmarshal(data, ©); err != nil { + // Fallback to shallow copy if JSON fails + return &iam_pb.Identity{ + Name: identity.Name, + Account: identity.Account, + Credentials: identity.Credentials, + Actions: identity.Actions, + } + } + + return © +} diff --git a/weed/credential/memory/memory_policy.go b/weed/credential/memory/memory_policy.go new file mode 100644 index 000000000..1c9268958 --- /dev/null +++ b/weed/credential/memory/memory_policy.go @@ -0,0 +1,77 @@ +package memory + +import ( + "context" + "fmt" + + "github.com/seaweedfs/seaweedfs/weed/credential" +) + +// GetPolicies retrieves all IAM policies from memory +func (store *MemoryStore) GetPolicies(ctx context.Context) (map[string]credential.PolicyDocument, error) { + store.mu.RLock() + defer store.mu.RUnlock() + + if !store.initialized { + return nil, fmt.Errorf("store not initialized") + } + + // Create a copy of the policies map to avoid mutation issues + policies := make(map[string]credential.PolicyDocument) + for name, doc := range store.policies { + policies[name] = doc + } + + return policies, nil +} + +// GetPolicy retrieves a specific IAM policy by name from memory +func (store *MemoryStore) GetPolicy(ctx context.Context, name string) (*credential.PolicyDocument, error) { + store.mu.RLock() + defer store.mu.RUnlock() + + if policy, exists := store.policies[name]; exists { + return &policy, nil + } + + return nil, nil // Policy not found +} + +// CreatePolicy creates a new IAM policy in memory +func (store *MemoryStore) CreatePolicy(ctx context.Context, name string, document credential.PolicyDocument) error { + store.mu.Lock() + defer store.mu.Unlock() + + if !store.initialized { + return fmt.Errorf("store not initialized") + } + + store.policies[name] = document + return nil +} + +// UpdatePolicy updates an existing IAM policy in memory +func (store *MemoryStore) UpdatePolicy(ctx context.Context, name string, document credential.PolicyDocument) error { + store.mu.Lock() + defer store.mu.Unlock() + + if !store.initialized { + return fmt.Errorf("store not initialized") + } + + store.policies[name] = document + return nil +} + +// DeletePolicy deletes an IAM policy from memory +func (store *MemoryStore) DeletePolicy(ctx context.Context, name string) error { + store.mu.Lock() + defer store.mu.Unlock() + + if !store.initialized { + return fmt.Errorf("store not initialized") + } + + delete(store.policies, name) + return nil +} diff --git a/weed/credential/memory/memory_store.go b/weed/credential/memory/memory_store.go index e6117bf48..f0f383c04 100644 --- a/weed/credential/memory/memory_store.go +++ b/weed/credential/memory/memory_store.go @@ -1,9 +1,6 @@ package memory import ( - "context" - "encoding/json" - "fmt" "sync" "github.com/seaweedfs/seaweedfs/weed/credential" @@ -19,8 +16,9 @@ func init() { // This is primarily intended for testing purposes type MemoryStore struct { mu sync.RWMutex - users map[string]*iam_pb.Identity // username -> identity - accessKeys map[string]string // access_key -> username + users map[string]*iam_pb.Identity // username -> identity + accessKeys map[string]string // access_key -> username + policies map[string]credential.PolicyDocument // policy_name -> policy_document initialized bool } @@ -38,313 +36,22 @@ func (store *MemoryStore) Initialize(configuration util.Configuration, prefix st store.users = make(map[string]*iam_pb.Identity) store.accessKeys = make(map[string]string) + store.policies = make(map[string]credential.PolicyDocument) store.initialized = true return nil } -func (store *MemoryStore) LoadConfiguration(ctx context.Context) (*iam_pb.S3ApiConfiguration, error) { - store.mu.RLock() - defer store.mu.RUnlock() - - if !store.initialized { - return nil, fmt.Errorf("store not initialized") - } - - config := &iam_pb.S3ApiConfiguration{} - - // Convert all users to identities - for _, user := range store.users { - // Deep copy the identity to avoid mutation issues - identityCopy := store.deepCopyIdentity(user) - config.Identities = append(config.Identities, identityCopy) - } - - return config, nil -} - -func (store *MemoryStore) SaveConfiguration(ctx context.Context, config *iam_pb.S3ApiConfiguration) error { - store.mu.Lock() - defer store.mu.Unlock() - - if !store.initialized { - return fmt.Errorf("store not initialized") - } - - // Clear existing data - store.users = make(map[string]*iam_pb.Identity) - store.accessKeys = make(map[string]string) - - // Add all identities - for _, identity := range config.Identities { - // Deep copy to avoid mutation issues - identityCopy := store.deepCopyIdentity(identity) - store.users[identity.Name] = identityCopy - - // Index access keys - for _, credential := range identity.Credentials { - store.accessKeys[credential.AccessKey] = identity.Name - } - } - - return nil -} - -func (store *MemoryStore) CreateUser(ctx context.Context, identity *iam_pb.Identity) error { - store.mu.Lock() - defer store.mu.Unlock() - - if !store.initialized { - return fmt.Errorf("store not initialized") - } - - if _, exists := store.users[identity.Name]; exists { - return credential.ErrUserAlreadyExists - } - - // Check for duplicate access keys - for _, cred := range identity.Credentials { - if _, exists := store.accessKeys[cred.AccessKey]; exists { - return fmt.Errorf("access key %s already exists", cred.AccessKey) - } - } - - // Deep copy to avoid mutation issues - identityCopy := store.deepCopyIdentity(identity) - store.users[identity.Name] = identityCopy - - // Index access keys - for _, cred := range identity.Credentials { - store.accessKeys[cred.AccessKey] = identity.Name - } - - return nil -} - -func (store *MemoryStore) GetUser(ctx context.Context, username string) (*iam_pb.Identity, error) { - store.mu.RLock() - defer store.mu.RUnlock() - - if !store.initialized { - return nil, fmt.Errorf("store not initialized") - } - - user, exists := store.users[username] - if !exists { - return nil, credential.ErrUserNotFound - } - - // Return a deep copy to avoid mutation issues - return store.deepCopyIdentity(user), nil -} - -func (store *MemoryStore) UpdateUser(ctx context.Context, username string, identity *iam_pb.Identity) error { - store.mu.Lock() - defer store.mu.Unlock() - - if !store.initialized { - return fmt.Errorf("store not initialized") - } - - existingUser, exists := store.users[username] - if !exists { - return credential.ErrUserNotFound - } - - // Remove old access keys from index - for _, cred := range existingUser.Credentials { - delete(store.accessKeys, cred.AccessKey) - } - - // Check for duplicate access keys (excluding current user) - for _, cred := range identity.Credentials { - if existingUsername, exists := store.accessKeys[cred.AccessKey]; exists && existingUsername != username { - return fmt.Errorf("access key %s already exists", cred.AccessKey) - } - } - - // Deep copy to avoid mutation issues - identityCopy := store.deepCopyIdentity(identity) - store.users[username] = identityCopy - - // Re-index access keys - for _, cred := range identity.Credentials { - store.accessKeys[cred.AccessKey] = username - } - - return nil -} - -func (store *MemoryStore) DeleteUser(ctx context.Context, username string) error { - store.mu.Lock() - defer store.mu.Unlock() - - if !store.initialized { - return fmt.Errorf("store not initialized") - } - - user, exists := store.users[username] - if !exists { - return credential.ErrUserNotFound - } - - // Remove access keys from index - for _, cred := range user.Credentials { - delete(store.accessKeys, cred.AccessKey) - } - - // Remove user - delete(store.users, username) - - return nil -} - -func (store *MemoryStore) ListUsers(ctx context.Context) ([]string, error) { - store.mu.RLock() - defer store.mu.RUnlock() - - if !store.initialized { - return nil, fmt.Errorf("store not initialized") - } - - var usernames []string - for username := range store.users { - usernames = append(usernames, username) - } - - return usernames, nil -} - -func (store *MemoryStore) GetUserByAccessKey(ctx context.Context, accessKey string) (*iam_pb.Identity, error) { - store.mu.RLock() - defer store.mu.RUnlock() - - if !store.initialized { - return nil, fmt.Errorf("store not initialized") - } - - username, exists := store.accessKeys[accessKey] - if !exists { - return nil, credential.ErrAccessKeyNotFound - } - - user, exists := store.users[username] - if !exists { - // This should not happen, but handle it gracefully - return nil, credential.ErrUserNotFound - } - - // Return a deep copy to avoid mutation issues - return store.deepCopyIdentity(user), nil -} - -func (store *MemoryStore) CreateAccessKey(ctx context.Context, username string, cred *iam_pb.Credential) error { - store.mu.Lock() - defer store.mu.Unlock() - - if !store.initialized { - return fmt.Errorf("store not initialized") - } - - user, exists := store.users[username] - if !exists { - return credential.ErrUserNotFound - } - - // Check if access key already exists - if _, exists := store.accessKeys[cred.AccessKey]; exists { - return fmt.Errorf("access key %s already exists", cred.AccessKey) - } - - // Add credential to user - user.Credentials = append(user.Credentials, &iam_pb.Credential{ - AccessKey: cred.AccessKey, - SecretKey: cred.SecretKey, - }) - - // Index the access key - store.accessKeys[cred.AccessKey] = username - - return nil -} - -func (store *MemoryStore) DeleteAccessKey(ctx context.Context, username string, accessKey string) error { - store.mu.Lock() - defer store.mu.Unlock() - - if !store.initialized { - return fmt.Errorf("store not initialized") - } - - user, exists := store.users[username] - if !exists { - return credential.ErrUserNotFound - } - - // Find and remove the credential - var newCredentials []*iam_pb.Credential - found := false - for _, cred := range user.Credentials { - if cred.AccessKey == accessKey { - found = true - // Remove from access key index - delete(store.accessKeys, accessKey) - } else { - newCredentials = append(newCredentials, cred) - } - } - - if !found { - return credential.ErrAccessKeyNotFound - } - - user.Credentials = newCredentials - return nil -} - func (store *MemoryStore) Shutdown() { store.mu.Lock() defer store.mu.Unlock() - // Clear all data store.users = nil store.accessKeys = nil + store.policies = nil store.initialized = false } -// deepCopyIdentity creates a deep copy of an identity to avoid mutation issues -func (store *MemoryStore) deepCopyIdentity(identity *iam_pb.Identity) *iam_pb.Identity { - if identity == nil { - return nil - } - - // Use JSON marshaling/unmarshaling for deep copy - // This is simple and safe for protobuf messages - data, err := json.Marshal(identity) - if err != nil { - // Fallback to shallow copy if JSON fails - return &iam_pb.Identity{ - Name: identity.Name, - Account: identity.Account, - Credentials: identity.Credentials, - Actions: identity.Actions, - } - } - - var copy iam_pb.Identity - if err := json.Unmarshal(data, ©); err != nil { - // Fallback to shallow copy if JSON fails - return &iam_pb.Identity{ - Name: identity.Name, - Account: identity.Account, - Credentials: identity.Credentials, - Actions: identity.Actions, - } - } - - return © -} - // Reset clears all data in the store (useful for testing) func (store *MemoryStore) Reset() { store.mu.Lock() |
