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]
94#[derive(Debug, Deserialize, Serialize)]
95pub struct AuthProfile {
96 pub id_tag: Box<str>,
97 pub roles: Option<Box<[Box<str>]>>,
98 pub keys: Vec<AuthKey>,
99}
100
101#[derive(Clone, Debug)]
103pub struct AuthCtx {
104 pub tn_id: TnId,
105 pub id_tag: Box<str>,
106 pub roles: Box<[Box<str>]>,
107 pub scope: Option<Box<str>>,
108}
109
110#[derive(Debug)]
111pub struct AuthLogin {
112 pub tn_id: TnId,
113 pub id_tag: Box<str>,
114 pub roles: Option<Box<[Box<str>]>>,
115 pub token: Box<str>,
116}
117
118#[derive(Debug)]
120pub struct KeyPair {
121 pub private_key: Box<str>,
122 pub public_key: Box<str>,
123}
124
125#[derive(Debug)]
126pub struct Webauthn<'a> {
127 pub credential_id: &'a str,
128 pub counter: u32,
129 pub public_key: &'a str,
130 pub description: Option<&'a str>,
131}
132
133#[derive(Debug)]
135pub struct CreateTenantData<'a> {
136 pub vfy_code: Option<&'a str>,
137 pub email: Option<&'a str>,
138 pub password: Option<&'a str>,
139 pub roles: Option<&'a [&'a str]>,
140}
141
142#[skip_serializing_none]
144#[derive(Debug, Clone, Deserialize, Serialize)]
145#[serde(rename_all = "camelCase")]
146pub struct TenantListItem {
147 pub tn_id: TnId,
148 pub id_tag: Box<str>,
149 pub email: Option<Box<str>>,
150 pub roles: Option<Box<[Box<str>]>>,
151 pub status: Option<Box<str>>,
152 #[serde(serialize_with = "serialize_timestamp_iso")]
153 pub created_at: Timestamp,
154}
155
156#[derive(Debug, Default)]
158pub struct ListTenantsOptions<'a> {
159 pub status: Option<&'a str>,
160 pub q: Option<&'a str>,
161 pub limit: Option<u32>,
162 pub offset: Option<u32>,
163}
164
165#[derive(Debug)]
167pub struct CertData {
168 pub tn_id: TnId,
169 pub id_tag: Box<str>,
170 pub domain: Box<str>,
171 pub cert: Box<str>,
172 pub key: Box<str>,
173 pub expires_at: Timestamp,
174}
175
176#[skip_serializing_none]
178#[derive(Debug, Clone, Deserialize, Serialize)]
179#[serde(rename_all = "camelCase")]
180pub struct ApiKeyInfo {
181 pub key_id: i64,
182 pub key_prefix: Box<str>,
183 pub name: Option<Box<str>>,
184 pub scopes: Option<Box<str>>,
185 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
186 pub expires_at: Option<Timestamp>,
187 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
188 pub last_used_at: Option<Timestamp>,
189 #[serde(serialize_with = "serialize_timestamp_iso")]
190 pub created_at: Timestamp,
191}
192
193#[derive(Debug)]
195pub struct CreateApiKeyOptions<'a> {
196 pub name: Option<&'a str>,
197 pub scopes: Option<&'a str>,
198 pub expires_at: Option<Timestamp>,
199}
200
201#[derive(Debug)]
203pub struct CreatedApiKey {
204 pub info: ApiKeyInfo,
205 pub plaintext_key: Box<str>,
206}
207
208#[derive(Debug)]
210pub struct ApiKeyValidation {
211 pub tn_id: TnId,
212 pub id_tag: Box<str>,
213 pub key_id: i64,
214 pub scopes: Option<Box<str>>,
215 pub roles: Option<Box<str>>,
216}
217
218#[skip_serializing_none]
223#[derive(Debug, Clone, Default, Serialize, Deserialize)]
224#[serde(rename_all = "camelCase")]
225pub struct ProxySiteConfig {
226 pub connect_timeout_secs: Option<u32>,
227 pub read_timeout_secs: Option<u32>,
228 pub preserve_host: Option<bool>,
229 pub proxy_protocol: Option<bool>,
230 pub custom_headers: Option<HashMap<String, String>>,
231 pub forward_headers: Option<bool>,
232 pub websocket: Option<bool>,
233}
234
235#[skip_serializing_none]
237#[derive(Debug, Clone, Serialize, Deserialize)]
238#[serde(rename_all = "camelCase")]
239pub struct ProxySiteData {
240 pub site_id: i64,
241 pub domain: Box<str>,
242 pub backend_url: Box<str>,
243 pub status: Box<str>,
244 #[serde(rename = "type")]
245 pub proxy_type: Box<str>,
246 #[serde(skip_serializing)]
247 pub cert: Option<Box<str>>,
248 #[serde(skip_serializing)]
249 pub cert_key: Option<Box<str>>,
250 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
251 pub cert_expires_at: Option<Timestamp>,
252 pub config: ProxySiteConfig,
253 pub created_by: Option<i64>,
254 #[serde(serialize_with = "serialize_timestamp_iso")]
255 pub created_at: Timestamp,
256 #[serde(serialize_with = "serialize_timestamp_iso")]
257 pub updated_at: Timestamp,
258}
259
260#[derive(Debug)]
262pub struct CreateProxySiteData<'a> {
263 pub domain: &'a str,
264 pub backend_url: &'a str,
265 pub proxy_type: &'a str,
266 pub config: &'a ProxySiteConfig,
267 pub created_by: Option<i64>,
268}
269
270#[derive(Debug)]
272pub struct UpdateProxySiteData<'a> {
273 pub backend_url: Option<&'a str>,
274 pub status: Option<&'a str>,
275 pub proxy_type: Option<&'a str>,
276 pub config: Option<&'a ProxySiteConfig>,
277}
278
279#[async_trait]
285pub trait AuthAdapter: Debug + Send + Sync {
286 async fn validate_access_token(&self, tn_id: TnId, token: &str) -> ClResult<AuthCtx>;
288
289 async fn read_id_tag(&self, tn_id: TnId) -> ClResult<Box<str>>;
292
293 async fn read_tn_id(&self, id_tag: &str) -> ClResult<TnId>;
295
296 async fn read_tenant(&self, id_tag: &str) -> ClResult<AuthProfile>;
298
299 async fn create_tenant_registration(&self, email: &str) -> ClResult<()>;
301
302 async fn create_tenant(&self, id_tag: &str, data: CreateTenantData<'_>) -> ClResult<TnId>;
304
305 async fn delete_tenant(&self, id_tag: &str) -> ClResult<()>;
307
308 async fn list_tenants(&self, opts: &ListTenantsOptions<'_>) -> ClResult<Vec<TenantListItem>>;
310
311 async fn create_tenant_login(&self, id_tag: &str) -> ClResult<AuthLogin>;
313 async fn check_tenant_password(&self, id_tag: &str, password: &str) -> ClResult<AuthLogin>;
314 async fn update_tenant_password(&self, id_tag: &str, password: &str) -> ClResult<()>;
315
316 async fn update_idp_api_key(&self, id_tag: &str, api_key: &str) -> ClResult<()>;
318
319 async fn create_cert(&self, cert_data: &CertData) -> ClResult<()>;
321 async fn read_cert_by_tn_id(&self, tn_id: TnId) -> ClResult<CertData>;
322 async fn read_cert_by_id_tag(&self, id_tag: &str) -> ClResult<CertData>;
323 async fn read_cert_by_domain(&self, domain: &str) -> ClResult<CertData>;
324 async fn list_all_certs(&self) -> ClResult<Vec<CertData>>;
325 async fn list_tenants_needing_cert_renewal(
326 &self,
327 renewal_days: u32,
328 ) -> ClResult<Vec<(TnId, Box<str>)>>;
329
330 async fn list_profile_keys(&self, tn_id: TnId) -> ClResult<Vec<AuthKey>>;
332 async fn read_profile_key(&self, tn_id: TnId, key_id: &str) -> ClResult<AuthKey>;
333 async fn create_profile_key(
334 &self,
335 tn_id: TnId,
336 expires_at: Option<Timestamp>,
337 ) -> ClResult<AuthKey>;
338
339 async fn create_access_token(
340 &self,
341 tn_id: TnId,
342 data: &AccessToken<&str>,
343 ) -> ClResult<Box<str>>;
344 async fn create_action_token(
345 &self,
346 tn_id: TnId,
347 data: action_types::CreateAction,
348 ) -> ClResult<Box<str>>;
349 async fn verify_access_token(&self, token: &str) -> ClResult<()>;
350
351 async fn read_vapid_key(&self, tn_id: TnId) -> ClResult<KeyPair>;
353 async fn read_vapid_public_key(&self, tn_id: TnId) -> ClResult<Box<str>>;
354 async fn create_vapid_key(&self, tn_id: TnId) -> ClResult<KeyPair>;
355 async fn update_vapid_key(&self, tn_id: TnId, key: &KeyPair) -> ClResult<()>;
356
357 async fn read_var(&self, tn_id: TnId, var: &str) -> ClResult<Box<str>>;
359 async fn update_var(&self, tn_id: TnId, var: &str, value: &str) -> ClResult<()>;
360
361 async fn list_webauthn_credentials(&self, tn_id: TnId) -> ClResult<Box<[Webauthn]>>;
363 async fn read_webauthn_credential(
364 &self,
365 tn_id: TnId,
366 credential_id: &str,
367 ) -> ClResult<Webauthn>;
368 async fn create_webauthn_credential(&self, tn_id: TnId, data: &Webauthn) -> ClResult<()>;
369 async fn update_webauthn_credential_counter(
370 &self,
371 tn_id: TnId,
372 credential_id: &str,
373 counter: u32,
374 ) -> ClResult<()>;
375 async fn delete_webauthn_credential(&self, tn_id: TnId, credential_id: &str) -> ClResult<()>;
376
377 async fn create_api_key(
379 &self,
380 tn_id: TnId,
381 opts: CreateApiKeyOptions<'_>,
382 ) -> ClResult<CreatedApiKey>;
383 async fn validate_api_key(&self, key: &str) -> ClResult<ApiKeyValidation>;
384 async fn list_api_keys(&self, tn_id: TnId) -> ClResult<Vec<ApiKeyInfo>>;
385 async fn read_api_key(&self, tn_id: TnId, key_id: i64) -> ClResult<ApiKeyInfo>;
386 async fn update_api_key(
387 &self,
388 tn_id: TnId,
389 key_id: i64,
390 name: Option<&str>,
391 scopes: Option<&str>,
392 expires_at: Option<Timestamp>,
393 ) -> ClResult<ApiKeyInfo>;
394 async fn delete_api_key(&self, tn_id: TnId, key_id: i64) -> ClResult<()>;
395 async fn cleanup_expired_api_keys(&self) -> ClResult<u32>;
396 async fn cleanup_expired_verification_codes(&self) -> ClResult<u32>;
397
398 async fn create_proxy_site(&self, data: &CreateProxySiteData<'_>) -> ClResult<ProxySiteData>;
400 async fn read_proxy_site(&self, site_id: i64) -> ClResult<ProxySiteData>;
401 async fn read_proxy_site_by_domain(&self, domain: &str) -> ClResult<ProxySiteData>;
402 async fn update_proxy_site(
403 &self,
404 site_id: i64,
405 data: &UpdateProxySiteData<'_>,
406 ) -> ClResult<ProxySiteData>;
407 async fn delete_proxy_site(&self, site_id: i64) -> ClResult<()>;
408 async fn list_proxy_sites(&self) -> ClResult<Vec<ProxySiteData>>;
409 async fn update_proxy_site_cert(
410 &self,
411 site_id: i64,
412 cert: &str,
413 key: &str,
414 expires_at: Timestamp,
415 ) -> ClResult<()>;
416 async fn list_proxy_sites_needing_cert_renewal(
417 &self,
418 renewal_days: u32,
419 ) -> ClResult<Vec<ProxySiteData>>;
420}
421
422#[cfg(test)]
423mod tests {
424 use super::*;
425
426 #[test]
427 pub fn test_access_token() {
428 let token: AccessToken<String> = AccessToken {
429 iss: "a@a".into(),
430 sub: Some("b@b".into()),
431 scope: None,
432 r: None,
433 exp: Timestamp::now(),
434 };
435
436 assert_eq!(token.iss, "a@a");
437 assert_eq!(token.sub.as_ref().unwrap(), "b@b");
438 }
439}
440
441