~starkingdoms/starkingdoms

ref: 601f0329e6034735259c5bf1cefa0d28dfcb5df7 starkingdoms/api/src/routes/beamin.rs -rw-r--r-- 3.7 KiB
601f0329 — core deployment! fix! 9.0! 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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use crate::config::CONFIG;
use crate::error::{APIError, APIErrorsResponse};
use crate::AppState;
use actix_web::web::{Data, Json};
use actix_web::{post, HttpResponse};
use hmac::digest::KeyInit;
use hmac::Hmac;
use jwt::VerifyWithKey;
use log::error;
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder};
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use starkingdoms_protocol::api::APISavedPlayerData;
use std::collections::BTreeMap;

#[derive(Serialize, Deserialize)]
pub struct BeaminRequest {
    pub api_token: String,
    pub user_auth_realm_id: String,
    pub user_auth_token: String,
}

#[derive(Serialize, Deserialize)]
pub struct BeaminResponse {
    pub save_id: String,
    pub save: APISavedPlayerData,
}

#[post("/beamin")]
pub async fn beam_in(data: Json<BeaminRequest>, state: Data<AppState>) -> HttpResponse {
    if !CONFIG.internal_tokens.contains(&data.api_token) {
        return HttpResponse::Unauthorized().json(APIErrorsResponse {
            errors: vec![APIError {
                code: "ERR_BAD_TOKEN".to_string(),
                message: "Missing or invalid api token".to_string(),
                path: None,
            }],
        });
    }

    let key: Hmac<Sha256> = Hmac::new_from_slice(CONFIG.jwt_signing_secret.as_bytes()).unwrap();
    let token: BTreeMap<String, String> = match data.user_auth_token.verify_with_key(&key) {
        Ok(t) => t,
        Err(e) => {
            error!("verifying error: {}", e);
            return HttpResponse::Unauthorized().json(APIErrorsResponse {
                errors: vec![APIError {
                    code: "ERR_BAD_TOKEN".to_string(),
                    message: "Missing or invalid user token".to_string(),
                    path: None,
                }],
            });
        }
    };

    if !token.contains_key("user") || !token.contains_key("nonce") {
        return HttpResponse::Unauthorized().json(APIErrorsResponse {
            errors: vec![APIError {
                code: "ERR_BAD_TOKEN".to_string(),
                message: "Missing or invalid user token (missing scopes)".to_string(),
                path: None,
            }],
        });
    }

    let user_id = token.get("user").unwrap();

    let user_savefile: Vec<starkingdoms_api_entities::entity::user_savefile::Model> =
        match starkingdoms_api_entities::entity::user_savefile::Entity::find()
            .filter(starkingdoms_api_entities::entity::user_savefile::Column::User.eq(user_id))
            .order_by_desc(starkingdoms_api_entities::entity::user_savefile::Column::Timestamp)
            .all(&state.conn)
            .await
        {
            Ok(sf) => sf,
            Err(e) => {
                error!("database error: {}", e);
                return HttpResponse::InternalServerError().json(APIErrorsResponse {
                    errors: vec![APIError {
                        code: "ERR_DB_ERROR".to_string(),
                        message: "Unable to fetch user savefiles".to_string(),
                        path: None,
                    }],
                });
            }
        };
    if user_savefile.is_empty() {
        return HttpResponse::NoContent().json(APIErrorsResponse {
            errors: vec![APIError {
                code: "ERR_NO_SAVES".to_string(),
                message: "This user has no savefiles".to_string(),
                path: None,
            }],
        });
    }

    let save = &user_savefile[0];
    let save_id = &save.id;
    let save_data_str = &save.data;
    let save_data: APISavedPlayerData =
        toml::from_str(save_data_str).expect("database contained corrupted player save data");

    HttpResponse::Ok().json(BeaminResponse {
        save_id: save_id.clone(),
        save: save_data,
    })
}