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