Skip to main content

alibi_schema_registry/
lib.rs

1//! Authoritative field registry for Alibi entity schemas.
2//!
3//! Shared by the `AuthEntity` proc macro (for compile-time validation)
4//! and the CLI (for code generation). This is the single source of truth
5//! for which fields belong to core vs which are plugin-provided.
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum EntityRole {
9    User,
10    Session,
11    Account,
12    Verification,
13}
14
15#[derive(Clone, Copy, Debug)]
16pub struct FieldDef {
17    pub name: &'static str,
18    /// Rust type as it appears in a `SeaORM` `Model` struct,
19    /// e.g. `"Option<String>"`, `"bool"`, `"DateTimeUtc"`, `"Json"`.
20    pub ty: &'static str,
21    pub is_primary_key: bool,
22    pub column_name: Option<&'static str>,
23    /// Database string default, rendered as a literal by each backend.
24    pub default_value: Option<&'static str>,
25}
26
27#[derive(Clone, Copy, Debug)]
28pub struct PluginSchema {
29    pub name: &'static str,
30    pub user_fields: &'static [FieldDef],
31    pub session_fields: &'static [FieldDef],
32    pub extra_entities: &'static [ExtraEntitySchema],
33}
34
35#[derive(Clone, Copy, Debug)]
36pub struct ExtraEntitySchema {
37    pub mod_name: &'static str,
38    pub table_name: &'static str,
39    pub role: Option<EntityRole>,
40    pub fields: &'static [FieldDef],
41}
42
43macro_rules! f {
44    ($name:expr, $ty:expr, $default:expr) => {
45        FieldDef {
46            default_value: Some($default),
47            ..f!($name, $ty)
48        }
49    };
50    ($name:expr, $ty:expr) => {
51        FieldDef {
52            name: $name,
53            ty: $ty,
54            is_primary_key: false,
55            column_name: None,
56            default_value: None,
57        }
58    };
59}
60
61macro_rules! pk {
62    ($name:expr, $ty:expr) => {
63        FieldDef {
64            is_primary_key: true,
65            ..f!($name, $ty)
66        }
67    };
68}
69
70macro_rules! f_col {
71    ($name:expr, $ty:expr, $column_name:expr) => {
72        FieldDef {
73            column_name: Some($column_name),
74            ..f!($name, $ty)
75        }
76    };
77}
78
79static USER_CORE: &[FieldDef] = &[
80    pk!("id", "String"),
81    f!("name", "Option<String>"),
82    f!("email", "Option<String>"),
83    f!("email_verified", "bool"),
84    f!("image", "Option<String>"),
85    f!("created_at", "DateTimeUtc"),
86    f!("updated_at", "DateTimeUtc"),
87];
88
89static SESSION_CORE: &[FieldDef] = &[
90    pk!("id", "String"),
91    f!("expires_at", "DateTimeUtc"),
92    f!("token", "String"),
93    f!("created_at", "DateTimeUtc"),
94    f!("updated_at", "DateTimeUtc"),
95    f!("ip_address", "Option<String>"),
96    f!("user_agent", "Option<String>"),
97    f!("user_id", "String"),
98    f!("active", "bool"),
99];
100
101static ACCOUNT_CORE: &[FieldDef] = &[
102    pk!("id", "String"),
103    f!("account_id", "String"),
104    f!("provider_id", "String"),
105    f!("user_id", "String"),
106    f!("access_token", "Option<String>"),
107    f!("refresh_token", "Option<String>"),
108    f!("id_token", "Option<String>"),
109    f!("access_token_expires_at", "Option<DateTimeUtc>"),
110    f!("refresh_token_expires_at", "Option<DateTimeUtc>"),
111    f!("scope", "Option<String>"),
112    f!("password", "Option<String>"),
113    f!("created_at", "DateTimeUtc"),
114    f!("updated_at", "DateTimeUtc"),
115];
116
117static VERIFICATION_CORE: &[FieldDef] = &[
118    pk!("id", "String"),
119    f!("identifier", "String"),
120    f!("value", "String"),
121    f!("expires_at", "DateTimeUtc"),
122    f!("created_at", "DateTimeUtc"),
123    f!("updated_at", "DateTimeUtc"),
124];
125
126static PLUGINS: &[PluginSchema] = &[
127    PluginSchema {
128        name: "siwe",
129        user_fields: &[],
130        session_fields: &[],
131        extra_entities: &[ExtraEntitySchema {
132            mod_name: "wallet_address",
133            table_name: "wallet_address",
134            role: None,
135            fields: &[
136                pk!("id", "String"),
137                f!("user_id", "String"),
138                f!("address", "String"),
139                f!("chain_id", "f64"),
140                f!("is_primary", "bool"),
141                f!("created_at", "DateTimeUtc"),
142            ],
143        }],
144    },
145    PluginSchema {
146        name: "jwt",
147        user_fields: &[],
148        session_fields: &[],
149        extra_entities: &[ExtraEntitySchema {
150            mod_name: "jwk",
151            table_name: "jwks",
152            role: None,
153            fields: &[
154                pk!("id", "String"),
155                f!("public_key", "String"),
156                f!("private_key", "String"),
157                f!("created_at", "DateTimeUtc"),
158                f!("expires_at", "Option<DateTimeUtc>"),
159                f!("alg", "Option<String>"),
160                f!("crv", "Option<String>"),
161            ],
162        }],
163    },
164    PluginSchema {
165        name: "anonymous",
166        user_fields: &[f!("is_anonymous", "Option<bool>")],
167        session_fields: &[],
168        extra_entities: &[],
169    },
170    PluginSchema {
171        name: "phone-number",
172        user_fields: &[
173            f!("phone_number", "Option<String>"),
174            f!("phone_number_verified", "Option<bool>"),
175        ],
176        session_fields: &[],
177        extra_entities: &[],
178    },
179    PluginSchema {
180        name: "last-login-method",
181        user_fields: &[f!("last_login_method", "Option<String>")],
182        session_fields: &[],
183        extra_entities: &[],
184    },
185    PluginSchema {
186        name: "organization-teams",
187        user_fields: &[],
188        session_fields: &[f!("active_team_id", "Option<String>")],
189        extra_entities: &[
190            ExtraEntitySchema {
191                mod_name: "team",
192                table_name: "team",
193                role: None,
194                fields: &[
195                    pk!("id", "String"),
196                    f!("name", "String"),
197                    f!("organization_id", "String"),
198                    f!("member_count", "i64"),
199                    f!("created_at", "DateTimeUtc"),
200                    f!("updated_at", "Option<DateTimeUtc>"),
201                ],
202            },
203            ExtraEntitySchema {
204                mod_name: "team_member",
205                table_name: "team_member",
206                role: None,
207                fields: &[
208                    pk!("id", "String"),
209                    f!("team_id", "String"),
210                    f!("user_id", "String"),
211                    f!("membership_key", "Option<String>"),
212                    f!("created_at", "DateTimeUtc"),
213                ],
214            },
215        ],
216    },
217    PluginSchema {
218        name: "organization-dynamic-roles",
219        user_fields: &[],
220        session_fields: &[],
221        extra_entities: &[ExtraEntitySchema {
222            mod_name: "organization_role",
223            table_name: "organization_role",
224            role: None,
225            fields: &[
226                pk!("id", "String"),
227                f!("organization_id", "String"),
228                f!("role", "String"),
229                f!("permission", "String"),
230                f!("created_at", "DateTimeUtc"),
231                f!("updated_at", "Option<DateTimeUtc>"),
232            ],
233        }],
234    },
235    PluginSchema {
236        name: "username",
237        user_fields: &[
238            f!("username", "Option<String>"),
239            f!("display_username", "Option<String>"),
240        ],
241        session_fields: &[],
242        extra_entities: &[],
243    },
244    PluginSchema {
245        name: "two-factor",
246        user_fields: &[f!("two_factor_enabled", "Option<bool>")],
247        session_fields: &[],
248        extra_entities: &[ExtraEntitySchema {
249            mod_name: "two_factor",
250            table_name: "two_factor",
251            role: None,
252            fields: &[
253                pk!("id", "String"),
254                f!("secret", "String"),
255                f!("backup_codes", "String"),
256                f!("user_id", "String"),
257                f!("verified", "Option<bool>"),
258                f!("failed_verification_count", "Option<f64>"),
259                f!("locked_until", "Option<DateTimeUtc>"),
260                f!("created_at", "DateTimeUtc"),
261                f!("updated_at", "DateTimeUtc"),
262            ],
263        }],
264    },
265    PluginSchema {
266        name: "device-authorization",
267        user_fields: &[],
268        session_fields: &[],
269        extra_entities: &[ExtraEntitySchema {
270            mod_name: "device_code",
271            table_name: "device_code",
272            role: None,
273            fields: &[
274                pk!("id", "String"),
275                f!("device_code", "String"),
276                f!("user_code", "String"),
277                f!("user_id", "Option<String>"),
278                f!("expires_at", "DateTimeUtc"),
279                f!("status", "String"),
280                f!("last_polled_at", "Option<DateTimeUtc>"),
281                f!("polling_interval", "Option<i64>"),
282                f!("client_id", "Option<String>"),
283                f!("scope", "Option<String>"),
284            ],
285        }],
286    },
287    PluginSchema {
288        name: "admin",
289        user_fields: &[
290            f!("role", "Option<String>"),
291            f!("banned", "Option<bool>"),
292            f!("ban_reason", "Option<String>"),
293            f!("ban_expires", "Option<DateTimeUtc>"),
294            f!("metadata", "Json"),
295        ],
296        session_fields: &[f!("impersonated_by", "Option<String>")],
297        extra_entities: &[],
298    },
299    PluginSchema {
300        name: "organization",
301        user_fields: &[],
302        session_fields: &[f!("active_organization_id", "Option<String>")],
303        extra_entities: &[
304            ExtraEntitySchema {
305                mod_name: "organization",
306                table_name: "organization",
307                role: None,
308                fields: &[
309                    pk!("id", "String"),
310                    f!("name", "String"),
311                    f!("slug", "String"),
312                    f!("logo", "Option<String>"),
313                    f!("metadata", "Option<Json>"),
314                    f!("created_at", "DateTimeUtc"),
315                    f!("updated_at", "DateTimeUtc"),
316                ],
317            },
318            ExtraEntitySchema {
319                mod_name: "member",
320                table_name: "member",
321                role: None,
322                fields: &[
323                    pk!("id", "String"),
324                    f!("organization_id", "String"),
325                    f!("user_id", "String"),
326                    f!("role", "String"),
327                    f!("created_at", "DateTimeUtc"),
328                ],
329            },
330            ExtraEntitySchema {
331                mod_name: "invitation",
332                table_name: "invitation",
333                role: None,
334                fields: &[
335                    pk!("id", "String"),
336                    f!("organization_id", "String"),
337                    f!("email", "String"),
338                    f!("role", "String"),
339                    f!("team_id", "Option<String>"),
340                    f!("status", "String"),
341                    f!("inviter_id", "String"),
342                    f!("expires_at", "DateTimeUtc"),
343                    f!("created_at", "DateTimeUtc"),
344                ],
345            },
346        ],
347    },
348    PluginSchema {
349        name: "api-key",
350        user_fields: &[],
351        session_fields: &[],
352        extra_entities: &[ExtraEntitySchema {
353            mod_name: "api_key",
354            table_name: "api_keys",
355            role: None,
356            fields: &[
357                pk!("id", "String"),
358                f!("name", "Option<String>"),
359                f!("start", "Option<String>"),
360                f!("prefix", "Option<String>"),
361                f_col!("key_hash", "String", "key"),
362                f!("reference_id", "String"),
363                f!("config_id", "String", "default"),
364                f!("refill_interval", "Option<f64>"),
365                f!("refill_amount", "Option<f64>"),
366                f!("last_refill_at", "Option<DateTimeUtc>"),
367                f!("enabled", "bool"),
368                f!("rate_limit_enabled", "bool"),
369                f!("rate_limit_time_window", "Option<f64>"),
370                f!("rate_limit_max", "Option<f64>"),
371                f!("request_count", "Option<f64>"),
372                f!("remaining", "Option<f64>"),
373                f!("last_request", "Option<DateTimeUtc>"),
374                f!("expires_at", "Option<DateTimeUtc>"),
375                f!("created_at", "DateTimeUtc"),
376                f!("updated_at", "DateTimeUtc"),
377                f!("permissions", "Option<String>"),
378                f!("metadata", "Option<String>"),
379            ],
380        }],
381    },
382    PluginSchema {
383        name: "passkey",
384        user_fields: &[],
385        session_fields: &[],
386        extra_entities: &[ExtraEntitySchema {
387            mod_name: "passkey",
388            table_name: "passkeys",
389            role: None,
390            fields: &[
391                pk!("id", "String"),
392                f!("name", "Option<String>"),
393                f!("public_key", "String"),
394                f!("user_id", "String"),
395                f!("credential_id", "String"),
396                f!("counter", "i64"),
397                f!("device_type", "String"),
398                f!("backed_up", "bool"),
399                f!("transports", "Option<String>"),
400                f!("credential", "String"),
401                f!("aaguid", "Option<String>"),
402                f!("created_at", "DateTimeUtc"),
403                f!("updated_at", "DateTimeUtc"),
404            ],
405        }],
406    },
407];
408
409/// Core fields that are always required for a given entity role.
410#[must_use]
411pub fn core_fields(role: EntityRole) -> &'static [FieldDef] {
412    match role {
413        EntityRole::User => USER_CORE,
414        EntityRole::Session => SESSION_CORE,
415        EntityRole::Account => ACCOUNT_CORE,
416        EntityRole::Verification => VERIFICATION_CORE,
417    }
418}
419
420/// Plugin schemas — each plugin can add fields to user and/or session entities.
421#[must_use]
422pub fn plugin_schemas() -> &'static [PluginSchema] {
423    PLUGINS
424}
425
426/// All plugin field names for a given role (convenience for the macro).
427#[must_use]
428pub fn plugin_field_names(role: EntityRole) -> Vec<&'static str> {
429    plugin_schemas()
430        .iter()
431        .flat_map(|p| match role {
432            EntityRole::User => p.user_fields.iter(),
433            EntityRole::Session => p.session_fields.iter(),
434            EntityRole::Account | EntityRole::Verification => [].iter(),
435        })
436        .map(|f| f.name)
437        .collect()
438}
439
440/// Core field names only (convenience for the macro).
441#[must_use]
442pub fn core_field_names(role: EntityRole) -> Vec<&'static str> {
443    core_fields(role).iter().map(|f| f.name).collect()
444}