1use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5use serde_with::skip_serializing_none;
6use std::fmt::Debug;
7
8use std::collections::HashMap;
9
10use crate::{
11 action_types,
12 prelude::*,
13 types::{serialize_timestamp_iso, serialize_timestamp_iso_opt},
14};
15
16pub const ACCESS_TOKEN_EXPIRY: i64 = 3600;
17
18#[skip_serializing_none]
23#[derive(Debug, Clone, Default, Deserialize, Serialize)]
24pub struct ActionToken {
25 pub iss: Box<str>,
27
28 pub k: Box<str>,
30
31 pub t: Box<str>,
33
34 pub c: Option<serde_json::Value>,
36
37 pub p: Option<Box<str>>,
39
40 pub a: Option<Vec<Box<str>>>,
42
43 pub aud: Option<Box<str>>,
45
46 pub sub: Option<Box<str>>,
48
49 pub iat: Timestamp,
51
52 pub exp: Option<Timestamp>,
54
55 pub f: Option<Box<str>>,
57
58 pub v: Option<char>,
60
61 #[serde(rename = "_", default, skip_serializing_if = "Option::is_none")]
63 pub nonce: Option<Box<str>>,
64}
65
66#[skip_serializing_none]
68#[derive(Clone, Debug, Deserialize, Serialize)]
69pub struct AccessToken<S> {
70 pub iss: S,
71 pub sub: Option<S>,
72 pub scope: Option<S>,
73 pub r: Option<S>,
74 pub exp: Timestamp,
75}
76
77#[skip_serializing_none]
79#[derive(Debug, Clone, Deserialize, Serialize)]
80pub struct AuthKey {
81 #[serde(rename = "keyId")]
82 pub key_id: Box<str>,
83 #[serde(rename = "publicKey")]
84 pub public_key: Box<str>,
85 #[serde(rename = "expiresAt", serialize_with = "serialize_timestamp_iso_opt")]
86 pub expires_at: Option<Timestamp>,
87}
88
89#[skip_serializing_none]
91#[derive(Debug, Deserialize, Serialize)]
92pub struct AuthProfile {
93 pub id_tag: Box<str>,
94 pub roles: Option<Box<[Box<str>]>>,
95 pub keys: Vec<AuthKey>,
96}
97
98#[derive(Clone, Debug)]
100pub struct AuthCtx {
101 pub tn_id: TnId,
102 pub id_tag: Box<str>,
103 pub roles: Box<[Box<str>]>,
104 pub scope: Option<Box<str>>,
105}
106
107#[derive(Debug)]
108pub struct AuthLogin {
109 pub tn_id: TnId,
110 pub id_tag: Box<str>,
111 pub roles: Option<Box<[Box<str>]>>,
112 pub token: Box<str>,
113}
114
115#[derive(Debug)]
117pub struct KeyPair {
118 pub private_key: Box<str>,
119 pub public_key: Box<str>,
120}
121
122#[derive(Debug)]
123pub struct Webauthn<'a> {
124 pub credential_id: &'a str,
125 pub counter: u32,
126 pub public_key: &'a str,
127 pub description: Option<&'a str>,
128}
129
130#[derive(Debug)]
132pub struct CreateTenantData<'a> {
133 pub vfy_code: Option<&'a str>,
134 pub email: Option<&'a str>,
135 pub password: Option<&'a str>,
136 pub roles: Option<&'a [&'a str]>,
137}
138
139#[skip_serializing_none]
141#[derive(Debug, Clone, Deserialize, Serialize)]
142#[serde(rename_all = "camelCase")]
143pub struct TenantListItem {
144 pub tn_id: TnId,
145 pub id_tag: Box<str>,
146 pub email: Option<Box<str>>,
147 pub roles: Option<Box<[Box<str>]>>,
148 pub status: Option<Box<str>>,
149 #[serde(serialize_with = "serialize_timestamp_iso")]
150 pub created_at: Timestamp,
151}
152
153#[derive(Debug, Default)]
155pub struct ListTenantsOptions<'a> {
156 pub status: Option<&'a str>,
157 pub q: Option<&'a str>,
158 pub limit: Option<u32>,
159 pub offset: Option<u32>,
160}
161
162#[derive(Debug)]
164pub struct CertData {
165 pub tn_id: TnId,
166 pub id_tag: Box<str>,
167 pub domain: Box<str>,
168 pub cert: Box<str>,
169 pub key: Box<str>,
170 pub expires_at: Timestamp,
171}
172
173#[skip_serializing_none]
175#[derive(Debug, Clone, Deserialize, Serialize)]
176#[serde(rename_all = "camelCase")]
177pub struct ApiKeyInfo {
178 pub key_id: i64,
179 pub key_prefix: Box<str>,
180 pub name: Option<Box<str>>,
181 pub scopes: Option<Box<str>>,
182 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
183 pub expires_at: Option<Timestamp>,
184 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
185 pub last_used_at: Option<Timestamp>,
186 #[serde(serialize_with = "serialize_timestamp_iso")]
187 pub created_at: Timestamp,
188}
189
190#[derive(Debug)]
192pub struct CreateApiKeyOptions<'a> {
193 pub name: Option<&'a str>,
194 pub scopes: Option<&'a str>,
195 pub expires_at: Option<Timestamp>,
196}
197
198#[derive(Debug)]
200pub struct CreatedApiKey {
201 pub info: ApiKeyInfo,
202 pub plaintext_key: Box<str>,
203}
204
205#[derive(Debug)]
207pub struct ApiKeyValidation {
208 pub tn_id: TnId,
209 pub id_tag: Box<str>,
210 pub key_id: i64,
211 pub scopes: Option<Box<str>>,
212 pub roles: Option<Box<str>>,
213}
214
215#[skip_serializing_none]
220#[derive(Debug, Clone, Default, Serialize, Deserialize)]
221#[serde(rename_all = "camelCase")]
222pub struct ProxySiteConfig {
223 pub connect_timeout_secs: Option<u32>,
224 pub read_timeout_secs: Option<u32>,
225 pub preserve_host: Option<bool>,
226 pub proxy_protocol: Option<bool>,
227 pub custom_headers: Option<HashMap<String, String>>,
228 pub forward_headers: Option<bool>,
229 pub websocket: Option<bool>,
230}
231
232#[skip_serializing_none]
234#[derive(Debug, Clone, Serialize, Deserialize)]
235#[serde(rename_all = "camelCase")]
236pub struct ProxySiteData {
237 pub site_id: i64,
238 pub domain: Box<str>,
239 pub backend_url: Box<str>,
240 pub status: Box<str>,
241 #[serde(rename = "type")]
242 pub proxy_type: Box<str>,
243 #[serde(skip_serializing)]
244 pub cert: Option<Box<str>>,
245 #[serde(skip_serializing)]
246 pub cert_key: Option<Box<str>>,
247 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
248 pub cert_expires_at: Option<Timestamp>,
249 pub config: ProxySiteConfig,
250 pub created_by: Option<i64>,
251 #[serde(serialize_with = "serialize_timestamp_iso")]
252 pub created_at: Timestamp,
253 #[serde(serialize_with = "serialize_timestamp_iso")]
254 pub updated_at: Timestamp,
255}
256
257#[derive(Debug)]
259pub struct CreateProxySiteData<'a> {
260 pub domain: &'a str,
261 pub backend_url: &'a str,
262 pub proxy_type: &'a str,
263 pub config: &'a ProxySiteConfig,
264 pub created_by: Option<i64>,
265}
266
267#[derive(Debug)]
269pub struct UpdateProxySiteData<'a> {
270 pub backend_url: Option<&'a str>,
271 pub status: Option<&'a str>,
272 pub proxy_type: Option<&'a str>,
273 pub config: Option<&'a ProxySiteConfig>,
274}
275
276#[async_trait]
282pub trait AuthAdapter: Debug + Send + Sync {
283 async fn validate_access_token(&self, tn_id: TnId, token: &str) -> ClResult<AuthCtx>;
285
286 async fn read_id_tag(&self, tn_id: TnId) -> ClResult<Box<str>>;
289
290 async fn read_tn_id(&self, id_tag: &str) -> ClResult<TnId>;
292
293 async fn read_tenant(&self, id_tag: &str) -> ClResult<AuthProfile>;
295
296 async fn create_tenant_registration(&self, email: &str) -> ClResult<()>;
298
299 async fn create_tenant(&self, id_tag: &str, data: CreateTenantData<'_>) -> ClResult<TnId>;
301
302 async fn delete_tenant(&self, id_tag: &str) -> ClResult<()>;
304
305 async fn list_tenants(&self, opts: &ListTenantsOptions<'_>) -> ClResult<Vec<TenantListItem>>;
307
308 async fn create_tenant_login(&self, id_tag: &str) -> ClResult<AuthLogin>;
310 async fn check_tenant_password(&self, id_tag: &str, password: &str) -> ClResult<AuthLogin>;
311 async fn update_tenant_password(&self, id_tag: &str, password: &str) -> ClResult<()>;
312
313 async fn update_idp_api_key(&self, id_tag: &str, api_key: &str) -> ClResult<()>;
315
316 async fn create_cert(&self, cert_data: &CertData) -> ClResult<()>;
318 async fn read_cert_by_tn_id(&self, tn_id: TnId) -> ClResult<CertData>;
319 async fn read_cert_by_id_tag(&self, id_tag: &str) -> ClResult<CertData>;
320 async fn read_cert_by_domain(&self, domain: &str) -> ClResult<CertData>;
321 async fn list_all_certs(&self) -> ClResult<Vec<CertData>>;
322 async fn list_tenants_needing_cert_renewal(
323 &self,
324 renewal_days: u32,
325 ) -> ClResult<Vec<(TnId, Box<str>)>>;
326
327 async fn list_profile_keys(&self, tn_id: TnId) -> ClResult<Vec<AuthKey>>;
329 async fn read_profile_key(&self, tn_id: TnId, key_id: &str) -> ClResult<AuthKey>;
330 async fn create_profile_key(
331 &self,
332 tn_id: TnId,
333 expires_at: Option<Timestamp>,
334 ) -> ClResult<AuthKey>;
335
336 async fn create_access_token(
337 &self,
338 tn_id: TnId,
339 data: &AccessToken<&str>,
340 ) -> ClResult<Box<str>>;
341 async fn create_action_token(
342 &self,
343 tn_id: TnId,
344 data: action_types::CreateAction,
345 ) -> ClResult<Box<str>>;
346 async fn create_proxy_token(
347 &self,
348 tn_id: TnId,
349 id_tag: &str,
350 roles: &[Box<str>],
351 ) -> ClResult<Box<str>>;
352 async fn verify_access_token(&self, token: &str) -> ClResult<()>;
353
354 async fn read_vapid_key(&self, tn_id: TnId) -> ClResult<KeyPair>;
356 async fn read_vapid_public_key(&self, tn_id: TnId) -> ClResult<Box<str>>;
357 async fn create_vapid_key(&self, tn_id: TnId) -> ClResult<KeyPair>;
358 async fn update_vapid_key(&self, tn_id: TnId, key: &KeyPair) -> ClResult<()>;
359
360 async fn read_var(&self, tn_id: TnId, var: &str) -> ClResult<Box<str>>;
362 async fn update_var(&self, tn_id: TnId, var: &str, value: &str) -> ClResult<()>;
363
364 async fn list_webauthn_credentials(&self, tn_id: TnId) -> ClResult<Box<[Webauthn]>>;
366 async fn read_webauthn_credential(
367 &self,
368 tn_id: TnId,
369 credential_id: &str,
370 ) -> ClResult<Webauthn>;
371 async fn create_webauthn_credential(&self, tn_id: TnId, data: &Webauthn) -> ClResult<()>;
372 async fn update_webauthn_credential_counter(
373 &self,
374 tn_id: TnId,
375 credential_id: &str,
376 counter: u32,
377 ) -> ClResult<()>;
378 async fn delete_webauthn_credential(&self, tn_id: TnId, credential_id: &str) -> ClResult<()>;
379
380 async fn create_api_key(
382 &self,
383 tn_id: TnId,
384 opts: CreateApiKeyOptions<'_>,
385 ) -> ClResult<CreatedApiKey>;
386 async fn validate_api_key(&self, key: &str) -> ClResult<ApiKeyValidation>;
387 async fn list_api_keys(&self, tn_id: TnId) -> ClResult<Vec<ApiKeyInfo>>;
388 async fn read_api_key(&self, tn_id: TnId, key_id: i64) -> ClResult<ApiKeyInfo>;
389 async fn update_api_key(
390 &self,
391 tn_id: TnId,
392 key_id: i64,
393 name: Option<&str>,
394 scopes: Option<&str>,
395 expires_at: Option<Timestamp>,
396 ) -> ClResult<ApiKeyInfo>;
397 async fn delete_api_key(&self, tn_id: TnId, key_id: i64) -> ClResult<()>;
398 async fn cleanup_expired_api_keys(&self) -> ClResult<u32>;
399 async fn cleanup_expired_verification_codes(&self) -> ClResult<u32>;
400
401 async fn create_proxy_site(&self, data: &CreateProxySiteData<'_>) -> ClResult<ProxySiteData>;
403 async fn read_proxy_site(&self, site_id: i64) -> ClResult<ProxySiteData>;
404 async fn read_proxy_site_by_domain(&self, domain: &str) -> ClResult<ProxySiteData>;
405 async fn update_proxy_site(
406 &self,
407 site_id: i64,
408 data: &UpdateProxySiteData<'_>,
409 ) -> ClResult<ProxySiteData>;
410 async fn delete_proxy_site(&self, site_id: i64) -> ClResult<()>;
411 async fn list_proxy_sites(&self) -> ClResult<Vec<ProxySiteData>>;
412 async fn update_proxy_site_cert(
413 &self,
414 site_id: i64,
415 cert: &str,
416 key: &str,
417 expires_at: Timestamp,
418 ) -> ClResult<()>;
419 async fn list_proxy_sites_needing_cert_renewal(
420 &self,
421 renewal_days: u32,
422 ) -> ClResult<Vec<ProxySiteData>>;
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428
429 #[test]
430 pub fn test_access_token() {
431 let token: AccessToken<String> = AccessToken {
432 iss: "a@a".into(),
433 sub: Some("b@b".into()),
434 scope: None,
435 r: None,
436 exp: Timestamp::now(),
437 };
438
439 assert_eq!(token.iss, "a@a");
440 assert_eq!(token.sub.as_ref().unwrap(), "b@b");
441 }
442}
443
444