1use crate::admin::AuthAdminData;
2use crate::repositories::PostgresAuthUserRepository;
3use platform_core::AppContext;
4use platform_http::ApiOpenApiRouter;
5use platform_module::{
6 AdminSchema, EntitySchema, FieldSchema, FieldType, LinkedBinding, LinkedHttpContribution,
7 Module, ModuleHttpMethod, ModuleHttpRoute, ModuleManifest,
8};
9use std::sync::Arc;
10
11pub const MODULE_NAME: &str = "auth";
12pub const AUTH_USERS_READ: &str = "auth.users.read";
13
14pub fn http_routes() -> Vec<ModuleHttpRoute> {
15 vec![
16 ModuleHttpRoute {
17 method: ModuleHttpMethod::Post,
18 path: "/v1/auth/dev/sessions".to_owned(),
19 capability: None,
20 display_name: Some("Create Development Session".to_owned()),
21 story_title: Some("Development Auth Session".to_owned()),
22 },
23 ModuleHttpRoute {
24 method: ModuleHttpMethod::Post,
25 path: "/v1/auth/sessions/revoke".to_owned(),
26 capability: None,
27 display_name: Some("Revoke Session".to_owned()),
28 story_title: Some("Auth Session Revoked".to_owned()),
29 },
30 ]
31}
32
33pub fn user_schema() -> AdminSchema {
34 AdminSchema {
35 entities: vec![EntitySchema {
36 name: "users".to_owned(),
37 label: "Users".to_owned(),
38 read_capability: AUTH_USERS_READ.to_owned(),
39 fields: vec![
40 FieldSchema {
41 name: "id".to_owned(),
42 label: "ID".to_owned(),
43 field_type: FieldType::String,
44 nullable: false,
45 },
46 FieldSchema {
47 name: "created_at".to_owned(),
48 label: "Created".to_owned(),
49 field_type: FieldType::Timestamp,
50 nullable: false,
51 },
52 FieldSchema {
53 name: "disabled_at".to_owned(),
54 label: "Disabled".to_owned(),
55 field_type: FieldType::Timestamp,
56 nullable: true,
57 },
58 ],
59 }],
60 }
61}
62
63pub fn manifest() -> ModuleManifest {
64 ModuleManifest::builder(MODULE_NAME)
65 .capabilities(vec![AUTH_USERS_READ.to_owned()])
66 .http_routes(http_routes())
67 .admin(user_schema())
68 .build()
69}
70
71pub fn merge_http(base: ApiOpenApiRouter) -> ApiOpenApiRouter {
72 base.merge(crate::routes::router())
73}
74
75pub fn binding() -> LinkedBinding {
76 LinkedBinding::builder()
77 .http(LinkedHttpContribution {
78 public_prefixes: &["/v1/auth/dev/", "/v1/auth/sessions/"],
79 merge: merge_http,
80 })
81 .build()
82}
83
84pub fn module(ctx: &AppContext) -> Module {
85 let repository = Arc::new(PostgresAuthUserRepository::new(ctx.db.clone()));
86 Module::linked(manifest(), binding()).with_admin_data(Arc::new(AuthAdminData::new(repository)))
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use platform_module::{ModuleManifestLintSeverity, ModuleSource, lint_module_manifest};
93
94 #[test]
95 fn manifest_declares_auth_user_anchor() {
96 let manifest = manifest();
97
98 assert_eq!(manifest.name, MODULE_NAME);
99 assert_eq!(manifest.capabilities, vec![AUTH_USERS_READ]);
100 assert_eq!(manifest.http_routes, http_routes());
101 assert_eq!(
102 manifest.admin,
103 Some(platform_module::AdminSurface::Schema(user_schema()))
104 );
105
106 let lints = lint_module_manifest(ModuleSource::Linked, &manifest);
107 assert!(
108 lints
109 .iter()
110 .all(|lint| lint.severity == ModuleManifestLintSeverity::Ok),
111 "auth manifest should not have warning/error lints: {lints:?}"
112 );
113 }
114}