#
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}