eqeqo_api_auth/
lib.rs

1use crate::auth::TokenManager;
2use crate::database::DB;
3use crate::handlers::*;
4pub mod auth;
5mod database;
6mod handlers;
7pub use httpageboy::{Request, Response, Rt, Server, StatusCode, handler};
8use std::sync::OnceLock;
9use tokio::time::{self, Duration};
10
11pub mod test_utils {
12  pub use httpageboy::test_utils::{run_test, setup_test_server};
13}
14
15pub fn active_test_server_url() -> &'static str {
16  httpageboy::test_utils::active_test_server_url()
17}
18
19fn build_cors_policy() -> httpageboy::CorsPolicy {
20  let mut policy = httpageboy::CorsPolicy::default();
21
22  if let Ok(origins) = std::env::var("CORS") {
23    if !origins.trim().is_empty() {
24      policy.allow_origin = origins;
25    }
26  }
27
28  let mut headers: Vec<String> = policy
29    .allow_headers
30    .split(',')
31    .map(|h| h.trim().to_string())
32    .filter(|h| !h.is_empty())
33    .collect();
34
35  let mut push_unique = |value: &str| {
36    if !headers.iter().any(|h| h.eq_ignore_ascii_case(value)) {
37      headers.push(value.to_string());
38    }
39  };
40
41  push_unique("token");
42  if let Ok(extra) = std::env::var("CORS_HEADERS") {
43    for header in extra.split(',').map(|h| h.trim()).filter(|h| !h.is_empty()) {
44      push_unique(header);
45    }
46  }
47
48  policy.allow_headers = headers.join(", ");
49  policy
50}
51
52static CLEANUP_JOB_STARTED: OnceLock<()> = OnceLock::new();
53
54fn spawn_cleanup_job() {
55  if CLEANUP_JOB_STARTED.set(()).is_err() {
56    return;
57  }
58  tokio::spawn(async move {
59    let mut ticker = time::interval(Duration::from_secs(60));
60    loop {
61      ticker.tick().await;
62      match DB::new().await {
63        Ok(db) => {
64          let manager = TokenManager::new(db.pool());
65          if let Err(err) = manager.cleanup_expired().await {
66            eprintln!("[cleanup] token cleanup failed: {}", err);
67          }
68        }
69        Err(err) => eprintln!("[cleanup] db unavailable: {}", err),
70      }
71    }
72  });
73}
74
75pub async fn create_server(server_url: &str) -> Server {
76  let mut server = Server::new(server_url, None)
77    .await
78    .expect("Failed to create server");
79
80  server.set_cors(build_cors_policy());
81  spawn_cleanup_job();
82
83  server.add_route("/", Rt::GET, handler!(home));
84
85  // Auth
86  server.add_route("/auth/login", Rt::POST, handler!(login));
87  server.add_route("/auth/logout", Rt::POST, handler!(logout));
88  server.add_route("/auth/profile", Rt::GET, handler!(profile));
89  server.add_route("/check-token", Rt::POST, handler!(check_token));
90
91  // Users
92  server.add_route("/users", Rt::GET, handler!(list_people));
93  server.add_route("/users", Rt::POST, handler!(create_user));
94  server.add_route("/users/{id}", Rt::GET, handler!(get_user));
95  server.add_route("/users/{id}", Rt::PUT, handler!(update_user));
96  server.add_route("/users/{id}", Rt::DELETE, handler!(delete_user));
97
98  // Services
99  server.add_route("/services", Rt::GET, handler!(list_services));
100  server.add_route("/services", Rt::POST, handler!(create_service));
101  server.add_route("/services/{id}", Rt::PUT, handler!(update_service));
102  server.add_route("/services/{id}", Rt::DELETE, handler!(delete_service));
103
104  // Roles
105  server.add_route("/roles", Rt::GET, handler!(list_roles));
106  server.add_route("/roles", Rt::POST, handler!(create_role));
107  server.add_route("/roles/{id}", Rt::GET, handler!(get_role));
108  server.add_route("/roles/{id}", Rt::PUT, handler!(update_role));
109  server.add_route("/roles/{id}", Rt::DELETE, handler!(delete_role));
110
111  // Permissions
112  server.add_route("/permissions", Rt::GET, handler!(list_permissions));
113  server.add_route("/permissions", Rt::POST, handler!(create_permission));
114  server.add_route("/permissions/{id}", Rt::PUT, handler!(update_permission));
115  server.add_route("/permissions/{id}", Rt::DELETE, handler!(delete_permission));
116
117  // Role-Permissions
118  server.add_route(
119    "/role-permissions",
120    Rt::POST,
121    handler!(assign_permission_to_role),
122  );
123  server.add_route(
124    "/role-permissions",
125    Rt::DELETE,
126    handler!(remove_permission_from_role),
127  );
128  server.add_route(
129    "/roles/{id}/permissions",
130    Rt::GET,
131    handler!(list_role_permissions),
132  );
133
134  // Service-Roles
135  server.add_route("/service-roles", Rt::POST, handler!(assign_role_to_service));
136  server.add_route(
137    "/service-roles",
138    Rt::DELETE,
139    handler!(remove_role_from_service),
140  );
141  server.add_route(
142    "/services/{id}/roles",
143    Rt::GET,
144    handler!(list_service_roles),
145  );
146
147  // Person-Service-Roles
148  server.add_route(
149    "/person-service-roles",
150    Rt::POST,
151    handler!(assign_role_to_person_in_service),
152  );
153  server.add_route(
154    "/person-service-roles",
155    Rt::DELETE,
156    handler!(remove_role_from_person_in_service),
157  );
158  server.add_route(
159    "/person-service-permissions",
160    Rt::POST,
161    handler!(grant_permission_to_person_in_service),
162  );
163  server.add_route(
164    "/people/{person_id}/services/{service_id}/roles",
165    Rt::GET,
166    handler!(list_person_roles_in_service),
167  );
168  server.add_route(
169    "/services/{service_id}/roles/{role_id}/people",
170    Rt::GET,
171    handler!(list_persons_with_role_in_service),
172  );
173  server.add_route(
174    "/people/{person_id}/services",
175    Rt::GET,
176    handler!(list_services_of_person),
177  );
178  server.add_route(
179    "/people/{person_id}/services/{service_id}",
180    Rt::GET,
181    handler!(get_person_service_info),
182  );
183
184  server
185}