1pub mod action_catalog;
15pub mod cert;
16pub mod column_policy_gate;
17pub mod enforcement_mode;
18pub mod locks;
19pub mod managed_config;
20pub mod managed_policy;
21pub mod middleware;
22pub mod oauth;
23pub mod policies;
24pub mod privileges;
25pub mod registry;
26pub mod scope_cache;
27pub mod scram;
28pub mod self_lock_guard;
29pub mod store;
30pub mod vault;
31
32pub use scope_cache::{AuthCache, AuthCacheStats, ScopeKey, DEFAULT_TTL as DEFAULT_SCOPE_TTL};
33
34pub use cert::{
35 CertAuthConfig, CertAuthError, CertAuthenticator, CertIdentity, CertIdentityMode,
36 ParsedClientCert,
37};
38pub use column_policy_gate::{
39 ColumnAccessRequest, ColumnDecision, ColumnDecisionEffect, ColumnPolicyGate,
40 ColumnPolicyOutcome, ColumnRef,
41};
42pub use oauth::{
43 DecodedJwt, Jwk, JwtClaims, JwtHeader, OAuthConfig, OAuthError, OAuthIdentity,
44 OAuthIdentityMode, OAuthValidator,
45};
46pub use privileges::{
47 check_grant, Action, AuthzContext, AuthzError, Grant, GrantPrincipal, GrantsView,
48 PermissionCache, Resource, UserAttributes,
49};
50pub use store::AuthStore;
51
52use std::fmt;
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
62pub enum Role {
63 Read,
64 Write,
65 Admin,
66}
67
68impl Role {
69 pub fn as_str(&self) -> &'static str {
70 match self {
71 Self::Read => "read",
72 Self::Write => "write",
73 Self::Admin => "admin",
74 }
75 }
76
77 pub fn from_str(s: &str) -> Option<Self> {
78 match s {
79 "read" => Some(Self::Read),
80 "write" => Some(Self::Write),
81 "admin" => Some(Self::Admin),
82 _ => None,
83 }
84 }
85
86 pub fn can_read(&self) -> bool {
87 true
88 }
89
90 pub fn can_write(&self) -> bool {
91 matches!(self, Self::Write | Self::Admin)
92 }
93
94 pub fn can_admin(&self) -> bool {
95 matches!(self, Self::Admin)
96 }
97}
98
99impl fmt::Display for Role {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 f.write_str(self.as_str())
102 }
103}
104
105#[derive(Debug, Clone)]
117pub struct User {
118 pub username: String,
119 pub tenant_id: Option<String>,
123 pub password_hash: String,
124 pub scram_verifier: Option<scram::ScramVerifier>,
127 pub role: Role,
128 pub api_keys: Vec<ApiKey>,
129 pub created_at: u128,
130 pub updated_at: u128,
131 pub enabled: bool,
132 pub system_owned: bool,
133}
134
135#[derive(Debug, Clone, PartialEq, Eq, Hash)]
150pub struct UserId {
151 pub tenant: Option<String>,
152 pub username: String,
153}
154
155impl UserId {
156 pub fn platform(name: impl Into<String>) -> Self {
158 Self {
159 tenant: None,
160 username: name.into(),
161 }
162 }
163
164 pub fn scoped(tenant: impl Into<String>, name: impl Into<String>) -> Self {
166 Self {
167 tenant: Some(tenant.into()),
168 username: name.into(),
169 }
170 }
171
172 pub fn from_parts(tenant: Option<&str>, username: &str) -> Self {
175 Self {
176 tenant: tenant.map(|t| t.to_string()),
177 username: username.to_string(),
178 }
179 }
180}
181
182impl fmt::Display for UserId {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 match &self.tenant {
185 Some(t) => write!(f, "{}/{}", t, self.username),
186 None => f.write_str(&self.username),
187 }
188 }
189}
190
191#[derive(Debug, Clone)]
197pub struct ApiKey {
198 pub key: String,
200 pub name: String,
202 pub role: Role,
204 pub created_at: u128,
205}
206
207#[derive(Debug, Clone)]
213pub struct Session {
214 pub token: String,
216 pub username: String,
217 pub tenant_id: Option<String>,
220 pub role: Role,
221 pub created_at: u128,
222 pub expires_at: u128,
224}
225
226#[derive(Debug, Clone)]
232pub struct AuthConfig {
233 pub enabled: bool,
235 pub session_ttl_secs: u64,
237 pub require_auth: bool,
239 pub auto_encrypt_storage: bool,
241 pub vault_enabled: bool,
246 pub cert: CertAuthConfig,
249 pub oauth: OAuthConfig,
252}
253
254impl Default for AuthConfig {
255 fn default() -> Self {
256 Self {
257 enabled: false,
258 session_ttl_secs: 3600,
259 require_auth: false,
260 auto_encrypt_storage: false,
261 vault_enabled: false,
262 cert: CertAuthConfig::default(),
263 oauth: OAuthConfig::default(),
264 }
265 }
266}
267
268#[derive(Debug, Clone)]
274pub enum AuthError {
275 UserExists(String),
276 UserNotFound(String),
277 InvalidCredentials,
278 KeyNotFound(String),
279 RoleExceeded { requested: Role, ceiling: Role },
280 SystemUserImmutable { username: String },
281 Disabled,
282 Forbidden(String),
283 Internal(String),
284}
285
286impl fmt::Display for AuthError {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 match self {
289 Self::UserExists(u) => write!(f, "user already exists: {u}"),
290 Self::UserNotFound(u) => write!(f, "user not found: {u}"),
291 Self::InvalidCredentials => write!(f, "invalid credentials"),
292 Self::KeyNotFound(k) => write!(f, "api key not found: {k}"),
293 Self::RoleExceeded { requested, ceiling } => {
294 write!(
295 f,
296 "requested role '{requested}' exceeds ceiling '{ceiling}'"
297 )
298 }
299 Self::SystemUserImmutable { username } => {
300 write!(f, "system-owned user is immutable: {username}")
301 }
302 Self::Disabled => write!(f, "authentication is disabled"),
303 Self::Forbidden(msg) => write!(f, "forbidden: {msg}"),
304 Self::Internal(msg) => write!(f, "internal auth error: {msg}"),
305 }
306 }
307}
308
309impl std::error::Error for AuthError {}
310
311pub(crate) fn now_ms() -> u128 {
317 std::time::SystemTime::now()
318 .duration_since(std::time::UNIX_EPOCH)
319 .unwrap_or_default()
320 .as_millis()
321}
322
323#[cfg(test)]
328mod tests {
329 use super::*;
330
331 #[test]
332 fn test_role_ordering() {
333 assert!(Role::Read < Role::Write);
334 assert!(Role::Write < Role::Admin);
335 }
336
337 #[test]
338 fn test_role_roundtrip() {
339 for role in [Role::Read, Role::Write, Role::Admin] {
340 assert_eq!(Role::from_str(role.as_str()), Some(role));
341 }
342 assert_eq!(Role::from_str("unknown"), None);
343 }
344
345 #[test]
346 fn test_role_permissions() {
347 assert!(Role::Read.can_read());
348 assert!(!Role::Read.can_write());
349 assert!(!Role::Read.can_admin());
350
351 assert!(Role::Write.can_read());
352 assert!(Role::Write.can_write());
353 assert!(!Role::Write.can_admin());
354
355 assert!(Role::Admin.can_read());
356 assert!(Role::Admin.can_write());
357 assert!(Role::Admin.can_admin());
358 }
359
360 #[test]
361 fn test_auth_config_default() {
362 let cfg = AuthConfig::default();
363 assert!(!cfg.enabled);
364 assert_eq!(cfg.session_ttl_secs, 3600);
365 assert!(!cfg.require_auth);
366 assert!(!cfg.auto_encrypt_storage);
367 }
368
369 #[test]
370 fn test_auth_error_display() {
371 let err = AuthError::UserExists("alice".into());
372 assert!(err.to_string().contains("alice"));
373
374 let err = AuthError::InvalidCredentials;
375 assert!(err.to_string().contains("invalid"));
376 }
377}