1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
use actix_web::web;
use anyhow::{ensure, Context, Result};
use sqlx::{Pool, Sqlite};

pub use self::middleware::AuthMiddlewareFactory;
pub use self::token::Claims;
use self::{db::Users, token::Tokens};
use crate::conf;

mod db;
mod endpoints;
mod middleware;
mod passwd;
mod token;

const JWT_SECRET_VAR_NAME: &str = "DE_JWT_SECRET";
const MIN_SECRET_LEN: usize = 12;
const MAX_SECRET_LEN: usize = 86;

/// This struct can be used to setup authentication on an actix-web App.
#[derive(Clone)]
pub struct Auth {
    tokens: Tokens,
    users: Users,
}

impl Auth {
    /// Creates and sets up new authentication object. This method should be
    /// called only once during the application startup.
    ///
    /// The resulting object can be repeatedly used to configure an actix-web
    /// App.
    pub async fn setup(pool: &'static Pool<Sqlite>) -> Result<Self> {
        let jwt_secret: String = conf::mandatory(JWT_SECRET_VAR_NAME)?;

        ensure!(
            jwt_secret.len() >= MIN_SECRET_LEN,
            "JWT secret is too short: {} < {}",
            jwt_secret.len(),
            MIN_SECRET_LEN
        );
        ensure!(
            jwt_secret.len() <= MAX_SECRET_LEN,
            "JWT secret is too long: {} > {}",
            jwt_secret.len(),
            MAX_SECRET_LEN
        );

        Ok(Self {
            tokens: Tokens::new(jwt_secret.as_str()).context("Failed to initialize tokens")?,
            users: Users::init(pool)
                .await
                .context("Failed to initialize users")?,
        })
    }

    /// Configure root scope of the actix-web application.
    pub fn configure_root(&self, cfg: &mut web::ServiceConfig) {
        cfg.app_data(web::Data::new(self.tokens.clone()));
        cfg.app_data(web::Data::new(self.users.clone()));
    }

    /// Configure public scope of the actix-web application.
    pub fn configure_public(&self, cfg: &mut web::ServiceConfig) {
        endpoints::configure(cfg);
    }
}