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 /// The operator's own UI language as a base tag (e.g. `kn`). `None` means the
29 /// operator inherits the deployment default. This only affects the admin's own
30 /// strings, not stored content.
31 pub locale: Option<String>,
32 pub created_at: DateTime<Utc>,
33 pub updated_at: DateTime<Utc>,
34}
35
36impl BackendUser {
37 /// The display name, derived rather than stored: `first_name` plus
38 /// `last_name` when present.
39 pub fn full_name(&self) -> String {
40 match &self.last_name {
41 Some(last) if !last.is_empty() => format!("{} {}", self.first_name, last),
42 _ => self.first_name.clone(),
43 }
44 }
45}
46
47/// A lightweight backend-user projection for listings (no credential fields).
48#[derive(Debug, Clone, Serialize)]
49pub struct BackendUserSummary {
50 pub id: i64,
51 pub username: String,
52 pub email: String,
53 pub first_name: String,
54 pub last_name: Option<String>,
55 pub is_superuser: bool,
56 pub is_active: bool,
57 pub created_at: DateTime<Utc>,
58}
59
60/// The kind of event recorded in the access log.
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum AccessEvent {
63 LoginSuccess,
64 LoginFailure,
65 LockedOut,
66 Logout,
67}
68
69impl AccessEvent {
70 /// The stored string form; stable, since it is persisted.
71 pub fn as_str(self) -> &'static str {
72 match self {
73 AccessEvent::LoginSuccess => "login_success",
74 AccessEvent::LoginFailure => "login_failure",
75 AccessEvent::LockedOut => "locked_out",
76 AccessEvent::Logout => "logout",
77 }
78 }
79}