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 pub anonymous: bool,
123}
124
125#[derive(Debug)]
126pub struct AuthLogin {
127 pub tn_id: TnId,
128 pub id_tag: Box<str>,
129 pub roles: Option<Box<[Box<str>]>>,
130 pub token: Box<str>,
131}
132
133#[derive(Debug)]
135pub struct KeyPair {
136 pub private_key: Box<str>,
137 pub public_key: Box<str>,
138}
139
140#[derive(Debug)]
141pub struct Webauthn<'a> {
142 pub credential_id: &'a str,
143 pub counter: u32,
144 pub public_key: &'a str,
145 pub description: Option<&'a str>,
146}
147
148#[derive(Debug)]
150pub struct CreateTenantData<'a> {
151 pub vfy_code: Option<&'a str>,
152 pub email: Option<&'a str>,
153 pub password: Option<&'a str>,
154 pub roles: Option<&'a [&'a str]>,
155}
156
157#[skip_serializing_none]
159#[derive(Debug, Clone, Deserialize, Serialize)]
160#[serde(rename_all = "camelCase")]
161pub struct TenantListItem {
162 pub tn_id: TnId,
163 pub id_tag: Box<str>,
164 pub email: Option<Box<str>>,
165 pub roles: Option<Box<[Box<str>]>>,
166 pub status: Option<Box<str>>,
167 #[serde(serialize_with = "serialize_timestamp_iso")]
168 pub created_at: Timestamp,
169}
170
171#[derive(Debug, Default)]
173pub struct ListTenantsOptions<'a> {
174 pub status: Option<&'a str>,
175 pub q: Option<&'a str>,
176 pub limit: Option<u32>,
177 pub offset: Option<u32>,
178}
179
180#[derive(Debug)]
182pub struct CertData {
183 pub tn_id: TnId,
184 pub id_tag: Box<str>,
185 pub domain: Box<str>,
186 pub cert: Box<str>,
187 pub key: Box<str>,
188 pub expires_at: Timestamp,
189 pub last_renewal_attempt_at: Option<Timestamp>,
190 pub last_renewal_error: Option<Box<str>>,
191 pub failure_count: u32,
192 pub notified_at: Option<Timestamp>,
193}
194
195#[derive(Debug)]
199pub struct TenantCertRenewalRow {
200 pub tn_id: TnId,
201 pub id_tag: Box<str>,
202 pub expires_at: Option<Timestamp>,
204 pub failure_count: u32,
205 pub last_renewal_error: Option<Box<str>>,
206 pub notified_at: Option<Timestamp>,
207}
208
209#[skip_serializing_none]
211#[derive(Debug, Clone, Deserialize, Serialize)]
212#[serde(rename_all = "camelCase")]
213pub struct ApiKeyInfo {
214 pub key_id: i64,
215 pub key_prefix: Box<str>,
216 pub name: Option<Box<str>>,
217 pub scopes: Option<Box<str>>,
218 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
219 pub expires_at: Option<Timestamp>,
220 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
221 pub last_used_at: Option<Timestamp>,
222 #[serde(serialize_with = "serialize_timestamp_iso")]
223 pub created_at: Timestamp,
224}
225
226#[derive(Debug)]
228pub struct CreateApiKeyOptions<'a> {
229 pub name: Option<&'a str>,
230 pub scopes: Option<&'a str>,
231 pub expires_at: Option<Timestamp>,
232}
233
234#[derive(Debug)]
236pub struct CreatedApiKey {
237 pub info: ApiKeyInfo,
238 pub plaintext_key: Box<str>,
239}
240
241#[derive(Debug)]
243pub struct ApiKeyValidation {
244 pub tn_id: TnId,
245 pub id_tag: Box<str>,
246 pub key_id: i64,
247 pub scopes: Option<Box<str>>,
248 pub roles: Option<Box<str>>,
249}
250
251#[skip_serializing_none]
256#[derive(Debug, Clone, Default, Serialize, Deserialize)]
257#[serde(rename_all = "camelCase")]
258pub struct ProxySiteConfig {
259 pub connect_timeout_secs: Option<u32>,
260 pub read_timeout_secs: Option<u32>,
261 pub preserve_host: Option<bool>,
262 pub proxy_protocol: Option<bool>,
263 pub custom_headers: Option<HashMap<String, String>>,
264 pub forward_headers: Option<bool>,
265 pub websocket: Option<bool>,
266}
267
268#[skip_serializing_none]
270#[derive(Debug, Clone, Serialize, Deserialize)]
271#[serde(rename_all = "camelCase")]
272pub struct ProxySiteData {
273 pub site_id: i64,
274 pub domain: Box<str>,
275 pub backend_url: Box<str>,
276 pub status: Box<str>,
277 #[serde(rename = "type")]
278 pub proxy_type: Box<str>,
279 #[serde(skip_serializing)]
280 pub cert: Option<Box<str>>,
281 #[serde(skip_serializing)]
282 pub cert_key: Option<Box<str>>,
283 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
284 pub cert_expires_at: Option<Timestamp>,
285 pub config: ProxySiteConfig,
286 pub created_by: Option<i64>,
287 #[serde(serialize_with = "serialize_timestamp_iso")]
288 pub created_at: Timestamp,
289 #[serde(serialize_with = "serialize_timestamp_iso")]
290 pub updated_at: Timestamp,
291}
292
293#[derive(Debug)]
295pub struct CreateProxySiteData<'a> {
296 pub domain: &'a str,
297 pub backend_url: &'a str,
298 pub proxy_type: &'a str,
299 pub config: &'a ProxySiteConfig,
300 pub created_by: Option<i64>,
301}
302
303#[derive(Debug)]
305pub struct UpdateProxySiteData<'a> {
306 pub backend_url: Option<&'a str>,
307 pub status: Option<&'a str>,
308 pub proxy_type: Option<&'a str>,
309 pub config: Option<&'a ProxySiteConfig>,
310}
311
312#[async_trait]
318pub trait AuthAdapter: Debug + Send + Sync {
319 async fn validate_access_token(
321 &self,
322 tn_id: TnId,
323 id_tag: &str,
324 token: &str,
325 ) -> ClResult<AuthCtx>;
326
327 async fn read_id_tag(&self, tn_id: TnId) -> ClResult<Box<str>>;
330
331 async fn read_tn_id(&self, id_tag: &str) -> ClResult<TnId>;
333
334 async fn read_tenant(&self, id_tag: &str) -> ClResult<AuthProfile>;
336
337 async fn create_tenant_registration(&self, email: &str) -> ClResult<()>;
339
340 async fn create_tenant(&self, id_tag: &str, data: CreateTenantData<'_>) -> ClResult<TnId>;
342
343 async fn delete_tenant(&self, id_tag: &str) -> ClResult<()>;
345
346 async fn list_tenants(&self, opts: &ListTenantsOptions<'_>) -> ClResult<Vec<TenantListItem>>;
348
349 async fn count_tenants(&self, opts: &ListTenantsOptions<'_>) -> ClResult<usize>;
351
352 async fn create_tenant_login(&self, id_tag: &str) -> ClResult<AuthLogin>;
354 async fn check_tenant_password(&self, id_tag: &str, password: &str) -> ClResult<AuthLogin>;
355 async fn update_tenant_password(&self, id_tag: &str, password: &str) -> ClResult<()>;
356
357 async fn update_idp_api_key(&self, id_tag: &str, api_key: &str) -> ClResult<()>;
359
360 async fn create_cert(&self, cert_data: &CertData) -> ClResult<()>;
362 async fn read_cert_by_tn_id(&self, tn_id: TnId) -> ClResult<CertData>;
363 async fn read_cert_by_id_tag(&self, id_tag: &str) -> ClResult<CertData>;
364 async fn read_cert_by_domain(&self, domain: &str) -> ClResult<CertData>;
365 async fn list_all_certs(&self) -> ClResult<Vec<CertData>>;
366 async fn list_tenants_needing_cert_renewal(
367 &self,
368 renewal_days: u32,
369 ) -> ClResult<Vec<TenantCertRenewalRow>>;
370
371 async fn record_cert_renewal_failure(&self, tn_id: TnId, error: &str) -> ClResult<()>;
375
376 async fn record_cert_renewal_success(&self, tn_id: TnId) -> ClResult<()>;
379
380 async fn record_cert_renewal_notification(&self, tn_id: TnId) -> ClResult<()>;
382
383 async fn update_tenant_status(&self, tn_id: TnId, status: char) -> ClResult<()>;
389
390 async fn list_profile_keys(&self, tn_id: TnId) -> ClResult<Vec<AuthKey>>;
392 async fn read_profile_key(&self, tn_id: TnId, key_id: &str) -> ClResult<AuthKey>;
393 async fn create_profile_key(
394 &self,
395 tn_id: TnId,
396 expires_at: Option<Timestamp>,
397 ) -> ClResult<AuthKey>;
398
399 async fn create_access_token(
400 &self,
401 tn_id: TnId,
402 data: &AccessToken<&str>,
403 ) -> ClResult<Box<str>>;
404 async fn create_action_token(
405 &self,
406 tn_id: TnId,
407 data: action_types::CreateAction,
408 ) -> ClResult<Box<str>>;
409 async fn verify_access_token(&self, token: &str) -> ClResult<()>;
410
411 async fn read_vapid_key(&self, tn_id: TnId) -> ClResult<KeyPair>;
413 async fn read_vapid_public_key(&self, tn_id: TnId) -> ClResult<Box<str>>;
414 async fn create_vapid_key(&self, tn_id: TnId) -> ClResult<KeyPair>;
415 async fn update_vapid_key(&self, tn_id: TnId, key: &KeyPair) -> ClResult<()>;
416
417 async fn read_var(&self, tn_id: TnId, var: &str) -> ClResult<Box<str>>;
419 async fn update_var(&self, tn_id: TnId, var: &str, value: &str) -> ClResult<()>;
420
421 async fn list_webauthn_credentials(&self, tn_id: TnId) -> ClResult<Box<[Webauthn]>>;
423 async fn read_webauthn_credential(
424 &self,
425 tn_id: TnId,
426 credential_id: &str,
427 ) -> ClResult<Webauthn>;
428 async fn create_webauthn_credential(&self, tn_id: TnId, data: &Webauthn) -> ClResult<()>;
429 async fn update_webauthn_credential_counter(
430 &self,
431 tn_id: TnId,
432 credential_id: &str,
433 counter: u32,
434 ) -> ClResult<()>;
435 async fn delete_webauthn_credential(&self, tn_id: TnId, credential_id: &str) -> ClResult<()>;
436
437 async fn create_api_key(
439 &self,
440 tn_id: TnId,
441 opts: CreateApiKeyOptions<'_>,
442 ) -> ClResult<CreatedApiKey>;
443 async fn validate_api_key(&self, key: &str) -> ClResult<ApiKeyValidation>;
444 async fn list_api_keys(&self, tn_id: TnId) -> ClResult<Vec<ApiKeyInfo>>;
445 async fn read_api_key(&self, tn_id: TnId, key_id: i64) -> ClResult<ApiKeyInfo>;
446 async fn update_api_key(
447 &self,
448 tn_id: TnId,
449 key_id: i64,
450 name: Option<&str>,
451 scopes: Option<&str>,
452 expires_at: Option<Timestamp>,
453 ) -> ClResult<ApiKeyInfo>;
454 async fn delete_api_key(&self, tn_id: TnId, key_id: i64) -> ClResult<()>;
455 async fn cleanup_expired_api_keys(&self) -> ClResult<u32>;
456 async fn cleanup_expired_verification_codes(&self) -> ClResult<u32>;
457
458 async fn create_proxy_site(&self, data: &CreateProxySiteData<'_>) -> ClResult<ProxySiteData>;
460 async fn read_proxy_site(&self, site_id: i64) -> ClResult<ProxySiteData>;
461 async fn read_proxy_site_by_domain(&self, domain: &str) -> ClResult<ProxySiteData>;
462 async fn update_proxy_site(
463 &self,
464 site_id: i64,
465 data: &UpdateProxySiteData<'_>,
466 ) -> ClResult<ProxySiteData>;
467 async fn delete_proxy_site(&self, site_id: i64) -> ClResult<()>;
468 async fn list_proxy_sites(&self) -> ClResult<Vec<ProxySiteData>>;
469 async fn update_proxy_site_cert(
470 &self,
471 site_id: i64,
472 cert: &str,
473 key: &str,
474 expires_at: Timestamp,
475 ) -> ClResult<()>;
476 async fn list_proxy_sites_needing_cert_renewal(
477 &self,
478 renewal_days: u32,
479 ) -> ClResult<Vec<ProxySiteData>>;
480}
481
482#[cfg(test)]
483mod tests {
484 use super::*;
485
486 #[test]
487 pub fn test_access_token() {
488 let token: AccessToken<String> = AccessToken {
489 iss: "a@a".into(),
490 sub: Some("b@b".into()),
491 scope: None,
492 r: None,
493 exp: Timestamp::now(),
494 };
495
496 assert_eq!(token.iss, "a@a");
497 assert_eq!(token.sub.as_ref().unwrap(), "b@b");
498 }
499}
500
501