Skip to main content

laterite_auth/
models.rs

1//! Persistent row types for the auth schema.
2
3use chrono::{DateTime, Utc};
4use serde::Serialize;
5
6/// A backend user: an operator of the admin surface.
7///
8/// Names follow the structured `first_name` plus optional `last_name` pair, the
9/// same shape the wider ecosystem standardizes on so integrations and any
10/// future extensions can rely on the same fields. `password_hash` is skipped in
11/// serialization so an identity can be handed to a template or API response
12/// without leaking the stored credential.
13#[derive(Debug, Clone, Serialize)]
14pub struct BackendUser {
15    pub id: i64,
16    pub username: String,
17    pub email: String,
18    pub first_name: String,
19    pub last_name: Option<String>,
20    #[serde(skip)]
21    pub password_hash: String,
22    pub is_superuser: bool,
23    pub is_active: bool,
24    /// The operator's own display timezone as an IANA name (e.g. `Asia/Kolkata`).
25    /// `None` means the operator inherits the deployment default. Storage is
26    /// always UTC; this only affects how timestamps render for this operator.
27    pub timezone: Option<String>,
28    pub created_at: DateTime<Utc>,
29    pub updated_at: DateTime<Utc>,
30}
31
32impl BackendUser {
33    /// The display name, derived rather than stored: `first_name` plus
34    /// `last_name` when present.
35    pub fn full_name(&self) -> String {
36        match &self.last_name {
37            Some(last) if !last.is_empty() => format!("{} {}", self.first_name, last),
38            _ => self.first_name.clone(),
39        }
40    }
41}
42
43/// A lightweight backend-user projection for listings (no credential fields).
44#[derive(Debug, Clone, Serialize)]
45pub struct BackendUserSummary {
46    pub id: i64,
47    pub username: String,
48    pub email: String,
49    pub first_name: String,
50    pub last_name: Option<String>,
51    pub is_superuser: bool,
52    pub is_active: bool,
53    pub created_at: DateTime<Utc>,
54}
55
56/// The kind of event recorded in the access log.
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum AccessEvent {
59    LoginSuccess,
60    LoginFailure,
61    LockedOut,
62    Logout,
63}
64
65impl AccessEvent {
66    /// The stored string form; stable, since it is persisted.
67    pub fn as_str(self) -> &'static str {
68        match self {
69            AccessEvent::LoginSuccess => "login_success",
70            AccessEvent::LoginFailure => "login_failure",
71            AccessEvent::LockedOut => "locked_out",
72            AccessEvent::Logout => "logout",
73        }
74    }
75}