pub struct AuthStore { /* private fields */ }Expand description
Central in-process authority for auth state.
All mutations are guarded by RwLocks so the store is Send + Sync.
Implementations§
Source§impl AuthStore
impl AuthStore
pub fn new(config: AuthConfig) -> Self
Sourcepub fn with_vault(
config: AuthConfig,
pager: Arc<Pager>,
passphrase: Option<&str>,
) -> Result<Self, AuthError>
pub fn with_vault( config: AuthConfig, pager: Arc<Pager>, passphrase: Option<&str>, ) -> Result<Self, AuthError>
Create an AuthStore backed by encrypted vault pages inside the
main .rdb database file.
If vault pages already exist, their contents are loaded and restored into the in-memory store. All subsequent mutations are automatically persisted to the vault pages via the pager.
pub fn config(&self) -> &AuthConfig
pub fn is_enabled(&self) -> bool
Sourcepub fn needs_bootstrap(&self) -> bool
pub fn needs_bootstrap(&self) -> bool
Returns true when no users exist yet and bootstrap hasn’t been sealed.
Sourcepub fn is_bootstrapped(&self) -> bool
pub fn is_bootstrapped(&self) -> bool
Whether bootstrap has already been performed (sealed).
Sourcepub fn bootstrap(
&self,
username: &str,
password: &str,
) -> Result<BootstrapResult, AuthError>
pub fn bootstrap( &self, username: &str, password: &str, ) -> Result<BootstrapResult, AuthError>
Bootstrap the first admin user. One-shot, irreversible.
Uses an atomic compare-exchange to guarantee that even under concurrent calls, only the first one succeeds. Once sealed, all subsequent calls fail immediately – there is no undo.
When a vault/pager is configured, a certificate-based keypair is
generated and the vault is re-encrypted with the certificate-derived
key. The certificate hex string is returned in BootstrapResult
so the admin can save it.
Sourcepub fn bootstrap_from_env(&self) -> Option<BootstrapResult>
pub fn bootstrap_from_env(&self) -> Option<BootstrapResult>
Auto-bootstrap from environment variables if no users exist.
Checks REDDB_USERNAME and REDDB_PASSWORD. If both are set and
the user store is empty, creates the first admin user automatically.
This mirrors the Docker pattern (MYSQL_ROOT_PASSWORD, etc.).
Returns Some(BootstrapResult) if bootstrapped, None if skipped.
Sourcepub fn is_vault_backed(&self) -> bool
pub fn is_vault_backed(&self) -> bool
True when this store has an encrypted vault and pager wired in.
Sourcepub fn vault_kv_get(&self, key: &str) -> Option<String>
pub fn vault_kv_get(&self, key: &str) -> Option<String>
Read a value from the vault KV store. Returns None if not set.
Sourcepub fn vault_kv_snapshot(&self) -> HashMap<String, String>
pub fn vault_kv_snapshot(&self) -> HashMap<String, String>
Snapshot vault KV values for statement-local secret resolution.
Sourcepub fn vault_kv_export_encrypted(&self) -> Result<Option<String>, AuthError>
pub fn vault_kv_export_encrypted(&self) -> Result<Option<String>, AuthError>
Export vault KV as an encrypted logical blob for JSONL dump/restore.
Returns None when the vault has no KV entries.
Sourcepub fn vault_kv_try_import(
&self,
entries: HashMap<String, String>,
) -> Result<usize, AuthError>
pub fn vault_kv_try_import( &self, entries: HashMap<String, String>, ) -> Result<usize, AuthError>
Merge imported vault KV entries and fail if the encrypted vault write cannot be made durable.
Sourcepub fn vault_kv_try_import_placeholders(
&self,
keys: &[String],
) -> Result<usize, AuthError>
pub fn vault_kv_try_import_placeholders( &self, keys: &[String], ) -> Result<usize, AuthError>
Import false placeholders for secrets whose encrypted payload could not be decrypted during logical restore.
Sourcepub fn vault_kv_set(&self, key: String, value: String)
pub fn vault_kv_set(&self, key: String, value: String)
Write a value to the vault KV store, persisting to disk.
Sourcepub fn vault_kv_try_set(
&self,
key: String,
value: String,
) -> Result<(), AuthError>
pub fn vault_kv_try_set( &self, key: String, value: String, ) -> Result<(), AuthError>
Write a value to the vault KV store and fail if the vault write cannot be made durable.
Sourcepub fn vault_kv_delete(&self, key: &str) -> bool
pub fn vault_kv_delete(&self, key: &str) -> bool
Delete a value from the vault KV store. Returns true if it existed.
Sourcepub fn vault_kv_try_delete(&self, key: &str) -> Result<bool, AuthError>
pub fn vault_kv_try_delete(&self, key: &str) -> Result<bool, AuthError>
Delete a value from the vault KV store and fail if the vault write cannot be made durable.
Sourcepub fn vault_kv_keys(&self) -> Vec<String>
pub fn vault_kv_keys(&self) -> Vec<String>
List all keys in the vault KV store.
Sourcepub fn vault_secret_key(&self) -> Option<[u8; 32]>
pub fn vault_secret_key(&self) -> Option<[u8; 32]>
Convenience: get the 32-byte secret key for Value::Secret encryption.
Generated on first boot and stored at red.secret.aes_key.
Sourcepub fn ensure_vault_secret_key(&self)
pub fn ensure_vault_secret_key(&self)
Generate and store the AES-256 secret key on first boot if not present.
Sourcepub fn create_user(
&self,
username: &str,
password: &str,
role: Role,
) -> Result<User, AuthError>
pub fn create_user( &self, username: &str, password: &str, role: Role, ) -> Result<User, AuthError>
Create a new platform-scoped user (tenant_id = None).
For tenant-scoped creation, use Self::create_user_in_tenant.
Sourcepub fn create_user_in_tenant(
&self,
tenant_id: Option<&str>,
username: &str,
password: &str,
role: Role,
) -> Result<User, AuthError>
pub fn create_user_in_tenant( &self, tenant_id: Option<&str>, username: &str, password: &str, role: Role, ) -> Result<User, AuthError>
Create a user under the given tenant scope. tenant_id == None
produces a platform-wide user. (tenant, username) is the
uniqueness key — the same username may exist independently
under multiple tenants.
Sourcepub fn lookup_scram_verifier(&self, id: &UserId) -> Option<ScramVerifier>
pub fn lookup_scram_verifier(&self, id: &UserId) -> Option<ScramVerifier>
Look up a user’s SCRAM verifier by full UserId.
The wire handshake passes the tenant resolved from the session
(or None for the bootstrap admin) so cross-tenant collisions
never authenticate the wrong identity.
Sourcepub fn lookup_scram_verifier_global(
&self,
username: &str,
) -> Option<ScramVerifier>
pub fn lookup_scram_verifier_global( &self, username: &str, ) -> Option<ScramVerifier>
Backwards-compatible shim for the v2 wire bootstrap path: looks
up a user by username assuming the platform (tenant=None)
scope. Use this only where the handshake hasn’t yet learned the
caller’s tenant.
Sourcepub fn list_users(&self) -> Vec<User>
pub fn list_users(&self) -> Vec<User>
Return all users (password hashes redacted).
Sourcepub fn list_users_scoped(
&self,
tenant_filter: Option<Option<&str>>,
) -> Vec<User>
pub fn list_users_scoped( &self, tenant_filter: Option<Option<&str>>, ) -> Vec<User>
Return users restricted to a tenant scope.
tenant_filter:
Nonelisting inSome(None)— only platform usersSome(Some("acme"))— only users in tenantacmeNoneargument — all users (admin-only callers)
Sourcepub fn get_user(&self, tenant_id: Option<&str>, username: &str) -> Option<User>
pub fn get_user(&self, tenant_id: Option<&str>, username: &str) -> Option<User>
Look up a single user by (tenant, username). Password hash
is redacted.
Sourcepub fn delete_user(&self, username: &str) -> Result<(), AuthError>
pub fn delete_user(&self, username: &str) -> Result<(), AuthError>
Delete a platform-scoped user (tenant_id = None) and revoke
all of their API keys + sessions.
For tenant-scoped deletes, use Self::delete_user_in_tenant.
Sourcepub fn delete_user_in_tenant(
&self,
tenant_id: Option<&str>,
username: &str,
) -> Result<(), AuthError>
pub fn delete_user_in_tenant( &self, tenant_id: Option<&str>, username: &str, ) -> Result<(), AuthError>
Delete a user identified by (tenant_id, username) and revoke
all of their API keys + sessions.
Sourcepub fn change_password(
&self,
username: &str,
old_password: &str,
new_password: &str,
) -> Result<(), AuthError>
pub fn change_password( &self, username: &str, old_password: &str, new_password: &str, ) -> Result<(), AuthError>
Change password (requires the old password). Defaults to
platform tenant; use Self::change_password_in_tenant for
scoped users.
pub fn change_password_in_tenant( &self, tenant_id: Option<&str>, username: &str, old_password: &str, new_password: &str, ) -> Result<(), AuthError>
Sourcepub fn change_role(
&self,
username: &str,
new_role: Role,
) -> Result<(), AuthError>
pub fn change_role( &self, username: &str, new_role: Role, ) -> Result<(), AuthError>
Change a user’s role (admin-only operation). Defaults to platform
tenant; use Self::change_role_in_tenant for scoped users.
pub fn change_role_in_tenant( &self, tenant_id: Option<&str>, username: &str, new_role: Role, ) -> Result<(), AuthError>
Sourcepub fn authenticate(
&self,
username: &str,
password: &str,
) -> Result<Session, AuthError>
pub fn authenticate( &self, username: &str, password: &str, ) -> Result<Session, AuthError>
Verify credentials for a platform-tenant user (tenant_id = None)
and create a session. For tenant-scoped login use
Self::authenticate_in_tenant.
When a keypair is available (certificate-based seal), session tokens are signed with the master secret so the server can verify they were genuinely issued by this vault instance.
Sourcepub fn authenticate_in_tenant(
&self,
tenant_id: Option<&str>,
username: &str,
password: &str,
) -> Result<Session, AuthError>
pub fn authenticate_in_tenant( &self, tenant_id: Option<&str>, username: &str, password: &str, ) -> Result<Session, AuthError>
Verify credentials for (tenant_id, username, password) and
create a session. Tenant-aware: alice@acme and alice@globex
authenticate independently.
Sourcepub fn validate_token(&self, token: &str) -> Option<(String, Role)>
pub fn validate_token(&self, token: &str) -> Option<(String, Role)>
Validate a token (session or API key).
Returns (username, role) if valid, None otherwise. Tenant
scope is dropped here for compatibility with the bulk of the
existing caller surface (routing, gRPC control, redwire). Use
Self::validate_token_full when the caller needs the
resolved UserId (e.g. to pin CURRENT_TENANT()).
Sourcepub fn validate_token_full(&self, token: &str) -> Option<(UserId, Role)>
pub fn validate_token_full(&self, token: &str) -> Option<(UserId, Role)>
Tenant-aware token validation. Returns the resolved UserId
(which carries the tenant) and the granted Role.
Sourcepub fn create_api_key(
&self,
username: &str,
name: &str,
role: Role,
) -> Result<ApiKey, AuthError>
pub fn create_api_key( &self, username: &str, name: &str, role: Role, ) -> Result<ApiKey, AuthError>
Create a persistent API key for a platform-tenant user.
For tenant-scoped users use Self::create_api_key_in_tenant.
pub fn create_api_key_in_tenant( &self, tenant_id: Option<&str>, username: &str, name: &str, role: Role, ) -> Result<ApiKey, AuthError>
Sourcepub fn revoke_session(&self, token: &str)
pub fn revoke_session(&self, token: &str)
Revoke a session token.
Sourcepub fn purge_expired_sessions(&self) -> usize
pub fn purge_expired_sessions(&self) -> usize
Purge expired sessions (housekeeping).
Sourcepub fn grant(
&self,
granter: &UserId,
granter_role: Role,
principal: GrantPrincipal,
resource: Resource,
actions: Vec<Action>,
with_grant_option: bool,
tenant: Option<String>,
) -> Result<(), AuthError>
pub fn grant( &self, granter: &UserId, granter_role: Role, principal: GrantPrincipal, resource: Resource, actions: Vec<Action>, with_grant_option: bool, tenant: Option<String>, ) -> Result<(), AuthError>
Persist a grant. Returns Forbidden when the granting user is
not Admin or attempts a cross-tenant grant.
Sourcepub fn revoke(
&self,
granter_role: Role,
principal: &GrantPrincipal,
resource: &Resource,
actions: &[Action],
) -> Result<usize, AuthError>
pub fn revoke( &self, granter_role: Role, principal: &GrantPrincipal, resource: &Resource, actions: &[Action], ) -> Result<usize, AuthError>
Drop matching grants from a principal. Returns the number of grants removed.
Sourcepub fn visible_collections_for_scope(
&self,
tenant: Option<&str>,
role: Role,
principal: &str,
all_collections: &[String],
) -> HashSet<String>
pub fn visible_collections_for_scope( &self, tenant: Option<&str>, role: Role, principal: &str, all_collections: &[String], ) -> HashSet<String>
Compute the set of collection ids a given (tenant, role)
scope can read, consulting the explicit grant table. The result
is cached for super::scope_cache::DEFAULT_TTL (60s) and
invalidated on every GRANT/REVOKE/policy/collection mutation
that could change the answer.
all_collections is the full list of collection ids known to
the storage layer. The runtime hands it in so this module stays
decoupled from the storage crate. Each collection passes through
check_grant(SELECT) under a synthetic (principal, role, tenant) view. The cache key includes principal because direct
grants can differ between users that share the same tenant and
role.
Sourcepub fn auth_cache_stats(&self) -> AuthCacheStats
pub fn auth_cache_stats(&self) -> AuthCacheStats
Stats probe required by issue #119 — exposes hit/miss counts and invalidations for the visible-collections cache so metrics pipelines can compute a hit rate.
Sourcepub fn invalidate_visible_collections_cache(&self)
pub fn invalidate_visible_collections_cache(&self)
Drop every cached (tenant, role) entry. Called from CREATE
POLICY / DROP POLICY / DROP COLLECTION paths where the affected
tenant set is unknown.
Sourcepub fn invalidate_visible_collections_for_tenant(&self, tenant: Option<&str>)
pub fn invalidate_visible_collections_for_tenant(&self, tenant: Option<&str>)
Drop cached entries for one tenant. Called from GRANT / REVOKE where the principal’s tenant is known.
Sourcepub fn effective_grants(&self, uid: &UserId) -> Vec<Grant>
pub fn effective_grants(&self, uid: &UserId) -> Vec<Grant>
Snapshot of every grant the principal effectively has, including
Public grants. Audit / introspection helper.
Sourcepub fn check_grant(
&self,
ctx: &AuthzContext<'_>,
action: Action,
resource: &Resource,
) -> Result<(), AuthzError>
pub fn check_grant( &self, ctx: &AuthzContext<'_>, action: Action, resource: &Resource, ) -> Result<(), AuthzError>
Run a privilege check using the in-memory grant tables. Returns
Ok(()) on allow, Err(AuthzError) on deny.
Sourcepub fn set_user_attributes(
&self,
uid: &UserId,
attrs: UserAttributes,
) -> Result<(), AuthError>
pub fn set_user_attributes( &self, uid: &UserId, attrs: UserAttributes, ) -> Result<(), AuthError>
Replace the attribute record for uid.
Sourcepub fn user_attributes(&self, uid: &UserId) -> UserAttributes
pub fn user_attributes(&self, uid: &UserId) -> UserAttributes
Read the attributes for uid. Returns Default::default() for
users that have never been altered.
pub fn add_user_to_group( &self, uid: &UserId, group: &str, ) -> Result<(), AuthError>
pub fn remove_user_from_group( &self, uid: &UserId, group: &str, ) -> Result<(), AuthError>
Sourcepub fn set_user_enabled(
&self,
uid: &UserId,
enabled: bool,
) -> Result<(), AuthError>
pub fn set_user_enabled( &self, uid: &UserId, enabled: bool, ) -> Result<(), AuthError>
Toggle User.enabled without rotating credentials.
Sourcepub fn authenticate_with_attrs(
&self,
tenant_id: Option<&str>,
username: &str,
password: &str,
) -> Result<Session, AuthError>
pub fn authenticate_with_attrs( &self, tenant_id: Option<&str>, username: &str, password: &str, ) -> Result<Session, AuthError>
Authenticate with VALID UNTIL / CONNECTION LIMIT enforcement.
Wraps authenticate_in_tenant and additionally:
- rejects logins after
valid_until, - rejects logins when the live session count would exceed the
connection_limitattribute.
Sourcepub fn decrement_session_count(&self, uid: &UserId)
pub fn decrement_session_count(&self, uid: &UserId)
Decrement the live-session count for uid. Call from session
revoke / expiry paths so CONNECTION LIMIT stays accurate.
Sourcepub fn rehydrate_acl(&self)
pub fn rehydrate_acl(&self)
Re-read the ACL state from vault_kv. Call after vault load /
restore so the in-memory maps reflect the persisted data.
Sourcepub fn put_policy(&self, p: Policy) -> Result<(), AuthError>
pub fn put_policy(&self, p: Policy) -> Result<(), AuthError>
Insert or replace a policy by id. Rejects synthetic ids
(_grant_* / _default_*) so callers can’t hand-write them
from the public API. Use put_policy_internal for synthetic
inserts.
Sourcepub fn put_policy_internal(&self, p: Policy) -> Result<(), AuthError>
pub fn put_policy_internal(&self, p: Policy) -> Result<(), AuthError>
Internal put bypassing the synthetic-namespace guard. Used by
the GRANT translation layer; exposed publicly so integration
tests can register synthetic _grant_* policies without going
through the SQL frontend.
Whether the IAM evaluator should be authoritative for runtime authorization. This flips on the first policy write and remains on after deletes so dropping all policies leaves the instance in default-deny rather than silently returning to role fallback.
Sourcepub fn delete_policy(&self, id: &str) -> Result<(), AuthError>
pub fn delete_policy(&self, id: &str) -> Result<(), AuthError>
Remove a policy and any attachments referencing it.
Sourcepub fn list_policies(&self) -> Vec<Arc<Policy>>
pub fn list_policies(&self) -> Vec<Arc<Policy>>
List all policies (id-sorted for deterministic output).
Sourcepub fn group_policies(&self, group: &str) -> Vec<Arc<Policy>>
pub fn group_policies(&self, group: &str) -> Vec<Arc<Policy>>
List policies directly attached to a group.
Sourcepub fn delete_synthetic_grant_policies(
&self,
principal: &GrantPrincipal,
resource: &Resource,
actions: &[Action],
) -> usize
pub fn delete_synthetic_grant_policies( &self, principal: &GrantPrincipal, resource: &Resource, actions: &[Action], ) -> usize
Delete synthetic policies produced by SQL GRANT translation. REVOKE uses this to keep the IAM lane and the legacy grant table in lock-step.
Sourcepub fn attach_policy(
&self,
principal: PrincipalRef,
policy_id: &str,
) -> Result<(), AuthError>
pub fn attach_policy( &self, principal: PrincipalRef, policy_id: &str, ) -> Result<(), AuthError>
Attach a policy to a user or group. Returns an error if the policy id doesn’t exist.
Sourcepub fn detach_policy(
&self,
principal: PrincipalRef,
policy_id: &str,
) -> Result<(), AuthError>
pub fn detach_policy( &self, principal: PrincipalRef, policy_id: &str, ) -> Result<(), AuthError>
Remove a policy attachment from a user or group.
Sourcepub fn effective_policies(&self, user: &UserId) -> Vec<Arc<Policy>>
pub fn effective_policies(&self, user: &UserId) -> Vec<Arc<Policy>>
Resolve the ordered list of effective policies for a user: group attachments first (least specific), then user attachments (most specific). Cached per user.
Sourcepub fn simulate(
&self,
principal: &UserId,
action: &str,
resource: &ResourceRef,
ctx_extras: SimCtx,
) -> SimulationOutcome
pub fn simulate( &self, principal: &UserId, action: &str, resource: &ResourceRef, ctx_extras: SimCtx, ) -> SimulationOutcome
Run the policy simulator for a principal. Synthesises an
EvalContext from the user record + caller-supplied extras.
Sourcepub fn check_policy_authz(
&self,
principal: &UserId,
action: &str,
resource: &ResourceRef,
ctx: &EvalContext,
) -> bool
pub fn check_policy_authz( &self, principal: &UserId, action: &str, resource: &ResourceRef, ctx: &EvalContext, ) -> bool
Production hot-path policy evaluation. Returns true on Allow
/ AdminBypass, false on Deny / DefaultDeny.
Sourcepub fn check_column_projection_authz(
&self,
principal: &UserId,
request: &ColumnAccessRequest,
ctx: &EvalContext,
) -> ColumnPolicyOutcome
pub fn check_column_projection_authz( &self, principal: &UserId, request: &ColumnAccessRequest, ctx: &EvalContext, ) -> ColumnPolicyOutcome
Evaluate a resolved table projection through the column policy gate. Query paths should pass already-resolved column names; this helper intentionally does not parse SQL projection syntax.
Sourcepub fn invalidate_all_iam_cache(&self)
pub fn invalidate_all_iam_cache(&self)
Drop every effective-policy cache entry. Called from execution paths that mutate policies/attachments without knowing which users will be affected.
Sourcepub fn rehydrate_iam(&self)
pub fn rehydrate_iam(&self)
Reload IAM state (policies + attachments) from the vault KV.
Replaces the legacy rehydrate_acl reader — pre-1.0 we drop
the old red.acl.* blob format entirely.
Auto Trait Implementations§
impl !Freeze for AuthStore
impl RefUnwindSafe for AuthStore
impl Send for AuthStore
impl Sync for AuthStore
impl Unpin for AuthStore
impl UnsafeUnpin for AuthStore
impl UnwindSafe for AuthStore
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request