Skip to main content

heldar_kernel/models/
auth.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4
5/// Operator account. `password_hash` is never serialized; use [`UserView`] for output.
6#[derive(Debug, Clone, FromRow)]
7pub struct User {
8    pub id: String,
9    pub username: String,
10    pub password_hash: String,
11    pub role: String,
12    pub display_name: Option<String>,
13    pub active: bool,
14    /// Consecutive failed logins (brute-force lockout). Never serialized (see [`UserView`]).
15    pub failed_login_count: i64,
16    /// Instant before which login is refused; `None` = not locked. Never serialized.
17    pub locked_until: Option<DateTime<Utc>>,
18    pub created_at: DateTime<Utc>,
19    pub updated_at: DateTime<Utc>,
20}
21
22#[derive(Debug, Clone, Serialize)]
23pub struct UserView {
24    pub id: String,
25    pub username: String,
26    pub role: String,
27    pub display_name: Option<String>,
28    pub active: bool,
29    pub created_at: DateTime<Utc>,
30    pub updated_at: DateTime<Utc>,
31}
32
33impl From<User> for UserView {
34    fn from(u: User) -> Self {
35        UserView {
36            id: u.id,
37            username: u.username,
38            role: u.role,
39            display_name: u.display_name,
40            active: u.active,
41            created_at: u.created_at,
42            updated_at: u.updated_at,
43        }
44    }
45}
46
47#[derive(Debug, Deserialize)]
48pub struct UserCreate {
49    pub username: String,
50    pub password: String,
51    pub role: Option<String>,
52    pub display_name: Option<String>,
53    pub active: Option<bool>,
54}
55
56#[derive(Debug, Deserialize, Default)]
57pub struct UserUpdate {
58    pub password: Option<String>,
59    pub role: Option<String>,
60    pub display_name: Option<String>,
61    pub active: Option<bool>,
62}
63
64#[derive(Debug, Deserialize)]
65pub struct LoginRequest {
66    pub username: String,
67    pub password: String,
68}
69
70#[derive(Debug, Clone, FromRow)]
71pub struct ApiKey {
72    pub id: String,
73    pub name: String,
74    /// Mapped from the row for completeness; never exposed (see [`ApiKeyView`]).
75    pub key_hash: String,
76    pub key_prefix: String,
77    pub role: String,
78    pub active: bool,
79    pub last_used_at: Option<DateTime<Utc>>,
80    pub created_at: DateTime<Utc>,
81}
82
83#[derive(Debug, Clone, Serialize)]
84pub struct ApiKeyView {
85    pub id: String,
86    pub name: String,
87    pub key_prefix: String,
88    pub role: String,
89    pub active: bool,
90    pub last_used_at: Option<DateTime<Utc>>,
91    pub created_at: DateTime<Utc>,
92}
93
94impl From<ApiKey> for ApiKeyView {
95    fn from(k: ApiKey) -> Self {
96        ApiKeyView {
97            id: k.id,
98            name: k.name,
99            key_prefix: k.key_prefix,
100            role: k.role,
101            active: k.active,
102            last_used_at: k.last_used_at,
103            created_at: k.created_at,
104        }
105    }
106}
107
108#[derive(Debug, Deserialize)]
109pub struct ApiKeyCreate {
110    pub name: String,
111    pub role: Option<String>,
112}