1use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8use serde_with::skip_serializing_none;
9use std::fmt::Debug;
10
11use std::collections::HashMap;
12
13use crate::{
14 action_types,
15 prelude::*,
16 types::{serialize_timestamp_iso, serialize_timestamp_iso_opt},
17};
18
19pub const ACCESS_TOKEN_EXPIRY: i64 = 3600;
20
21#[skip_serializing_none]
26#[derive(Debug, Clone, Default, Deserialize, Serialize)]
27pub struct ActionToken {
28 pub iss: Box<str>,
30
31 pub k: Box<str>,
33
34 pub t: Box<str>,
36
37 pub c: Option<serde_json::Value>,
39
40 pub p: Option<Box<str>>,
42
43 pub a: Option<Vec<Box<str>>>,
45
46 pub aud: Option<Box<str>>,
48
49 pub sub: Option<Box<str>>,
51
52 pub iat: Timestamp,
54
55 pub exp: Option<Timestamp>,
57
58 pub f: Option<Box<str>>,
60
61 pub v: Option<char>,
63
64 #[serde(rename = "_", default, skip_serializing_if = "Option::is_none")]
66 pub nonce: Option<Box<str>>,
67}
68
69#[skip_serializing_none]
71#[derive(Clone, Debug, Deserialize, Serialize)]
72pub struct AccessToken<S> {
73 pub iss: S,
74 pub sub: Option<S>,
75 pub scope: Option<S>,
76 pub r: Option<S>,
77 pub exp: Timestamp,
78}
79
80#[skip_serializing_none]
82#[derive(Debug, Clone, Deserialize, Serialize)]
83pub struct AuthKey {
84 #[serde(rename = "keyId")]
85 pub key_id: Box<str>,
86 #[serde(rename = "publicKey")]
87 pub public_key: Box<str>,
88 #[serde(rename = "expiresAt", serialize_with = "serialize_timestamp_iso_opt")]
89 pub expires_at: Option<Timestamp>,
90}
91
92#[skip_serializing_none]
98#[derive(Debug, Deserialize, Serialize)]
99pub struct AuthProfile {
100 pub tn_id: TnId,
101 pub id_tag: Box<str>,
102 pub email: Option<Box<str>>,
103 pub roles: Option<Box<[Box<str>]>>,
104 pub status: Option<Box<str>>,
106 pub keys: Vec<AuthKey>,
107}
108
109#[derive(Clone, Debug)]
111pub struct AuthCtx {
112 pub tn_id: TnId,
113 pub id_tag: Box<str>,
114 pub roles: Box<[Box<str>]>,
115 pub scope: Option<Box<str>>,
116}
117
118#[derive(Debug)]
119pub struct AuthLogin {
120 pub tn_id: TnId,
121 pub id_tag: Box<str>,
122 pub roles: Option<Box<[Box<str>]>>,
123 pub token: Box<str>,
124}
125
126#[derive(Debug)]
128pub struct KeyPair {
129 pub private_key: Box<str>,
130 pub public_key: Box<str>,
131}
132
133#[derive(Debug)]
134pub struct Webauthn<'a> {
135 pub credential_id: &'a str,
136 pub counter: u32,
137 pub public_key: &'a str,
138 pub description: Option<&'a str>,
139}
140
141#[derive(Debug)]
143pub struct CreateTenantData<'a> {
144 pub vfy_code: Option<&'a str>,
145 pub email: Option<&'a str>,
146 pub password: Option<&'a str>,
147 pub roles: Option<&'a [&'a str]>,
148}
149
150#[skip_serializing_none]
152#[derive(Debug, Clone, Deserialize, Serialize)]
153#[serde(rename_all = "camelCase")]
154pub struct TenantListItem {
155 pub tn_id: TnId,
156 pub id_tag: Box<str>,
157 pub email: Option<Box<str>>,
158 pub roles: Option<Box<[Box<str>]>>,
159 pub status: Option<Box<str>>,
160 #[serde(serialize_with = "serialize_timestamp_iso")]
161 pub created_at: Timestamp,
162}
163
164#[derive(Debug, Default)]
166pub struct ListTenantsOptions<'a> {
167 pub status: Option<&'a str>,
168 pub q: Option<&'a str>,
169 pub limit: Option<u32>,
170 pub offset: Option<u32>,
171}
172
173#[derive(Debug)]
175pub struct CertData {
176 pub tn_id: TnId,
177 pub id_tag: Box<str>,
178 pub domain: Box<str>,
179 pub cert: Box<str>,
180 pub key: Box<str>,
181 pub expires_at: Timestamp,
182 pub last_renewal_attempt_at: Option<Timestamp>,
183 pub last_renewal_error: Option<Box<str>>,
184 pub failure_count: u32,
185 pub notified_at: Option<Timestamp>,
186}
187
188#[derive(Debug)]
192pub struct TenantCertRenewalRow {
193 pub tn_id: TnId,
194 pub id_tag: Box<str>,
195 pub expires_at: Option<Timestamp>,
197 pub failure_count: u32,
198 pub last_renewal_error: Option<Box<str>>,
199 pub notified_at: Option<Timestamp>,
200}
201
202#[skip_serializing_none]
204#[derive(Debug, Clone, Deserialize, Serialize)]
205#[serde(rename_all = "camelCase")]
206pub struct ApiKeyInfo {
207 pub key_id: i64,
208 pub key_prefix: Box<str>,
209 pub name: Option<Box<str>>,
210 pub scopes: Option<Box<str>>,
211 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
212 pub expires_at: Option<Timestamp>,
213 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
214 pub last_used_at: Option<Timestamp>,
215 #[serde(serialize_with = "serialize_timestamp_iso")]
216 pub created_at: Timestamp,
217}
218
219#[derive(Debug)]
221pub struct CreateApiKeyOptions<'a> {
222 pub name: Option<&'a str>,
223 pub scopes: Option<&'a str>,
224 pub expires_at: Option<Timestamp>,
225}
226
227#[derive(Debug)]
229pub struct CreatedApiKey {
230 pub info: ApiKeyInfo,
231 pub plaintext_key: Box<str>,
232}
233
234#[derive(Debug)]
236pub struct ApiKeyValidation {
237 pub tn_id: TnId,
238 pub id_tag: Box<str>,
239 pub key_id: i64,
240 pub scopes: Option<Box<str>>,
241 pub roles: Option<Box<str>>,
242}
243
244#[skip_serializing_none]
249#[derive(Debug, Clone, Default, Serialize, Deserialize)]
250#[serde(rename_all = "camelCase")]
251pub struct ProxySiteConfig {
252 pub connect_timeout_secs: Option<u32>,
253 pub read_timeout_secs: Option<u32>,
254 pub preserve_host: Option<bool>,
255 pub proxy_protocol: Option<bool>,
256 pub custom_headers: Option<HashMap<String, String>>,
257 pub forward_headers: Option<bool>,
258 pub websocket: Option<bool>,
259}
260
261#[skip_serializing_none]
263#[derive(Debug, Clone, Serialize, Deserialize)]
264#[serde(rename_all = "camelCase")]
265pub struct ProxySiteData {
266 pub site_id: i64,
267 pub domain: Box<str>,
268 pub backend_url: Box<str>,
269 pub status: Box<str>,
270 #[serde(rename = "type")]
271 pub proxy_type: Box<str>,
272 #[serde(skip_serializing)]
273 pub cert: Option<Box<str>>,
274 #[serde(skip_serializing)]
275 pub cert_key: Option<Box<str>>,
276 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
277 pub cert_expires_at: Option<Timestamp>,
278 pub config: ProxySiteConfig,
279 pub created_by: Option<i64>,
280 #[serde(serialize_with = "serialize_timestamp_iso")]
281 pub created_at: Timestamp,
282 #[serde(serialize_with = "serialize_timestamp_iso")]
283 pub updated_at: Timestamp,
284}
285
286#[derive(Debug)]
288pub struct CreateProxySiteData<'a> {
289 pub domain: &'a str,
290 pub backend_url: &'a str,
291 pub proxy_type: &'a str,
292 pub config: &'a ProxySiteConfig,
293 pub created_by: Option<i64>,
294}
295
296#[derive(Debug)]
298pub struct UpdateProxySiteData<'a> {
299 pub backend_url: Option<&'a str>,
300 pub status: Option<&'a str>,
301 pub proxy_type: Option<&'a str>,
302 pub config: Option<&'a ProxySiteConfig>,
303}
304
305#[async_trait]
311pub trait AuthAdapter: Debug + Send + Sync {
312 async fn validate_access_token(
314 &self,
315 tn_id: TnId,
316 id_tag: &str,
317 token: &str,
318 ) -> ClResult<AuthCtx>;
319
320 async fn read_id_tag(&self, tn_id: TnId) -> ClResult<Box<str>>;
323
324 async fn read_tn_id(&self, id_tag: &str) -> ClResult<TnId>;
326
327 async fn read_tenant(&self, id_tag: &str) -> ClResult<AuthProfile>;
329
330 async fn create_tenant_registration(&self, email: &str) -> ClResult<()>;
332
333 async fn create_tenant(&self, id_tag: &str, data: CreateTenantData<'_>) -> ClResult<TnId>;
335
336 async fn delete_tenant(&self, id_tag: &str) -> ClResult<()>;
338
339 async fn list_tenants(&self, opts: &ListTenantsOptions<'_>) -> ClResult<Vec<TenantListItem>>;
341
342 async fn count_tenants(&self, opts: &ListTenantsOptions<'_>) -> ClResult<usize>;
344
345 async fn create_tenant_login(&self, id_tag: &str) -> ClResult<AuthLogin>;
347 async fn check_tenant_password(&self, id_tag: &str, password: &str) -> ClResult<AuthLogin>;
348 async fn update_tenant_password(&self, id_tag: &str, password: &str) -> ClResult<()>;
349
350 async fn update_idp_api_key(&self, id_tag: &str, api_key: &str) -> ClResult<()>;
352
353 async fn create_cert(&self, cert_data: &CertData) -> ClResult<()>;
355 async fn read_cert_by_tn_id(&self, tn_id: TnId) -> ClResult<CertData>;
356 async fn read_cert_by_id_tag(&self, id_tag: &str) -> ClResult<CertData>;
357 async fn read_cert_by_domain(&self, domain: &str) -> ClResult<CertData>;
358 async fn list_all_certs(&self) -> ClResult<Vec<CertData>>;
359 async fn list_tenants_needing_cert_renewal(
360 &self,
361 renewal_days: u32,
362 ) -> ClResult<Vec<TenantCertRenewalRow>>;
363
364 async fn record_cert_renewal_failure(&self, tn_id: TnId, error: &str) -> ClResult<()>;
368
369 async fn record_cert_renewal_success(&self, tn_id: TnId) -> ClResult<()>;
372
373 async fn record_cert_renewal_notification(&self, tn_id: TnId) -> ClResult<()>;
375
376 async fn update_tenant_status(&self, tn_id: TnId, status: char) -> ClResult<()>;
382
383 async fn list_profile_keys(&self, tn_id: TnId) -> ClResult<Vec<AuthKey>>;
385 async fn read_profile_key(&self, tn_id: TnId, key_id: &str) -> ClResult<AuthKey>;
386 async fn create_profile_key(
387 &self,
388 tn_id: TnId,
389 expires_at: Option<Timestamp>,
390 ) -> ClResult<AuthKey>;
391
392 async fn create_access_token(
393 &self,
394 tn_id: TnId,
395 data: &AccessToken<&str>,
396 ) -> ClResult<Box<str>>;
397 async fn create_action_token(
398 &self,
399 tn_id: TnId,
400 data: action_types::CreateAction,
401 ) -> ClResult<Box<str>>;
402 async fn verify_access_token(&self, token: &str) -> ClResult<()>;
403
404 async fn read_vapid_key(&self, tn_id: TnId) -> ClResult<KeyPair>;
406 async fn read_vapid_public_key(&self, tn_id: TnId) -> ClResult<Box<str>>;
407 async fn create_vapid_key(&self, tn_id: TnId) -> ClResult<KeyPair>;
408 async fn update_vapid_key(&self, tn_id: TnId, key: &KeyPair) -> ClResult<()>;
409
410 async fn read_var(&self, tn_id: TnId, var: &str) -> ClResult<Box<str>>;
412 async fn update_var(&self, tn_id: TnId, var: &str, value: &str) -> ClResult<()>;
413
414 async fn list_webauthn_credentials(&self, tn_id: TnId) -> ClResult<Box<[Webauthn]>>;
416 async fn read_webauthn_credential(
417 &self,
418 tn_id: TnId,
419 credential_id: &str,
420 ) -> ClResult<Webauthn>;
421 async fn create_webauthn_credential(&self, tn_id: TnId, data: &Webauthn) -> ClResult<()>;
422 async fn update_webauthn_credential_counter(
423 &self,
424 tn_id: TnId,
425 credential_id: &str,
426 counter: u32,
427 ) -> ClResult<()>;
428 async fn delete_webauthn_credential(&self, tn_id: TnId, credential_id: &str) -> ClResult<()>;
429
430 async fn create_api_key(
432 &self,
433 tn_id: TnId,
434 opts: CreateApiKeyOptions<'_>,
435 ) -> ClResult<CreatedApiKey>;
436 async fn validate_api_key(&self, key: &str) -> ClResult<ApiKeyValidation>;
437 async fn list_api_keys(&self, tn_id: TnId) -> ClResult<Vec<ApiKeyInfo>>;
438 async fn read_api_key(&self, tn_id: TnId, key_id: i64) -> ClResult<ApiKeyInfo>;
439 async fn update_api_key(
440 &self,
441 tn_id: TnId,
442 key_id: i64,
443 name: Option<&str>,
444 scopes: Option<&str>,
445 expires_at: Option<Timestamp>,
446 ) -> ClResult<ApiKeyInfo>;
447 async fn delete_api_key(&self, tn_id: TnId, key_id: i64) -> ClResult<()>;
448 async fn cleanup_expired_api_keys(&self) -> ClResult<u32>;
449 async fn cleanup_expired_verification_codes(&self) -> ClResult<u32>;
450
451 async fn create_proxy_site(&self, data: &CreateProxySiteData<'_>) -> ClResult<ProxySiteData>;
453 async fn read_proxy_site(&self, site_id: i64) -> ClResult<ProxySiteData>;
454 async fn read_proxy_site_by_domain(&self, domain: &str) -> ClResult<ProxySiteData>;
455 async fn update_proxy_site(
456 &self,
457 site_id: i64,
458 data: &UpdateProxySiteData<'_>,
459 ) -> ClResult<ProxySiteData>;
460 async fn delete_proxy_site(&self, site_id: i64) -> ClResult<()>;
461 async fn list_proxy_sites(&self) -> ClResult<Vec<ProxySiteData>>;
462 async fn update_proxy_site_cert(
463 &self,
464 site_id: i64,
465 cert: &str,
466 key: &str,
467 expires_at: Timestamp,
468 ) -> ClResult<()>;
469 async fn list_proxy_sites_needing_cert_renewal(
470 &self,
471 renewal_days: u32,
472 ) -> ClResult<Vec<ProxySiteData>>;
473}
474
475#[cfg(test)]
476mod tests {
477 use super::*;
478
479 #[test]
480 pub fn test_access_token() {
481 let token: AccessToken<String> = AccessToken {
482 iss: "a@a".into(),
483 sub: Some("b@b".into()),
484 scope: None,
485 r: None,
486 exp: Timestamp::now(),
487 };
488
489 assert_eq!(token.iss, "a@a");
490 assert_eq!(token.sub.as_ref().unwrap(), "b@b");
491 }
492}
493
494