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