Skip to main content

auth/
module.rs

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}