db_core/
lib.rs

1// Copyright (C) 2022  Aravinth Manivannan <realaravinth@batsense.net>
2// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6#![warn(missing_docs)]
7//! # `mCaptcha` database operations
8//!
9//! Traits and datastructures used in mCaptcha to interact with database.
10//!
11//! To use an unsupported database with mCaptcha, traits present within this crate should be
12//! implemented.
13//!
14//!
15//! ## Organisation
16//!
17//! Database functionality is divided across various modules:
18//!
19//! - [errors](crate::auth): error data structures used in this crate
20//! - [ops](crate::ops): meta operations like connection pool creation, migrations and getting
21//! connection from pool
22use serde::{Deserialize, Serialize};
23
24pub use libmcaptcha::defense::Level;
25
26pub mod errors;
27pub mod ops;
28#[cfg(feature = "test")]
29pub mod tests;
30
31use dev::*;
32pub use ops::GetConnection;
33
34pub mod prelude {
35    //! useful imports for users working with a supported database
36
37    pub use super::errors::*;
38    pub use super::ops::*;
39    pub use super::*;
40}
41
42pub mod dev {
43    //! useful imports for supporting a new database
44    pub use super::prelude::*;
45    pub use async_trait::async_trait;
46}
47
48#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
49/// Data required to register a new user
50pub struct Register<'a> {
51    /// username of new user
52    pub username: &'a str,
53    /// secret of new user
54    pub secret: &'a str,
55    /// hashed password of new use
56    pub hash: &'a str,
57    /// Optionally, email of new use
58    pub email: Option<&'a str>,
59}
60
61#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
62/// data required to update them email of a user
63pub struct UpdateEmail<'a> {
64    /// username of the user
65    pub username: &'a str,
66    /// new email address of the user
67    pub new_email: &'a str,
68}
69
70#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
71/// types of credentials used as identifiers during login
72pub enum Login<'a> {
73    /// username as login
74    Username(&'a str),
75    /// email as login
76    Email(&'a str),
77}
78
79#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
80/// type encapsulating username and hashed password of a user
81pub struct NameHash {
82    /// username
83    pub username: String,
84    /// hashed password
85    pub hash: String,
86}
87
88#[async_trait]
89/// mCaptcha's database requirements. To implement support for $Database, kindly implement this
90/// trait.
91pub trait MCDatabase: std::marker::Send + std::marker::Sync + CloneSPDatabase {
92    /// ping DB
93    async fn ping(&self) -> bool;
94
95    /// register a new user
96    async fn register(&self, p: &Register) -> DBResult<()>;
97
98    /// delete a user
99    async fn delete_user(&self, username: &str) -> DBResult<()>;
100
101    /// check if username exists
102    async fn username_exists(&self, username: &str) -> DBResult<bool>;
103
104    /// get user email
105    async fn get_email(&self, username: &str) -> DBResult<Option<String>>;
106
107    /// check if email exists
108    async fn email_exists(&self, email: &str) -> DBResult<bool>;
109
110    /// update a user's email
111    async fn update_email(&self, p: &UpdateEmail) -> DBResult<()>;
112
113    /// get a user's password
114    async fn get_password(&self, l: &Login) -> DBResult<NameHash>;
115
116    /// update user's password
117    async fn update_password(&self, p: &NameHash) -> DBResult<()>;
118
119    /// update username
120    async fn update_username(&self, current: &str, new: &str) -> DBResult<()>;
121
122    /// get a user's secret
123    async fn get_secret(&self, username: &str) -> DBResult<Secret>;
124
125    /// get a user's secret from a captcha key
126    async fn get_secret_from_captcha(&self, key: &str) -> DBResult<Secret>;
127
128    /// update a user's secret
129    async fn update_secret(&self, username: &str, secret: &str) -> DBResult<()>;
130
131    /// create new captcha
132    async fn create_captcha(&self, username: &str, p: &CreateCaptcha) -> DBResult<()>;
133
134    /// Get captcha config
135    async fn get_captcha_config(&self, username: &str, key: &str) -> DBResult<Captcha>;
136
137    /// Get all captchas belonging to user
138    async fn get_all_user_captchas(&self, username: &str) -> DBResult<Vec<Captcha>>;
139
140    /// update captcha metadata; doesn't change captcha key
141    async fn update_captcha_metadata(
142        &self,
143        username: &str,
144        p: &CreateCaptcha,
145    ) -> DBResult<()>;
146
147    /// update captcha key; doesn't change metadata
148    async fn update_captcha_key(
149        &self,
150        username: &str,
151        old_key: &str,
152        new_key: &str,
153    ) -> DBResult<()>;
154
155    /// Add levels to captcha
156    async fn add_captcha_levels(
157        &self,
158        username: &str,
159        captcha_key: &str,
160        levels: &[Level],
161    ) -> DBResult<()>;
162
163    /// check if captcha exists
164    async fn captcha_exists(
165        &self,
166        username: Option<&str>,
167        captcha_key: &str,
168    ) -> DBResult<bool>;
169
170    /// Delete all levels of a captcha
171    async fn delete_captcha_levels(
172        &self,
173        username: &str,
174        captcha_key: &str,
175    ) -> DBResult<()>;
176
177    /// Delete captcha
178    async fn delete_captcha(&self, username: &str, captcha_key: &str) -> DBResult<()>;
179
180    /// Get captcha levels
181    async fn get_captcha_levels(
182        &self,
183        username: Option<&str>,
184        captcha_key: &str,
185    ) -> DBResult<Vec<Level>>;
186
187    /// Get captcha's cooldown period
188    async fn get_captcha_cooldown(&self, captcha_key: &str) -> DBResult<i32>;
189
190    /// Add traffic configuration
191    async fn add_traffic_pattern(
192        &self,
193        username: &str,
194        captcha_key: &str,
195        pattern: &TrafficPattern,
196    ) -> DBResult<()>;
197
198    /// Get traffic configuration
199    async fn get_traffic_pattern(
200        &self,
201        username: &str,
202        captcha_key: &str,
203    ) -> DBResult<TrafficPattern>;
204
205    /// Get all easy captcha configurations on instance
206    async fn get_all_easy_captchas(
207        &self,
208        limit: usize,
209        offset: usize,
210    ) -> DBResult<Vec<EasyCaptcha>>;
211
212    /// Delete traffic configuration
213    async fn delete_traffic_pattern(
214        &self,
215        username: &str,
216        captcha_key: &str,
217    ) -> DBResult<()>;
218
219    /// create new notification
220    async fn create_notification(&self, p: &AddNotification) -> DBResult<()>;
221
222    /// get all unread notifications
223    async fn get_all_unread_notifications(
224        &self,
225        username: &str,
226    ) -> DBResult<Vec<Notification>>;
227
228    /// mark a notification read
229    async fn mark_notification_read(&self, username: &str, id: i32) -> DBResult<()>;
230
231    /// record PoWConfig fetches
232    async fn record_fetch(&self, key: &str) -> DBResult<()>;
233
234    /// record PoWConfig solves
235    async fn record_solve(&self, key: &str) -> DBResult<()>;
236
237    /// record PoWConfig confirms
238    async fn record_confirm(&self, key: &str) -> DBResult<()>;
239
240    /// fetch PoWConfig fetches
241    async fn fetch_config_fetched(&self, user: &str, key: &str) -> DBResult<Vec<i64>>;
242
243    /// fetch PoWConfig solves
244    async fn fetch_solve(&self, user: &str, key: &str) -> DBResult<Vec<i64>>;
245
246    /// fetch PoWConfig confirms
247    async fn fetch_confirm(&self, user: &str, key: &str) -> DBResult<Vec<i64>>;
248
249    /// record PoW timing
250    async fn analysis_save(
251        &self,
252        captcha_id: &str,
253        d: &CreatePerformanceAnalytics,
254    ) -> DBResult<()>;
255
256    /// fetch PoW analytics
257    async fn analytics_fetch(
258        &self,
259        captcha_id: &str,
260        limit: usize,
261        offset: usize,
262    ) -> DBResult<Vec<PerformanceAnalytics>>;
263
264    /// Create psuedo ID against campaign ID to publish analytics
265    async fn analytics_create_psuedo_id_if_not_exists(
266        &self,
267        captcha_id: &str,
268    ) -> DBResult<()>;
269
270    /// Get psuedo ID from campaign ID
271    async fn analytics_get_psuedo_id_from_capmaign_id(
272        &self,
273        captcha_id: &str,
274    ) -> DBResult<String>;
275
276    /// Get campaign ID from psuedo ID
277    async fn analytics_get_capmaign_id_from_psuedo_id(
278        &self,
279        psuedo_id: &str,
280    ) -> DBResult<String>;
281
282    /// Delete all records for campaign
283    async fn analytics_delete_all_records_for_campaign(
284        &self,
285        campaign_id: &str,
286    ) -> DBResult<()>;
287
288    /// Get publishing status of pow analytics for captcha ID/ campaign ID
289    async fn analytics_captcha_is_published(&self, campaign_id: &str) -> DBResult<bool> {
290        match self
291            .analytics_get_psuedo_id_from_capmaign_id(campaign_id)
292            .await
293        {
294            Ok(_) => Ok(true),
295            Err(errors::DBError::CaptchaNotFound) => Ok(false),
296            Err(e) => Err(e),
297        }
298    }
299
300    /// Get all psuedo IDs
301    async fn analytics_get_all_psuedo_ids(&self, page: usize) -> DBResult<Vec<String>>;
302
303    /// Track maximum nonce received against captcha levels
304    async fn update_max_nonce_for_level(
305        &self,
306        captcha_key: &str,
307        difficulty_factor: u32,
308        latest_nonce: u32,
309    ) -> DBResult<()>;
310
311    /// Get maximum nonce tracked so far for captcha levels
312    async fn get_max_nonce_for_level(
313        &self,
314        captcha_key: &str,
315        difficulty_factor: u32,
316    ) -> DBResult<u32>;
317
318    /// Get number of analytics entries that are under a certain duration
319    async fn stats_get_num_logs_under_time(&self, duration: u32) -> DBResult<usize>;
320
321    /// Get the entry at a location in the list of analytics entires under a certain time limit
322    /// and sorted in ascending order
323    async fn stats_get_entry_at_location_for_time_limit_asc(
324        &self,
325        duration: u32,
326        location: u32,
327    ) -> DBResult<Option<usize>>;
328}
329
330#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
331/// Log Proof-of-Work CAPTCHA performance analytics
332pub struct CreatePerformanceAnalytics {
333    /// time taken to generate proof
334    pub time: u32,
335    /// difficulty factor for which the proof was generated
336    pub difficulty_factor: u32,
337    /// worker/client type: wasm, javascript, python, etc.
338    pub worker_type: String,
339}
340
341#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
342/// Proof-of-Work CAPTCHA performance analytics
343pub struct PerformanceAnalytics {
344    /// log ID
345    pub id: usize,
346    /// time taken to generate proof
347    pub time: u32,
348    /// difficulty factor for which the proof was generated
349    pub difficulty_factor: u32,
350    /// worker/client type: wasm, javascript, python, etc.
351    pub worker_type: String,
352}
353
354#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
355/// Captcha statistics with time recorded in UNIX epoch formats
356pub struct StatsUnixTimestamp {
357    /// times at which the configuration were fetched
358    pub config_fetches: Vec<i64>,
359    /// times at which the PoW was solved
360    pub solves: Vec<i64>,
361    /// times at which the PoW token was verified
362    pub confirms: Vec<i64>,
363}
364
365#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
366/// Represents notification
367pub struct Notification {
368    /// receiver name  of the notification
369    pub name: Option<String>,
370    /// heading of the notification
371    pub heading: Option<String>,
372    /// message of the notification
373    pub message: Option<String>,
374    /// when notification was received
375    pub received: Option<i64>,
376    /// db assigned ID of the notification
377    pub id: Option<i32>,
378}
379
380#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
381/// Data required to add notification
382pub struct AddNotification<'a> {
383    /// who is the notification addressed to?
384    pub to: &'a str,
385    /// notification sender
386    pub from: &'a str,
387    /// heading of the notification
388    pub heading: &'a str,
389    /// message of the notification
390    pub message: &'a str,
391}
392
393#[derive(Default, PartialEq, Serialize, Deserialize, Clone, Debug)]
394/// Represents Easy captcha configuration
395pub struct EasyCaptcha {
396    /// traffic pattern of easy captcha
397    pub traffic_pattern: TrafficPattern,
398    /// captcha key/sitekey
399    pub key: String,
400    /// captcha description
401    pub description: String,
402    /// Owner of the captcha configuration
403    pub username: String,
404}
405
406#[derive(Default, PartialEq, Serialize, Deserialize, Clone, Debug)]
407/// User's traffic pattern; used in generating a captcha configuration
408pub struct TrafficPattern {
409    /// average traffic of user's website
410    pub avg_traffic: u32,
411    /// the peak traffic that the user's website can handle
412    pub peak_sustainable_traffic: u32,
413    /// traffic that bought the user's website down; optional
414    pub broke_my_site_traffic: Option<u32>,
415}
416
417#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
418/// data required to create new captcha
419pub struct CreateCaptcha<'a> {
420    /// cool down duration
421    pub duration: i32,
422    /// description of the captcha
423    pub description: &'a str,
424    /// secret key of the captcha
425    pub key: &'a str,
426}
427
428#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
429/// Data representing a captcha
430pub struct Captcha {
431    /// Database assigned ID
432    pub config_id: i32,
433    /// cool down duration
434    pub duration: i32,
435    /// description of the captcha
436    pub description: String,
437    /// secret key of the captcha
438    pub key: String,
439}
440
441#[derive(Clone, Debug, Deserialize, PartialEq, Default, Serialize)]
442/// datastructure representing a user's secret
443pub struct Secret {
444    /// user's secret
445    pub secret: String,
446}
447/// Trait to clone MCDatabase
448pub trait CloneSPDatabase {
449    /// clone DB
450    fn clone_db(&self) -> Box<dyn MCDatabase>;
451}
452
453impl<T> CloneSPDatabase for T
454where
455    T: MCDatabase + Clone + 'static,
456{
457    fn clone_db(&self) -> Box<dyn MCDatabase> {
458        Box::new(self.clone())
459    }
460}
461
462impl Clone for Box<dyn MCDatabase> {
463    fn clone(&self) -> Self {
464        (**self).clone_db()
465    }
466}