Skip to main content

cloudillo_auth_adapter_sqlite/
lib.rs

1// SPDX-FileCopyrightText: Szilárd Hajba
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4use std::{fmt::Debug, path::Path, sync::Arc};
5
6use async_trait::async_trait;
7use jsonwebtoken::DecodingKey;
8use sqlx::sqlite::{self, SqlitePool};
9use tokio::fs;
10
11use cloudillo_types::{
12	auth_adapter::{
13		AccessToken, ApiKeyInfo, ApiKeyValidation, AuthAdapter, AuthCtx, AuthKey, AuthLogin,
14		AuthProfile, CertData, CreateApiKeyOptions, CreateProxySiteData, CreateTenantData,
15		CreatedApiKey, KeyPair, ListTenantsOptions, ProxySiteData, TenantListItem,
16		UpdateProxySiteData, Webauthn,
17	},
18	prelude::*,
19	worker::WorkerPool,
20};
21
22mod api_key;
23mod auth;
24mod cert;
25mod crypto;
26mod profile_key;
27mod proxy_site;
28mod schema;
29mod tenant;
30mod utils;
31mod vapid;
32mod variable;
33mod webauthn;
34
35pub struct AuthAdapterSqlite {
36	db: SqlitePool,
37	worker: Arc<WorkerPool>,
38	jwt_secret_str: String,
39	jwt_secret: DecodingKey,
40}
41
42impl Debug for AuthAdapterSqlite {
43	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44		f.debug_struct("AuthAdapterSqlite").finish()
45	}
46}
47
48impl AuthAdapterSqlite {
49	pub async fn new(worker: Arc<WorkerPool>, path: impl AsRef<Path>) -> ClResult<Self> {
50		let db_path = path.as_ref().join("auth.db");
51		fs::create_dir_all(&path)
52			.await
53			.map_err(|e| Error::Internal(format!("Cannot create auth-adapter dir: {e}")))?;
54		let opts = sqlite::SqliteConnectOptions::new()
55			.filename(&db_path)
56			.create_if_missing(true)
57			.journal_mode(sqlite::SqliteJournalMode::Wal);
58		let db = sqlite::SqlitePoolOptions::new()
59			.max_connections(5)
60			.connect_with(opts)
61			.await
62			.inspect_err(|err| println!("DbError: {:#?}", err))
63			.or(Err(Error::DbError))?;
64
65		schema::init_db(&db)
66			.await
67			.inspect_err(|err| println!("DbError: {:#?}", err))
68			.or(Err(Error::DbError))?;
69
70		// Get or generate JWT secret
71		let jwt_secret_str = auth::ensure_jwt_secret(&db).await?;
72		let jwt_secret = DecodingKey::from_secret(jwt_secret_str.as_bytes());
73
74		Ok(Self { db, worker, jwt_secret_str, jwt_secret })
75	}
76}
77
78#[async_trait]
79impl AuthAdapter for AuthAdapterSqlite {
80	async fn validate_access_token(&self, tn_id: TnId, token: &str) -> ClResult<AuthCtx> {
81		auth::validate_access_token(&self.jwt_secret, tn_id, token).await
82	}
83
84	async fn read_id_tag(&self, tn_id: TnId) -> ClResult<Box<str>> {
85		tenant::read_id_tag(&self.db, tn_id).await
86	}
87
88	async fn read_tn_id(&self, id_tag: &str) -> ClResult<TnId> {
89		tenant::read_tn_id(&self.db, id_tag).await
90	}
91
92	async fn read_tenant(&self, id_tag: &str) -> ClResult<AuthProfile> {
93		tenant::read_tenant(&self.db, id_tag).await
94	}
95
96	async fn create_tenant_registration(&self, email: &str) -> ClResult<()> {
97		tenant::create_tenant_registration(&self.db, email).await
98	}
99
100	async fn create_tenant(&self, id_tag: &str, data: CreateTenantData<'_>) -> ClResult<TnId> {
101		tenant::create_tenant(&self.db, &self.worker, id_tag, data).await
102	}
103
104	async fn delete_tenant(&self, id_tag: &str) -> ClResult<()> {
105		tenant::delete_tenant(&self.db, id_tag).await
106	}
107
108	async fn list_tenants(&self, opts: &ListTenantsOptions<'_>) -> ClResult<Vec<TenantListItem>> {
109		tenant::list_tenants(&self.db, opts).await
110	}
111
112	async fn create_tenant_login(&self, id_tag: &str) -> ClResult<AuthLogin> {
113		auth::create_tenant_login(&self.db, &self.worker, id_tag, &self.jwt_secret_str).await
114	}
115
116	async fn check_tenant_password(&self, id_tag: &str, password: &str) -> ClResult<AuthLogin> {
117		auth::check_tenant_password(&self.db, &self.worker, id_tag, password, &self.jwt_secret_str)
118			.await
119	}
120
121	async fn update_tenant_password(&self, id_tag: &str, password: &str) -> ClResult<()> {
122		auth::update_tenant_password(&self.db, &self.worker, id_tag, password).await
123	}
124
125	async fn update_idp_api_key(&self, id_tag: &str, api_key: &str) -> ClResult<()> {
126		auth::update_idp_api_key(&self.db, id_tag, api_key).await
127	}
128
129	async fn create_cert(&self, cert_data: &CertData) -> ClResult<()> {
130		cert::create_cert(&self.db, cert_data).await
131	}
132
133	async fn read_cert_by_tn_id(&self, tn_id: TnId) -> ClResult<CertData> {
134		cert::read_cert_by_tn_id(&self.db, tn_id).await
135	}
136
137	async fn read_cert_by_id_tag(&self, id_tag: &str) -> ClResult<CertData> {
138		cert::read_cert_by_id_tag(&self.db, id_tag).await
139	}
140
141	async fn read_cert_by_domain(&self, domain: &str) -> ClResult<CertData> {
142		cert::read_cert_by_domain(&self.db, domain).await
143	}
144
145	async fn list_all_certs(&self) -> ClResult<Vec<CertData>> {
146		cert::list_all_certs(&self.db).await
147	}
148
149	async fn list_tenants_needing_cert_renewal(
150		&self,
151		renewal_days: u32,
152	) -> ClResult<Vec<(TnId, Box<str>)>> {
153		cert::list_tenants_needing_cert_renewal(&self.db, renewal_days).await
154	}
155
156	async fn list_profile_keys(&self, tn_id: TnId) -> ClResult<Vec<AuthKey>> {
157		profile_key::list_profile_keys(&self.db, tn_id).await
158	}
159
160	async fn read_profile_key(&self, tn_id: TnId, key_id: &str) -> ClResult<AuthKey> {
161		profile_key::read_profile_key(&self.db, tn_id, key_id).await
162	}
163
164	async fn create_profile_key(
165		&self,
166		tn_id: TnId,
167		expires_at: Option<Timestamp>,
168	) -> ClResult<AuthKey> {
169		profile_key::create_profile_key(&self.db, &self.worker, tn_id, expires_at).await
170	}
171
172	async fn create_access_token(
173		&self,
174		tn_id: TnId,
175		data: &AccessToken<&str>,
176	) -> ClResult<Box<str>> {
177		auth::create_access_token(&self.db, &self.worker, tn_id, data, &self.jwt_secret_str).await
178	}
179
180	async fn create_action_token(
181		&self,
182		tn_id: TnId,
183		action: cloudillo_types::action_types::CreateAction,
184	) -> ClResult<Box<str>> {
185		auth::create_action_token(&self.db, &self.worker, tn_id, action).await
186	}
187
188	async fn verify_access_token(&self, token: &str) -> ClResult<()> {
189		auth::verify_access_token(&self.jwt_secret, token).await
190	}
191
192	async fn read_vapid_key(&self, tn_id: TnId) -> ClResult<KeyPair> {
193		vapid::read_vapid_key(&self.db, tn_id).await
194	}
195
196	async fn read_vapid_public_key(&self, tn_id: TnId) -> ClResult<Box<str>> {
197		vapid::read_vapid_public_key(&self.db, tn_id).await
198	}
199
200	async fn create_vapid_key(&self, tn_id: TnId) -> ClResult<KeyPair> {
201		let keypair = crypto::generate_vapid_key(&self.worker).await?;
202		vapid::update_vapid_key(&self.db, tn_id, &keypair).await?;
203		Ok(keypair)
204	}
205
206	async fn update_vapid_key(&self, tn_id: TnId, key: &KeyPair) -> ClResult<()> {
207		vapid::update_vapid_key(&self.db, tn_id, key).await
208	}
209
210	async fn read_var(&self, tn_id: TnId, var: &str) -> ClResult<Box<str>> {
211		variable::read_var(&self.db, tn_id, var).await
212	}
213
214	async fn update_var(&self, tn_id: TnId, var: &str, value: &str) -> ClResult<()> {
215		variable::update_var(&self.db, tn_id, var, value).await
216	}
217
218	async fn list_webauthn_credentials(&self, tn_id: TnId) -> ClResult<Box<[Webauthn]>> {
219		webauthn::list_webauthn_credentials(&self.db, tn_id).await
220	}
221
222	async fn read_webauthn_credential(
223		&self,
224		tn_id: TnId,
225		credential_id: &str,
226	) -> ClResult<Webauthn> {
227		webauthn::read_webauthn_credential(&self.db, tn_id, credential_id).await
228	}
229
230	async fn create_webauthn_credential(&self, tn_id: TnId, data: &Webauthn) -> ClResult<()> {
231		webauthn::create_webauthn_credential(&self.db, tn_id, data).await
232	}
233
234	async fn update_webauthn_credential_counter(
235		&self,
236		tn_id: TnId,
237		credential_id: &str,
238		counter: u32,
239	) -> ClResult<()> {
240		webauthn::update_webauthn_credential_counter(&self.db, tn_id, credential_id, counter).await
241	}
242
243	async fn delete_webauthn_credential(&self, tn_id: TnId, credential_id: &str) -> ClResult<()> {
244		webauthn::delete_webauthn_credential(&self.db, tn_id, credential_id).await
245	}
246
247	// API Key management
248	async fn create_api_key(
249		&self,
250		tn_id: TnId,
251		opts: CreateApiKeyOptions<'_>,
252	) -> ClResult<CreatedApiKey> {
253		api_key::create_api_key(&self.db, &self.worker, tn_id, opts).await
254	}
255
256	async fn validate_api_key(&self, key: &str) -> ClResult<ApiKeyValidation> {
257		api_key::validate_api_key(&self.db, &self.worker, key).await
258	}
259
260	async fn list_api_keys(&self, tn_id: TnId) -> ClResult<Vec<ApiKeyInfo>> {
261		api_key::list_api_keys(&self.db, tn_id).await
262	}
263
264	async fn read_api_key(&self, tn_id: TnId, key_id: i64) -> ClResult<ApiKeyInfo> {
265		api_key::read_api_key(&self.db, tn_id, key_id).await
266	}
267
268	async fn update_api_key(
269		&self,
270		tn_id: TnId,
271		key_id: i64,
272		name: Option<&str>,
273		scopes: Option<&str>,
274		expires_at: Option<Timestamp>,
275	) -> ClResult<ApiKeyInfo> {
276		api_key::update_api_key(&self.db, tn_id, key_id, name, scopes, expires_at).await
277	}
278
279	async fn delete_api_key(&self, tn_id: TnId, key_id: i64) -> ClResult<()> {
280		api_key::delete_api_key(&self.db, tn_id, key_id).await
281	}
282
283	async fn cleanup_expired_api_keys(&self) -> ClResult<u32> {
284		api_key::cleanup_expired_api_keys(&self.db).await
285	}
286
287	async fn cleanup_expired_verification_codes(&self) -> ClResult<u32> {
288		let result = sqlx::query(
289			"DELETE FROM user_vfy WHERE expires_at IS NOT NULL AND expires_at < unixepoch()",
290		)
291		.execute(&self.db)
292		.await
293		.or(Err(Error::DbError))?;
294		Ok(u32::try_from(result.rows_affected()).unwrap_or_default())
295	}
296
297	// Proxy site management
298	async fn create_proxy_site(&self, data: &CreateProxySiteData<'_>) -> ClResult<ProxySiteData> {
299		proxy_site::create_proxy_site(&self.db, data).await
300	}
301
302	async fn read_proxy_site(&self, site_id: i64) -> ClResult<ProxySiteData> {
303		proxy_site::read_proxy_site(&self.db, site_id).await
304	}
305
306	async fn read_proxy_site_by_domain(&self, domain: &str) -> ClResult<ProxySiteData> {
307		proxy_site::read_proxy_site_by_domain(&self.db, domain).await
308	}
309
310	async fn update_proxy_site(
311		&self,
312		site_id: i64,
313		data: &UpdateProxySiteData<'_>,
314	) -> ClResult<ProxySiteData> {
315		proxy_site::update_proxy_site(&self.db, site_id, data).await
316	}
317
318	async fn delete_proxy_site(&self, site_id: i64) -> ClResult<()> {
319		proxy_site::delete_proxy_site(&self.db, site_id).await
320	}
321
322	async fn list_proxy_sites(&self) -> ClResult<Vec<ProxySiteData>> {
323		proxy_site::list_proxy_sites(&self.db).await
324	}
325
326	async fn update_proxy_site_cert(
327		&self,
328		site_id: i64,
329		cert: &str,
330		key: &str,
331		expires_at: Timestamp,
332	) -> ClResult<()> {
333		proxy_site::update_proxy_site_cert(&self.db, site_id, cert, key, expires_at).await
334	}
335
336	async fn list_proxy_sites_needing_cert_renewal(
337		&self,
338		renewal_days: u32,
339	) -> ClResult<Vec<ProxySiteData>> {
340		proxy_site::list_proxy_sites_needing_cert_renewal(&self.db, renewal_days).await
341	}
342}
343
344// vim: ts=4