1use crate::model::{ActiveRow, SqlxModel};
4use crate::pool::Engine;
5use crate::value::SqlValue;
6use alibi_core::AuthError;
7use alibi_core::entity::{AuthAccount, AuthSession, AuthUser, AuthVerification};
8use alibi_core::error::AuthResult;
9use alibi_core::field_policy::FieldValues;
10pub use alibi_core::schema::AuthSchema;
11use alibi_core::types::{
12 CreateAccount, CreateSession, CreateUser, CreateVerification, UpdateAccount, UpdateUser,
13};
14use chrono::{DateTime, Utc};
15
16pub trait SqlxUserModel: AuthUser + SqlxModel {
17 fn additional_field_bindings(
23 fields: &FieldValues,
24 _backend: Engine,
25 ) -> AuthResult<Vec<(&'static str, SqlValue)>> {
26 if fields.is_empty() {
27 return Ok(Vec::new());
28 }
29 Err(alibi_core::AuthError::internal(
30 "the user schema has no additional field bindings",
31 ))
32 }
33
34 fn set_additional_field(
38 _active: &mut ActiveRow,
39 _column: &'static str,
40 _value: SqlValue,
41 _backend: Engine,
42 ) -> AuthResult<()> {
43 Err(alibi_core::AuthError::internal(
44 "the user schema cannot stage additional fields",
45 ))
46 }
47
48 fn id_column() -> &'static str;
49
50 fn email_column() -> &'static str;
51
52 #[must_use]
54 fn username_column() -> Option<&'static str> {
55 None
56 }
57
58 #[must_use]
59 fn phone_number_column() -> Option<&'static str> {
60 None
61 }
62
63 fn name_column() -> &'static str;
64
65 fn created_at_column() -> &'static str;
66
67 #[must_use]
71 fn list_users_column(field: &str) -> Option<&'static str> {
72 match field {
73 "id" => Some(Self::id_column()),
74 "email" => Some(Self::email_column()),
75 "name" => Some(Self::name_column()),
76 "username" => Self::username_column(),
77 "createdAt" => Some(Self::created_at_column()),
78 _ => None,
79 }
80 }
81
82 fn parse_id(id: &str) -> AuthResult<SqlValue>;
86
87 fn new_active(id: Option<SqlValue>, create_user: CreateUser, now: DateTime<Utc>) -> ActiveRow;
88
89 fn apply_update(active: &mut ActiveRow, update: UpdateUser, now: DateTime<Utc>);
90
91 fn prepare_json_metadata(_active: &mut ActiveRow, _backend: Engine) -> AuthResult<()> {
98 Ok(())
99 }
100}
101
102pub trait SqlxSessionModel: AuthSession + SqlxModel {
103 fn materialize_secondary(_active: ActiveRow) -> AuthResult<Self> {
110 Err(AuthError::NotImplemented(
111 "Secondary session materialization is unsupported".into(),
112 ))
113 }
114
115 fn additional_field_bindings(
121 fields: &FieldValues,
122 _backend: Engine,
123 ) -> AuthResult<Vec<(&'static str, SqlValue)>> {
124 if fields.is_empty() {
125 return Ok(Vec::new());
126 }
127 Err(alibi_core::AuthError::internal(
128 "the session schema has no additional field bindings",
129 ))
130 }
131
132 fn set_additional_field(
136 _active: &mut ActiveRow,
137 _column: &'static str,
138 _value: SqlValue,
139 _backend: Engine,
140 ) -> AuthResult<()> {
141 Err(alibi_core::AuthError::internal(
142 "the session schema cannot stage additional fields",
143 ))
144 }
145
146 fn id_column() -> &'static str;
147
148 fn token_column() -> &'static str;
149
150 fn user_id_column() -> &'static str;
151
152 fn expires_at_column() -> &'static str;
153
154 fn created_at_column() -> &'static str;
155
156 fn parse_id(id: &str) -> AuthResult<SqlValue>;
160
161 fn parse_user_id(user_id: &str) -> AuthResult<SqlValue>;
165
166 fn new_active(
167 id: Option<SqlValue>,
168 token: String,
169 create_session: CreateSession,
170 now: DateTime<Utc>,
171 ) -> ActiveRow;
172
173 fn set_expires_at(active: &mut ActiveRow, expires_at: DateTime<Utc>);
174
175 fn set_updated_at(active: &mut ActiveRow, updated_at: DateTime<Utc>);
176
177 fn set_active_organization_id(active: &mut ActiveRow, organization_id: Option<String>);
178
179 fn set_active_team_id(_active: &mut ActiveRow, _team_id: Option<String>) -> AuthResult<()> {
183 Err(alibi_core::AuthError::internal(
184 "the session schema has no active-team field",
185 ))
186 }
187}
188
189pub trait SqlxAccountModel: AuthAccount + SqlxModel {
190 fn additional_field_bindings(
196 fields: &FieldValues,
197 _backend: Engine,
198 ) -> AuthResult<Vec<(&'static str, SqlValue)>> {
199 if fields.is_empty() {
200 return Ok(Vec::new());
201 }
202 Err(alibi_core::AuthError::internal(
203 "the account schema has no additional field bindings",
204 ))
205 }
206
207 fn set_additional_field(
211 _active: &mut ActiveRow,
212 _column: &'static str,
213 _value: SqlValue,
214 _backend: Engine,
215 ) -> AuthResult<()> {
216 Err(alibi_core::AuthError::internal(
217 "the account schema cannot stage additional fields",
218 ))
219 }
220
221 fn id_column() -> &'static str;
222 fn provider_id_column() -> &'static str;
223 fn account_id_column() -> &'static str;
224 fn user_id_column() -> &'static str;
225 fn created_at_column() -> &'static str;
226 fn parse_id(id: &str) -> AuthResult<SqlValue>;
230
231 fn parse_user_id(user_id: &str) -> AuthResult<SqlValue>;
235
236 fn new_active(
237 id: Option<SqlValue>,
238 create_account: CreateAccount,
239 now: DateTime<Utc>,
240 ) -> ActiveRow;
241 fn apply_update(active: &mut ActiveRow, update: UpdateAccount, now: DateTime<Utc>);
242}
243
244pub trait SqlxVerificationModel: AuthVerification + SqlxModel {
245 fn id_column() -> &'static str;
246 fn identifier_column() -> &'static str;
247 fn value_column() -> &'static str;
248 fn expires_at_column() -> &'static str;
249 fn created_at_column() -> &'static str;
250 #[must_use]
251 fn updated_at_column() -> Option<&'static str> {
252 None
253 }
254 fn parse_id(id: &str) -> AuthResult<SqlValue>;
258
259 fn parse_reservation_id(encoded: &str, digest: [u8; 32]) -> AuthResult<SqlValue> {
268 if let Ok(id) = Self::parse_id(encoded) {
269 return Ok(id);
270 }
271 let mut bytes = [0; 16];
272 for (byte, source) in bytes.iter_mut().zip(digest) {
273 *byte = source;
274 }
275 Self::parse_id(&uuid::Uuid::from_bytes(bytes).to_string()).map_err(|_error| {
276 alibi_core::AuthError::internal(
277 "the verification schema cannot represent deterministic reservation IDs",
278 )
279 })
280 }
281
282 fn new_active(
283 id: Option<SqlValue>,
284 verification: CreateVerification,
285 now: DateTime<Utc>,
286 ) -> ActiveRow;
287}