* fix(s3): include static identities in listing operations Static identities loaded from -s3.config file were only stored in the S3 API server's in-memory state. Listing operations (s3.configure shell command, aws iam list-users) queried the credential manager which only returned dynamic identities from the backend store. Register static identities with the credential manager after loading so they are included in LoadConfiguration and ListUsers results, and filtered out before SaveConfiguration to avoid persisting them to the dynamic store. Fixes https://github.com/seaweedfs/seaweedfs/discussions/8896 * fix: avoid mutating caller's config and defensive copies - SaveConfiguration: use shallow struct copy instead of mutating the caller's config.Identities field - SetStaticIdentities: skip nil entries to avoid panics - GetStaticIdentities: defensively copy PolicyNames slice to avoid aliasing the original * fix: filter nil static identities and sync on config reload - SetStaticIdentities: filter nil entries from the stored slice (not just from staticNames) to prevent panics in LoadConfiguration/ListUsers - Extract updateCredentialManagerStaticIdentities helper and call it from both startup and the grace.OnReload handler so the credential manager's static snapshot stays current after config file reloads * fix: add mutex for static identity fields and fix ListUsers for store callers - Add sync.RWMutex to protect staticIdentities/staticNames against concurrent reads during config reload - Revert CredentialManager.ListUsers to return only store users, since internal callers (e.g. DeletePolicy) look up each user in the store and fail on non-existent static entries - Merge static usernames in the filer gRPC ListUsers handler instead, via the new GetStaticUsernames method - Fix CI: TestIAMPolicyManagement/managed_policy_crud_lifecycle was failing because DeletePolicy iterated static users that don't exist in the store * fix: show static identities in admin UI and weed shell The admin UI and weed shell s3.configure command query the filer's credential manager via gRPC, which is a separate instance from the S3 server's credential manager. Static identities were only registered on the S3 server's credential manager, so they never appeared in the filer's responses. - Add CredentialManager.LoadS3ConfigFile to parse a static S3 config file and register its identities - Add FilerOptions.s3ConfigFile so the filer can load the same static config that the S3 server uses - Wire s3ConfigFile through in weed mini and weed server modes - Merge static usernames in filer gRPC ListUsers handler - Add CredentialManager.GetStaticUsernames helper - Add sync.RWMutex to protect concurrent access to static identity fields - Avoid importing weed/filer from weed/credential (which pulled in filer store init() registrations and broke test isolation) - Add docker/compose/s3_static_users_example.json * fix(admin): make static users read-only in admin UI Static users loaded from the -s3.config file should not be editable or deletable through the admin UI since they are managed via the config file. - Add IsStatic field to ObjectStoreUser, set from credential manager - Hide edit, delete, and access key buttons for static users in the users table template - Show a "static" badge next to static user names - Return 403 Forbidden from UpdateUser and DeleteUser API handlers when the target user is a static identity * fix(admin): show details for static users GetObjectStoreUserDetails called credentialManager.GetUser which only queries the dynamic store. For static users this returned ErrUserNotFound. Fall back to GetStaticIdentity when the store lookup fails. * fix(admin): load static S3 identities in admin server The admin server has its own credential manager (gRPC store) which is a separate instance from the S3 server's and filer's. It had no static identity data, so IsStaticIdentity returned false (edit/delete buttons shown) and GetStaticIdentity returned nil (details page failed). Pass the -s3.config file path through to the admin server and call LoadS3ConfigFile on its credential manager, matching the approach used for the filer. * fix: use protobuf is_static field instead of passing config file path The previous approach passed -s3.config file path to every component (filer, admin). This is wrong because the admin server should not need to know about S3 config files. Instead, add an is_static field to the Identity protobuf message. The field is set when static identities are serialized (in GetStaticIdentities and LoadS3ConfigFile). Any gRPC client that loads configuration via GetConfiguration automatically sees which identities are static, without needing the config file. - Add is_static field (tag 8) to iam_pb.Identity proto message - Set IsStatic=true in GetStaticIdentities and LoadS3ConfigFile - Admin GetObjectStoreUsers reads identity.IsStatic from proto - Admin IsStaticUser helper loads config via gRPC to check the flag - Filer GetUser gRPC handler falls back to GetStaticIdentity - Remove s3ConfigFile from AdminOptions and NewAdminServer signature
335 lines
7.9 KiB
Protocol Buffer
335 lines
7.9 KiB
Protocol Buffer
syntax = "proto3";
|
|
|
|
package iam_pb;
|
|
|
|
option go_package = "github.com/seaweedfs/seaweedfs/weed/pb/iam_pb";
|
|
option java_package = "seaweedfs.client";
|
|
option java_outer_classname = "IamProto";
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
service SeaweedIdentityAccessManagement {
|
|
// Configuration Management
|
|
rpc GetConfiguration (GetConfigurationRequest) returns (GetConfigurationResponse);
|
|
rpc PutConfiguration (PutConfigurationRequest) returns (PutConfigurationResponse);
|
|
|
|
// User Management
|
|
rpc CreateUser (CreateUserRequest) returns (CreateUserResponse);
|
|
rpc GetUser (GetUserRequest) returns (GetUserResponse);
|
|
rpc UpdateUser (UpdateUserRequest) returns (UpdateUserResponse);
|
|
rpc DeleteUser (DeleteUserRequest) returns (DeleteUserResponse);
|
|
rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
|
|
|
|
// Access Key Management
|
|
rpc CreateAccessKey (CreateAccessKeyRequest) returns (CreateAccessKeyResponse);
|
|
rpc DeleteAccessKey (DeleteAccessKeyRequest) returns (DeleteAccessKeyResponse);
|
|
rpc GetUserByAccessKey (GetUserByAccessKeyRequest) returns (GetUserByAccessKeyResponse);
|
|
|
|
// Policy Management
|
|
rpc PutPolicy (PutPolicyRequest) returns (PutPolicyResponse);
|
|
rpc GetPolicy (GetPolicyRequest) returns (GetPolicyResponse);
|
|
rpc ListPolicies (ListPoliciesRequest) returns (ListPoliciesResponse);
|
|
rpc DeletePolicy (DeletePolicyRequest) returns (DeletePolicyResponse);
|
|
|
|
// Service Account Management
|
|
rpc CreateServiceAccount (CreateServiceAccountRequest) returns (CreateServiceAccountResponse);
|
|
rpc UpdateServiceAccount (UpdateServiceAccountRequest) returns (UpdateServiceAccountResponse);
|
|
rpc DeleteServiceAccount (DeleteServiceAccountRequest) returns (DeleteServiceAccountResponse);
|
|
rpc GetServiceAccount (GetServiceAccountRequest) returns (GetServiceAccountResponse);
|
|
rpc ListServiceAccounts (ListServiceAccountsRequest) returns (ListServiceAccountsResponse);
|
|
rpc GetServiceAccountByAccessKey (GetServiceAccountByAccessKeyRequest) returns (GetServiceAccountByAccessKeyResponse);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
// Configuration Management Messages
|
|
|
|
message GetConfigurationRequest {
|
|
}
|
|
|
|
message GetConfigurationResponse {
|
|
S3ApiConfiguration configuration = 1;
|
|
}
|
|
|
|
message PutConfigurationRequest {
|
|
S3ApiConfiguration configuration = 1;
|
|
}
|
|
|
|
message PutConfigurationResponse {
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
// User Management Messages
|
|
|
|
message CreateUserRequest {
|
|
Identity identity = 1;
|
|
}
|
|
|
|
message CreateUserResponse {
|
|
}
|
|
|
|
message GetUserRequest {
|
|
string username = 1;
|
|
}
|
|
|
|
message GetUserResponse {
|
|
Identity identity = 1;
|
|
}
|
|
|
|
message UpdateUserRequest {
|
|
string username = 1;
|
|
Identity identity = 2;
|
|
}
|
|
|
|
message UpdateUserResponse {
|
|
}
|
|
|
|
message DeleteUserRequest {
|
|
string username = 1;
|
|
}
|
|
|
|
message DeleteUserResponse {
|
|
}
|
|
|
|
message ListUsersRequest {
|
|
}
|
|
|
|
message ListUsersResponse {
|
|
repeated string usernames = 1;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
// Access Key Management Messages
|
|
|
|
message CreateAccessKeyRequest {
|
|
string username = 1;
|
|
Credential credential = 2;
|
|
}
|
|
|
|
message CreateAccessKeyResponse {
|
|
}
|
|
|
|
message DeleteAccessKeyRequest {
|
|
string username = 1;
|
|
string access_key = 2;
|
|
}
|
|
|
|
message DeleteAccessKeyResponse {
|
|
}
|
|
|
|
message GetUserByAccessKeyRequest {
|
|
string access_key = 1;
|
|
}
|
|
|
|
message GetUserByAccessKeyResponse {
|
|
Identity identity = 1;
|
|
}
|
|
|
|
message ListAccessKeysRequest {
|
|
string username = 1;
|
|
}
|
|
|
|
message ListAccessKeysResponse {
|
|
repeated Credential access_keys = 1;
|
|
}
|
|
|
|
// User Policy Management Messages
|
|
message PutUserPolicyRequest {
|
|
string username = 1;
|
|
string policy_name = 2;
|
|
string policy_document = 3;
|
|
}
|
|
|
|
message PutUserPolicyResponse {
|
|
}
|
|
|
|
message GetUserPolicyRequest {
|
|
string username = 1;
|
|
string policy_name = 2;
|
|
}
|
|
|
|
message GetUserPolicyResponse {
|
|
string username = 1;
|
|
string policy_name = 2;
|
|
string policy_document = 3;
|
|
}
|
|
|
|
message DeleteUserPolicyRequest {
|
|
string username = 1;
|
|
string policy_name = 2;
|
|
}
|
|
|
|
message DeleteUserPolicyResponse {
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
message S3ApiConfiguration {
|
|
repeated Identity identities = 1;
|
|
repeated Account accounts = 2;
|
|
repeated ServiceAccount service_accounts = 3;
|
|
repeated Policy policies = 4;
|
|
repeated Group groups = 5;
|
|
}
|
|
|
|
message Group {
|
|
string name = 1;
|
|
repeated string members = 2; // usernames
|
|
repeated string policy_names = 3; // attached managed policy names
|
|
bool disabled = 4;
|
|
}
|
|
|
|
message Identity {
|
|
string name = 1;
|
|
repeated Credential credentials = 2;
|
|
repeated string actions = 3;
|
|
Account account = 4;
|
|
bool disabled = 5; // User status: false = enabled (default), true = disabled
|
|
repeated string service_account_ids = 6; // IDs of service accounts owned by this user
|
|
repeated string policy_names = 7;
|
|
bool is_static = 8; // Loaded from static config file (read-only, not editable via API)
|
|
}
|
|
|
|
message Credential {
|
|
string access_key = 1;
|
|
string secret_key = 2;
|
|
string status = 3; // Access key status: "Active" or "Inactive"
|
|
}
|
|
|
|
message Account {
|
|
string id = 1;
|
|
string display_name = 2;
|
|
string email_address = 3;
|
|
}
|
|
|
|
// ServiceAccount represents a service account - special credentials for applications.
|
|
// Service accounts are linked to a parent user and can have restricted permissions.
|
|
message ServiceAccount {
|
|
string id = 1; // Unique identifier (e.g., "sa-xxxxx")
|
|
string parent_user = 2; // Parent identity name
|
|
string description = 3; // Optional description
|
|
Credential credential = 4; // Access key/secret for this service account
|
|
repeated string actions = 5; // Allowed actions (subset of parent)
|
|
int64 expiration = 6; // Unix timestamp, 0 = no expiration
|
|
bool disabled = 7; // Status: false = enabled (default)
|
|
int64 created_at = 8; // Creation timestamp
|
|
string created_by = 9; // Who created this service account
|
|
}
|
|
|
|
message PutPolicyRequest {
|
|
string name = 1;
|
|
string content = 2;
|
|
}
|
|
|
|
message PutPolicyResponse {
|
|
}
|
|
|
|
message GetPolicyRequest {
|
|
string name = 1;
|
|
}
|
|
|
|
message GetPolicyResponse {
|
|
string name = 1;
|
|
string content = 2;
|
|
}
|
|
|
|
message ListPoliciesRequest {
|
|
}
|
|
|
|
message ListPoliciesResponse {
|
|
repeated Policy policies = 1;
|
|
}
|
|
|
|
message DeletePolicyRequest {
|
|
string name = 1;
|
|
}
|
|
|
|
message DeletePolicyResponse {
|
|
}
|
|
|
|
message Policy {
|
|
string name = 1;
|
|
string content = 2; // JSON content of the policy
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
// Service Account Messages
|
|
|
|
message CreateServiceAccountRequest {
|
|
ServiceAccount service_account = 1;
|
|
}
|
|
|
|
message CreateServiceAccountResponse {
|
|
}
|
|
|
|
message UpdateServiceAccountRequest {
|
|
string id = 1;
|
|
ServiceAccount service_account = 2;
|
|
}
|
|
|
|
message UpdateServiceAccountResponse {
|
|
}
|
|
|
|
message DeleteServiceAccountRequest {
|
|
string id = 1;
|
|
}
|
|
|
|
message DeleteServiceAccountResponse {
|
|
}
|
|
|
|
message GetServiceAccountRequest {
|
|
string id = 1;
|
|
}
|
|
|
|
message GetServiceAccountResponse {
|
|
ServiceAccount service_account = 1;
|
|
}
|
|
|
|
message ListServiceAccountsRequest {
|
|
}
|
|
|
|
message ListServiceAccountsResponse {
|
|
repeated ServiceAccount service_accounts = 1;
|
|
}
|
|
|
|
message GetServiceAccountByAccessKeyRequest {
|
|
string access_key = 1;
|
|
}
|
|
|
|
message GetServiceAccountByAccessKeyResponse {
|
|
ServiceAccount service_account = 1;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
// S3 IAM Cache Management
|
|
// Designed for unidirectional propagation from Filer to S3 Servers
|
|
|
|
|
|
message PutIdentityRequest {
|
|
Identity identity = 1;
|
|
}
|
|
|
|
message PutIdentityResponse {
|
|
}
|
|
|
|
message RemoveIdentityRequest {
|
|
string username = 1;
|
|
}
|
|
|
|
message RemoveIdentityResponse {
|
|
}
|
|
|
|
message PutGroupRequest {
|
|
Group group = 1;
|
|
}
|
|
|
|
message PutGroupResponse {
|
|
}
|
|
|
|
message RemoveGroupRequest {
|
|
string group_name = 1;
|
|
}
|
|
|
|
message RemoveGroupResponse {
|
|
}
|