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