Skip to main content

alibi_core/
entity.rs

1//! Entity traits for the Alibi framework.
2//!
3//! These traits define the interface that entity types must implement.
4//! The framework accesses entity fields through these trait methods,
5//! allowing users to define their own entity structs with custom field names
6//! and extra fields.
7//!
8//! Implement these traits manually for any custom types used inside the auth
9//! runtime.
10
11use crate::AuthError;
12use crate::types::InvitationStatus;
13use crate::{AdapterOutput, AuthResult};
14use chrono::{DateTime, Utc};
15use serde::{Deserialize, Serialize};
16use std::borrow::Cow;
17
18/// Conversion between an application timestamp field and the auth UTC clock.
19///
20/// `NaiveDateTime` fields represent UTC wall-clock values, never local time.
21/// The optional implementation preserves typed absence in model mutations.
22pub trait AuthTimestamp: Sized {
23    /// The core timestamp shape (`DateTime<Utc>` or its optional form).
24    type UtcValue;
25    /// Convert a core UTC timestamp into the model representation.
26    fn from_utc(value: Self::UtcValue) -> Self;
27    /// Interpret the model timestamp on the core UTC clock.
28    fn into_utc(self) -> Self::UtcValue;
29}
30
31impl AuthTimestamp for chrono::NaiveDateTime {
32    type UtcValue = DateTime<Utc>;
33    fn from_utc(value: Self::UtcValue) -> Self {
34        value.naive_utc()
35    }
36    fn into_utc(self) -> Self::UtcValue {
37        self.and_utc()
38    }
39}
40
41impl<Tz: chrono::TimeZone> AuthTimestamp for DateTime<Tz>
42where
43    Self: From<DateTime<Utc>>,
44{
45    type UtcValue = DateTime<Utc>;
46    fn from_utc(value: Self::UtcValue) -> Self {
47        value.into()
48    }
49    fn into_utc(self) -> Self::UtcValue {
50        self.with_timezone(&Utc)
51    }
52}
53
54impl<T: AuthTimestamp> AuthTimestamp for Option<T> {
55    type UtcValue = Option<T::UtcValue>;
56    fn from_utc(value: Self::UtcValue) -> Self {
57        value.map(T::from_utc)
58    }
59    fn into_utc(self) -> Self::UtcValue {
60        self.map(T::into_utc)
61    }
62}
63
64/// Trait representing a user entity.
65///
66/// The framework reads user fields through these getters. Custom types
67/// must provide all framework fields and may have additional fields.
68pub trait AuthUser: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
69    /// Encode the complete typed model for a trusted secondary backend.
70    /// Custom entities can opt in without adding a Deserialize bound to all schemas.
71    fn secondary_snapshot(&self) -> AuthResult<serde_json::Value> {
72        Err(AuthError::NotImplemented(
73            "Secondary model encoding is unsupported".into(),
74        ))
75    }
76    /// Restore a model from a trusted secondary backend, before public projection.
77    fn from_secondary_snapshot(_snapshot: serde_json::Value) -> AuthResult<Self> {
78        Err(AuthError::NotImplemented(
79            "Secondary model decoding is unsupported".into(),
80        ))
81    }
82
83    /// A genuine retained adapter result, including declared undefined values.
84    fn adapter_snapshot(&self) -> Option<&AdapterOutput> {
85        None
86    }
87    /// A chosen authenticated view is already projected and must remain exact.
88    fn retained_user_view(&self) -> Option<&crate::UserView> {
89        None
90    }
91    /// Physical application/plugin values; public policy is applied separately.
92    fn additional_fields(&self) -> crate::field_policy::FieldOutput {
93        crate::field_policy::FieldOutput::new()
94    }
95    fn id(&self) -> Cow<'_, str>;
96    fn email(&self) -> Option<&str>;
97    fn name(&self) -> Option<&str>;
98    fn email_verified(&self) -> bool;
99    fn image(&self) -> Option<&str>;
100    fn created_at(&self) -> DateTime<Utc>;
101    fn updated_at(&self) -> DateTime<Utc>;
102    fn username(&self) -> Option<&str>;
103    fn display_username(&self) -> Option<&str>;
104    fn two_factor_enabled(&self) -> bool;
105    /// Persisted plugin state. `None` preserves an absent or unset field.
106    fn two_factor_enabled_value(&self) -> Option<bool> {
107        Some(self.two_factor_enabled())
108    }
109    fn role(&self) -> Option<&str>;
110    fn banned(&self) -> bool;
111    /// Persisted plugin state. `None` preserves an absent or unset field.
112    fn banned_value(&self) -> Option<bool> {
113        Some(self.banned())
114    }
115    fn ban_reason(&self) -> Option<&str>;
116    fn ban_expires(&self) -> Option<DateTime<Utc>>;
117    fn metadata(&self) -> &serde_json::Value;
118    /// Anonymous-plugin state; `None` means the field is absent or unset.
119    fn is_anonymous(&self) -> Option<bool> {
120        None
121    }
122    fn phone_number(&self) -> Option<&str> {
123        None
124    }
125    fn phone_number_verified(&self) -> Option<bool> {
126        None
127    }
128    fn last_login_method(&self) -> Option<&str> {
129        None
130    }
131}
132
133/// Trait representing a session entity.
134pub trait AuthSession: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
135    /// Encode the complete typed model for a trusted secondary backend.
136    /// Custom entities can opt in without adding a Deserialize bound to all schemas.
137    fn secondary_snapshot(&self) -> AuthResult<serde_json::Value> {
138        Err(AuthError::NotImplemented(
139            "Secondary model encoding is unsupported".into(),
140        ))
141    }
142    /// Restore a model from a trusted secondary backend, before public projection.
143    fn from_secondary_snapshot(_snapshot: serde_json::Value) -> AuthResult<Self> {
144        Err(AuthError::NotImplemented(
145            "Secondary model decoding is unsupported".into(),
146        ))
147    }
148
149    fn adapter_snapshot(&self) -> Option<&AdapterOutput> {
150        None
151    }
152    fn retained_session_view(&self) -> Option<&crate::SessionView> {
153        None
154    }
155    fn additional_fields(&self) -> serde_json::Map<String, serde_json::Value> {
156        serde_json::Map::new()
157    }
158    fn id(&self) -> Cow<'_, str>;
159    fn expires_at(&self) -> DateTime<Utc>;
160    fn token(&self) -> &str;
161    fn created_at(&self) -> DateTime<Utc>;
162    fn updated_at(&self) -> DateTime<Utc>;
163    fn ip_address(&self) -> Option<&str>;
164    fn user_agent(&self) -> Option<&str>;
165    fn user_id(&self) -> Cow<'_, str>;
166    fn impersonated_by(&self) -> Option<&str>;
167    fn active_organization_id(&self) -> Option<&str>;
168    fn active_team_id(&self) -> Option<&str> {
169        None
170    }
171}
172
173/// Trait representing an account entity (OAuth provider linking).
174pub trait AuthAccount: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
175    fn adapter_snapshot(&self) -> Option<&AdapterOutput> {
176        None
177    }
178    fn additional_fields(&self) -> crate::field_policy::FieldOutput {
179        crate::field_policy::FieldOutput::new()
180    }
181    fn id(&self) -> Cow<'_, str>;
182    fn account_id(&self) -> &str;
183    fn provider_id(&self) -> &str;
184    fn user_id(&self) -> Cow<'_, str>;
185    fn access_token(&self) -> Option<&str>;
186    fn refresh_token(&self) -> Option<&str>;
187    fn id_token(&self) -> Option<&str>;
188    fn access_token_expires_at(&self) -> Option<DateTime<Utc>>;
189    fn refresh_token_expires_at(&self) -> Option<DateTime<Utc>>;
190    fn scope(&self) -> Option<&str>;
191    fn password(&self) -> Option<&str>;
192    fn created_at(&self) -> DateTime<Utc>;
193    fn updated_at(&self) -> DateTime<Utc>;
194}
195
196/// Trait representing an organization entity.
197pub trait AuthOrganization: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
198    fn additional_fields(&self) -> std::collections::BTreeMap<String, serde_json::Value> {
199        std::collections::BTreeMap::default()
200    }
201    fn id(&self) -> Cow<'_, str>;
202    fn name(&self) -> &str;
203    fn slug(&self) -> &str;
204    fn logo(&self) -> Option<&str>;
205    fn metadata(&self) -> Option<&serde_json::Value>;
206    fn created_at(&self) -> DateTime<Utc>;
207}
208
209/// Trait representing an organization member entity.
210pub trait AuthMember: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
211    fn id(&self) -> Cow<'_, str>;
212    fn organization_id(&self) -> Cow<'_, str>;
213    fn user_id(&self) -> Cow<'_, str>;
214    fn role(&self) -> &str;
215    fn created_at(&self) -> DateTime<Utc>;
216}
217
218/// Trait representing an invitation entity.
219pub trait AuthInvitation: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
220    fn id(&self) -> Cow<'_, str>;
221    fn organization_id(&self) -> Cow<'_, str>;
222    fn email(&self) -> &str;
223    fn role(&self) -> &str;
224    fn optional_role(&self) -> Option<&str> {
225        Some(self.role())
226    }
227    fn team_id(&self) -> Option<&str> {
228        None
229    }
230    fn status(&self) -> &InvitationStatus;
231    fn inviter_id(&self) -> Cow<'_, str>;
232    fn expires_at(&self) -> DateTime<Utc>;
233    fn created_at(&self) -> DateTime<Utc>;
234
235    /// Check if the invitation is still pending.
236    fn is_pending(&self) -> bool {
237        *self.status() == InvitationStatus::Pending
238    }
239
240    /// Check if the invitation has expired.
241    fn is_expired(&self) -> bool {
242        self.expires_at() < Utc::now()
243    }
244}
245
246/// Trait representing a verification token entity.
247pub trait AuthVerification: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
248    fn id(&self) -> Cow<'_, str>;
249    fn identifier(&self) -> &str;
250    fn value(&self) -> &str;
251    fn expires_at(&self) -> DateTime<Utc>;
252    fn created_at(&self) -> DateTime<Utc>;
253    fn updated_at(&self) -> DateTime<Utc>;
254}
255
256/// Trait representing a two-factor authentication entity.
257pub trait AuthTwoFactor: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
258    fn id(&self) -> Cow<'_, str>;
259    fn secret(&self) -> &str;
260    fn backup_codes(&self) -> &str;
261    fn user_id(&self) -> Cow<'_, str>;
262    /// A nullable verification value is not explicitly false.
263    fn verified(&self) -> Option<bool> {
264        None
265    }
266    fn failed_verification_count(&self) -> Option<f64> {
267        None
268    }
269    fn locked_until(&self) -> Option<DateTime<Utc>> {
270        None
271    }
272}
273
274/// Trait representing an API key entity.
275pub trait AuthApiKey: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
276    fn id(&self) -> Cow<'_, str>;
277    fn name(&self) -> Option<&str>;
278    fn start(&self) -> Option<&str>;
279    fn prefix(&self) -> Option<&str>;
280    fn key_hash(&self) -> &str;
281    /// Owner of the key — a user id, or an organization id when the key's
282    /// configuration references organizations.
283    fn reference_id(&self) -> Cow<'_, str>;
284    /// Name of the API-key configuration this key belongs to (`"default"`
285    /// unless the application registers named configurations).
286    fn config_id(&self) -> Cow<'_, str>;
287    fn refill_interval(&self) -> Option<f64>;
288    fn refill_amount(&self) -> Option<f64>;
289    fn last_refill_at(&self) -> Option<&str>;
290    fn enabled(&self) -> bool;
291    fn rate_limit_enabled(&self) -> bool;
292    fn rate_limit_time_window(&self) -> Option<f64>;
293    fn rate_limit_max(&self) -> Option<f64>;
294    fn request_count(&self) -> Option<f64>;
295    fn remaining(&self) -> Option<f64>;
296    fn last_request(&self) -> Option<&str>;
297    fn expires_at(&self) -> Option<&str>;
298    fn created_at(&self) -> &str;
299    fn updated_at(&self) -> &str;
300    fn permissions(&self) -> Option<&str>;
301    fn metadata(&self) -> Option<&str>;
302}
303
304/// Trait representing a passkey entity.
305pub trait AuthPasskey: Clone + Send + Sync + Serialize + std::fmt::Debug + 'static {
306    fn id(&self) -> Cow<'_, str>;
307    fn name(&self) -> Option<&str>;
308    fn public_key(&self) -> &str;
309    fn user_id(&self) -> Cow<'_, str>;
310    fn credential_id(&self) -> &str;
311    fn counter(&self) -> u64;
312    fn device_type(&self) -> &str;
313    fn backed_up(&self) -> bool;
314    fn transports(&self) -> Option<&str>;
315    fn created_at(&self) -> Option<DateTime<Utc>>;
316    fn aaguid(&self) -> Option<&str>;
317}
318
319/// Minimal user info for member-related API responses.
320///
321/// This is a concrete framework type (not generic) used to project
322/// user fields into member responses.
323#[derive(Debug, Clone, Serialize, Deserialize)]
324pub struct MemberUserView {
325    pub id: String,
326    pub email: Option<String>,
327    pub name: Option<String>,
328    pub image: Option<String>,
329}
330
331impl MemberUserView {
332    /// Construct from any type implementing [`AuthUser`].
333    #[must_use]
334    pub fn from_user(user: &impl AuthUser) -> Self {
335        Self {
336            id: user.id().to_string(),
337            email: user.email().map(ToOwned::to_owned),
338            name: user.name().map(ToOwned::to_owned),
339            image: user.image().map(ToOwned::to_owned),
340        }
341    }
342}