s3api: add AttachUserPolicy/DetachUserPolicy/ListAttachedUserPolicies (#8379)

* iam: add XML responses for managed user policy APIs

* s3api: implement attach/detach/list attached user policies

* s3api: add embedded IAM tests for managed user policies

* iam: update CredentialStore interface and Manager for managed policies

Updated the `CredentialStore` interface to include `AttachUserPolicy`,
`DetachUserPolicy`, and `ListAttachedUserPolicies` methods.
The `CredentialManager` was updated to delegate these calls to the store.
Added common error variables for policy management.

* iam: implement managed policy methods in MemoryStore

Implemented `AttachUserPolicy`, `DetachUserPolicy`, and
`ListAttachedUserPolicies` in the MemoryStore.
Also ensured deep copying of identities includes PolicyNames.

* iam: implement managed policy methods in PostgresStore

Modified Postgres schema to include `policy_names` JSONB column in `users`.
Implemented `AttachUserPolicy`, `DetachUserPolicy`, and `ListAttachedUserPolicies`.
Updated user CRUD operations to handle policy names persistence.

* iam: implement managed policy methods in remaining stores

Implemented user policy management in:
- `FilerEtcStore` (partial implementation)
- `IamGrpcStore` (delegated via GetUser/UpdateUser)
- `PropagatingCredentialStore` (to broadcast updates)
Ensures cluster-wide consistency for policy attachments.

* s3api: refactor EmbeddedIamApi to use managed policy APIs

- Refactored `AttachUserPolicy`, `DetachUserPolicy`, and `ListAttachedUserPolicies`
  to use `e.credentialManager` directly.
- Fixed a critical error suppression bug in `ExecuteAction` that always
  returned success even on failure.
- Implemented robust error matching using string comparison fallbacks.
- Improved consistency by reloading configuration after policy changes.

* s3api: update and refine IAM integration tests

- Updated tests to use a real `MemoryStore`-backed `CredentialManager`.
- Refined test configuration synchronization using `sync.Once` and
  manual deep-copying to prevent state corruption.
- Improved `extractEmbeddedIamErrorCodeAndMessage` to handle more XML
  formats robustly.
- Adjusted test expectations to match current AWS IAM behavior.

* fix compilation

* visibility

* ensure 10 policies

* reload

* add integration tests

* Guard raft command registration

* Allow IAM actions in policy tests

* Validate gRPC policy attachments

* Revert Validate gRPC policy attachments

* Tighten gRPC policy attach/detach

* Improve IAM managed policy handling

* Improve managed policy filters
This commit is contained in:
Chris Lu
2026-02-19 12:26:27 -08:00
committed by GitHub
parent 6787dccace
commit 7b8df39cf7
13 changed files with 1153 additions and 232 deletions

View File

@@ -3,6 +3,7 @@ package grpc
import (
"context"
"github.com/seaweedfs/seaweedfs/weed/credential"
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
)
@@ -118,3 +119,66 @@ func (store *IamGrpcStore) DeleteAccessKey(ctx context.Context, username string,
return err
})
}
// AttachUserPolicy attaches a managed policy to a user by policy name
func (store *IamGrpcStore) AttachUserPolicy(ctx context.Context, username string, policyName string) error {
// Get current user
identity, err := store.GetUser(ctx, username)
if err != nil {
return err
}
// Verify policy exists
policy, err := store.GetPolicy(ctx, policyName)
if err != nil {
return err
}
if policy == nil {
return credential.ErrPolicyNotFound
}
// Check if already attached
for _, p := range identity.PolicyNames {
if p == policyName {
// Already attached - return success (idempotent)
return nil
}
}
identity.PolicyNames = append(identity.PolicyNames, policyName)
return store.UpdateUser(ctx, username, identity)
}
// DetachUserPolicy detaches a managed policy from a user
func (store *IamGrpcStore) DetachUserPolicy(ctx context.Context, username string, policyName string) error {
identity, err := store.GetUser(ctx, username)
if err != nil {
return err
}
found := false
var newPolicies []string
for _, p := range identity.PolicyNames {
if p == policyName {
found = true
} else {
newPolicies = append(newPolicies, p)
}
}
if !found {
return credential.ErrPolicyNotAttached
}
identity.PolicyNames = newPolicies
return store.UpdateUser(ctx, username, identity)
}
// ListAttachedUserPolicies returns the list of policy names attached to a user
func (store *IamGrpcStore) ListAttachedUserPolicies(ctx context.Context, username string) ([]string, error) {
identity, err := store.GetUser(ctx, username)
if err != nil {
return nil, err
}
return identity.PolicyNames, nil
}