mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-17 20:10:51 +02:00
* fix(s3): preserve exact policy document in embedded IAM PutUserPolicy/GetUserPolicy (#9008) The embedded IAM implementation (used when IAM requests go through the S3 gateway) discarded the original policy document on PutUserPolicy, storing only the lossy ident.Actions representation. GetUserPolicy then reconstructed the document from these coarse-grained actions, producing wildcard-expanded actions (s3:GetObject → s3:Get*), duplicates, and collapsed resources (array → single string). PR #9009 fixed this in the standalone IAM server (weed/iamapi/) but the embedded IAM (weed/s3api/) — which is the code path most users hit — had the same bugs. Changes: - Add InlinePolicyStore optional interface to credential store, with implementations for FilerEtcStore (uses existing PoliciesCollection), MemoryStore, and PropagatingCredentialStore. - Embedded IAM PutUserPolicy now persists the original policy document via CredentialManager.PutUserInlinePolicy for lossless round-trips. - Embedded IAM GetUserPolicy first tries the stored inline policy; only falls back to lossy reconstruction from ident.Actions when no stored document exists (e.g. policies created before this fix). - Fix the fallback reconstruction: add action deduplication and preserve resource paths verbatim (no more spurious /* appending). - Update DeleteUserPolicy/ListUserPolicies to use stored inline policies. * fix(s3): address PR review feedback for embedded IAM inline policies - Validate PolicyName is non-empty in PutUserPolicy and DeleteUserPolicy - Add recomputeActions() to aggregate ident.Actions from ALL stored inline policies on put/delete, fixing the issue where a second PutUserPolicy would overwrite the first policy's enforcement - Log errors from GetUserInlinePolicy in the GetUserPolicy fallback instead of silently ignoring them - Add initialization guards to MemoryStore GetUserInlinePolicy and ListUserInlinePolicies for consistency with other read methods * fix(s3): make inline policy persistence fatal and propagate recompute errors Address second round of review feedback: - recomputeActions() now returns ([]string, error) so callers can distinguish store failures from "no stored policies" and abort the mutation on transient errors instead of silently falling back. - PutUserInlinePolicy and DeleteUserInlinePolicy failures are now fatal: the API call returns ServiceFailure instead of logging and continuing, keeping ident.Actions and stored policy state in sync. * chore: gofmt weed/s3api/iceberg/handlers_oauth.go Pre-existing formatting issue from #9017; fixes S3 Tables Format Check CI.
153 lines
6.7 KiB
Go
153 lines
6.7 KiB
Go
package credential
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine"
|
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
|
)
|
|
|
|
var (
|
|
ErrUserNotFound = errors.New("user not found")
|
|
ErrUserAlreadyExists = errors.New("user already exists")
|
|
ErrAccessKeyNotFound = errors.New("access key not found")
|
|
ErrServiceAccountNotFound = errors.New("service account not found")
|
|
ErrPolicyNotFound = errors.New("policy not found")
|
|
ErrPolicyAlreadyAttached = errors.New("policy already attached")
|
|
ErrPolicyNotAttached = errors.New("policy not attached to user")
|
|
ErrGroupNotFound = errors.New("group not found")
|
|
ErrGroupAlreadyExists = errors.New("group already exists")
|
|
ErrGroupNotEmpty = errors.New("group is not empty")
|
|
ErrUserNotInGroup = errors.New("user is not a member of the group")
|
|
)
|
|
|
|
// CredentialStoreTypeName represents the type name of a credential store
|
|
type CredentialStoreTypeName string
|
|
|
|
// Credential store name constants
|
|
const (
|
|
StoreTypeMemory CredentialStoreTypeName = "memory"
|
|
StoreTypeFilerEtc CredentialStoreTypeName = "filer_etc"
|
|
StoreTypePostgres CredentialStoreTypeName = "postgres"
|
|
StoreTypeGrpc CredentialStoreTypeName = "grpc"
|
|
)
|
|
|
|
// CredentialStore defines the interface for user credential storage and retrieval
|
|
type CredentialStore interface {
|
|
// GetName returns the name of the credential store implementation
|
|
GetName() CredentialStoreTypeName
|
|
|
|
// Initialize initializes the credential store with configuration
|
|
Initialize(configuration util.Configuration, prefix string) error
|
|
|
|
// LoadConfiguration loads the entire S3 API configuration
|
|
LoadConfiguration(ctx context.Context) (*iam_pb.S3ApiConfiguration, error)
|
|
|
|
// SaveConfiguration saves the entire S3 API configuration
|
|
SaveConfiguration(ctx context.Context, config *iam_pb.S3ApiConfiguration) error
|
|
|
|
// CreateUser creates a new user with the given identity
|
|
CreateUser(ctx context.Context, identity *iam_pb.Identity) error
|
|
|
|
// GetUser retrieves a user by username
|
|
GetUser(ctx context.Context, username string) (*iam_pb.Identity, error)
|
|
|
|
// UpdateUser updates an existing user
|
|
UpdateUser(ctx context.Context, username string, identity *iam_pb.Identity) error
|
|
|
|
// DeleteUser removes a user by username
|
|
DeleteUser(ctx context.Context, username string) error
|
|
|
|
// ListUsers returns all usernames
|
|
ListUsers(ctx context.Context) ([]string, error)
|
|
|
|
// GetUserByAccessKey retrieves a user by access key
|
|
GetUserByAccessKey(ctx context.Context, accessKey string) (*iam_pb.Identity, error)
|
|
|
|
// CreateAccessKey creates a new access key for a user
|
|
CreateAccessKey(ctx context.Context, username string, credential *iam_pb.Credential) error
|
|
|
|
// DeleteAccessKey removes an access key for a user
|
|
DeleteAccessKey(ctx context.Context, username string, accessKey string) error
|
|
|
|
// Policy Management
|
|
GetPolicies(ctx context.Context) (map[string]policy_engine.PolicyDocument, error)
|
|
// ListPolicyNames returns the names of all policies
|
|
ListPolicyNames(ctx context.Context) ([]string, error)
|
|
// PutPolicy creates or replaces a policy document.
|
|
PutPolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error
|
|
DeletePolicy(ctx context.Context, name string) error
|
|
GetPolicy(ctx context.Context, name string) (*policy_engine.PolicyDocument, error)
|
|
|
|
// Service Account Management
|
|
CreateServiceAccount(ctx context.Context, sa *iam_pb.ServiceAccount) error
|
|
UpdateServiceAccount(ctx context.Context, id string, sa *iam_pb.ServiceAccount) error
|
|
DeleteServiceAccount(ctx context.Context, id string) error
|
|
GetServiceAccount(ctx context.Context, id string) (*iam_pb.ServiceAccount, error)
|
|
ListServiceAccounts(ctx context.Context) ([]*iam_pb.ServiceAccount, error)
|
|
GetServiceAccountByAccessKey(ctx context.Context, accessKey string) (*iam_pb.ServiceAccount, error)
|
|
|
|
// User Policy Attachment Management
|
|
// AttachUserPolicy attaches a managed policy to a user by policy name
|
|
AttachUserPolicy(ctx context.Context, username string, policyName string) error
|
|
// DetachUserPolicy detaches a managed policy from a user
|
|
DetachUserPolicy(ctx context.Context, username string, policyName string) error
|
|
// ListAttachedUserPolicies returns the list of policy names attached to a user
|
|
ListAttachedUserPolicies(ctx context.Context, username string) ([]string, error)
|
|
|
|
// Group Management
|
|
CreateGroup(ctx context.Context, group *iam_pb.Group) error
|
|
GetGroup(ctx context.Context, groupName string) (*iam_pb.Group, error)
|
|
DeleteGroup(ctx context.Context, groupName string) error
|
|
ListGroups(ctx context.Context) ([]string, error)
|
|
UpdateGroup(ctx context.Context, group *iam_pb.Group) error
|
|
|
|
// Shutdown performs cleanup when the store is being shut down
|
|
Shutdown()
|
|
}
|
|
|
|
// AccessKeyInfo represents access key information with metadata
|
|
type AccessKeyInfo struct {
|
|
AccessKey string `json:"accessKey"`
|
|
SecretKey string `json:"secretKey"`
|
|
Username string `json:"username"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
// UserCredentials represents a user's credentials and metadata
|
|
type UserCredentials struct {
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
Account *iam_pb.Account `json:"account,omitempty"`
|
|
Credentials []*iam_pb.Credential `json:"credentials"`
|
|
Actions []string `json:"actions"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
UpdatedAt time.Time `json:"updatedAt"`
|
|
}
|
|
|
|
// PolicyManager interface for managing IAM policies
|
|
type PolicyManager interface {
|
|
GetPolicies(ctx context.Context) (map[string]policy_engine.PolicyDocument, error)
|
|
CreatePolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error
|
|
UpdatePolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error
|
|
DeletePolicy(ctx context.Context, name string) error
|
|
GetPolicy(ctx context.Context, name string) (*policy_engine.PolicyDocument, error)
|
|
}
|
|
|
|
// InlinePolicyStore is an optional interface for credential stores that support
|
|
// per-user inline policy storage. Stores that implement this interface preserve
|
|
// the exact policy document submitted via PutUserPolicy, enabling lossless
|
|
// round-trips through GetUserPolicy.
|
|
type InlinePolicyStore interface {
|
|
PutUserInlinePolicy(ctx context.Context, userName, policyName string, document policy_engine.PolicyDocument) error
|
|
GetUserInlinePolicy(ctx context.Context, userName, policyName string) (*policy_engine.PolicyDocument, error)
|
|
DeleteUserInlinePolicy(ctx context.Context, userName, policyName string) error
|
|
ListUserInlinePolicies(ctx context.Context, userName string) ([]string, error)
|
|
}
|
|
|
|
// Stores holds all available credential store implementations
|
|
var Stores []CredentialStore
|