summaryrefslogtreecommitdiffstats
path: root/src/admin/user_creation.rs
blob: d54b0632cc8aba1ab7396ec8caee44dd8c4c51e4 (plain) (blame)
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use actix::{Actor, Handler, Message, SyncContext};
use argonautica::{input::SecretKey, Hasher};
use diesel::MysqlConnection;
use diesel::{
    dsl::sql,
    types::{Bigint, Unsigned},
    ExpressionMethods, QueryDsl, RunQueryDsl,
};

use crate::error::ServiceError;
use crate::model::{DbExecutor, NewPassword, NewUser, User};
use crate::schema::passwords::{columns as password_columns, dsl::passwords};
use crate::schema::users::{columns as user_columns, dsl::users};

static LAST_ID_SQL: &str = "SELECT LAST_INSERT_ID()";

pub struct PassHashExecutor(pub Hasher<'static>);

impl Actor for PassHashExecutor {
    type Context = SyncContext<Self>;
}

pub struct PassHashRequest {
    password: String,
    secret: String,
}

impl PassHashRequest {
    pub fn new(password: String, secret: String) -> Self {
        Self { password, secret }
    }
}

impl Message for PassHashRequest {
    type Result = Result<String, ServiceError>;
}

impl Handler<PassHashRequest> for PassHashExecutor {
    type Result = Result<String, ServiceError>;

    fn handle(&mut self, req: PassHashRequest, _: &mut Self::Context) -> Self::Result {
        let secret = SecretKey::from_base64_encoded(req.secret)?;
        Ok(self
            .0
            .with_password(req.password)
            .with_secret_key(secret)
            .hash()?)
    }
}

pub struct SaveUserRequest {
    user: NewUser,
    pass_hash: String,
}

impl SaveUserRequest {
    pub fn new(user: NewUser, pass_hash: String) -> Self {
        Self { user, pass_hash }
    }
}

impl Message for SaveUserRequest {
    type Result = Result<(), ServiceError>;
}

impl Handler<SaveUserRequest> for DbExecutor {
    type Result = Result<(), ServiceError>;

    fn handle(&mut self, msg: SaveUserRequest, _: &mut Self::Context) -> Self::Result {
        let conn: &MysqlConnection = &self.0.get().unwrap();

        diesel::insert_into(users).values(&msg.user).execute(conn)?;

        let id = sql::<Unsigned<Bigint>>(LAST_ID_SQL).get_result(conn)?;

        let new_pass = NewPassword {
            id,
            hash: &msg.pass_hash,
        };

        diesel::insert_into(passwords)
            .values(&new_pass)
            .execute(conn)?;

        Ok(())
    }
}

pub struct ChangePasswordRequest(pub String, pub String);

impl Message for ChangePasswordRequest {
    type Result = Result<(), ServiceError>;
}

impl Handler<ChangePasswordRequest> for DbExecutor {
    type Result = Result<(), ServiceError>;

    fn handle(&mut self, req: ChangePasswordRequest, _: &mut Self::Context) -> Self::Result {
        let conn: &MysqlConnection = &self.0.get().unwrap();
        let (name, hash) = (req.0, req.1);

        let user = users
            .filter(user_columns::name.eq(name))
            .first::<User>(conn)
            .map_err(|_| ServiceError::NotFound)?;

        if user.power_level == 9001 {
            return Err(ServiceError::Unauthorized);
        }

        diesel::update(passwords.filter(password_columns::id.eq(user.id)))
            .set(password_columns::hash.eq(hash))
            .execute(conn)?;

        Ok(())
    }
}