~starkingdoms/starkingdoms

ref: ccb7ac1036a620f213bb5d24a3332091074e3667 starkingdoms/starkingdoms-backplane/src/routes/login.rs -rw-r--r-- 2.8 KiB
ccb7ac10 — ghostlyzsh Merge branch 'bevy_rewrite' of https://gitlab.com/starkingdoms.tk/starkingdoms.tk into bevy_rewrite 2 years ago
                                                                                
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
// StarKingdoms.IO, a browser game about drifting through space
//     Copyright (C) 2023 ghostly_zsh, TerraMaster85, core
//
//     This program is free software: you can redistribute it and/or modify
//     it under the terms of the GNU Affero General Public License as published by
//     the Free Software Foundation, either version 3 of the License, or
//     (at your option) any later version.
//
//     This program is distributed in the hope that it will be useful,
//     but WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//     GNU Affero General Public License for more details.
//
//     You should have received a copy of the GNU Affero General Public License
//     along with this program.  If not, see <https://www.gnu.org/licenses/>.

use crate::models::User;
use crate::response::JsonAPIResponse;
use crate::schema::users;
use crate::tokens::UserToken;
use crate::AppState;
use actix_web::{
    http::StatusCode,
    post,
    web::{Data, Json},
};
use argon2::Argon2;
use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper};
use diesel_async::RunQueryDsl;
use jwt::SignWithKey;
use password_hash::{Encoding, PasswordHash};
use serde::{Deserialize, Serialize};
use std::time::{Duration, SystemTime};

#[derive(Deserialize)]
pub struct LoginRequest {
    pub username: String,
    pub password: String,
}

#[derive(Serialize, Debug)]
pub struct LoginResponse {
    pub token: String,
    pub user: UserToken,
}

#[post("/login")]
pub async fn user_login_req(
    req: Json<LoginRequest>,
    state: Data<AppState>,
) -> JsonAPIResponse<LoginResponse> {
    let mut conn = handle_error!(state.pool.get().await);

    let user: User = match handle_error!(users::dsl::users
        .filter(users::username.eq(&req.username))
        .select(User::as_select())
        .first(&mut conn)
        .await
        .optional())
    {
        Some(t) => t,
        None => {
            err!(
                StatusCode::UNAUTHORIZED,
                make_err!("ERR_UNAUTHORIZED", "user does not exist", "username")
            );
        }
    };

    let hash = handle_error!(PasswordHash::parse(&user.password_hash, Encoding::B64));
    let pwd_ok = hash
        .verify_password(&[&Argon2::default()], req.password.as_bytes())
        .is_ok();
    if !pwd_ok {
        err!(
            StatusCode::UNAUTHORIZED,
            make_err!("ERR_UNAUTHORIZED", "unauthorized")
        )
    }

    let token = UserToken {
        id: user.id,
        username: user.username.clone(),
        permission_level: user.permission_level,
        expires: SystemTime::now() + Duration::from_secs(86400 * 365), // 1 year
    };
    let jwt_str = handle_error!(token.clone().sign_with_key(&state.key));

    ok!(LoginResponse {
        token: jwt_str,
        user: token
    })
}