~starkingdoms/starkingdoms

ref: 317954ef75ec4d951fd7fa70b22b11a0127bdf49 starkingdoms/api/src/routes/beamout.rs -rw-r--r-- 3.3 KiB
317954ef — c0repwn3r input + cleanup 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
use std::collections::BTreeMap;
use std::time::{SystemTime, UNIX_EPOCH};
use actix_web::{HttpResponse, post};
use actix_web::web::{Data, Json};
use hmac::digest::KeyInit;
use hmac::Hmac;
use jwt::VerifyWithKey;
use log::error;
use sea_orm::{ActiveModelTrait, IntoActiveModel};
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use ulid::Ulid;
use starkingdoms_protocol::api::APISavedPlayerData;
use crate::AppState;
use crate::config::CONFIG;
use crate::error::{APIError, APIErrorsResponse};

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

#[derive(Serialize, Deserialize)]
pub struct BeamoutResponse {}

#[post("/beamout")]
pub async fn beam_out(data: Json<BeamoutRequest>, 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 saved_data = toml::to_string(&data.data).unwrap();
    let savefile_model = starkingdoms_api_entities::entity::user_savefile::Model {
        id: format!("save-{}", Ulid::new().to_string()),
        user: token.get("user").unwrap().clone(),
        data: saved_data,
        timestamp: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64,
    };
    let savefile_active_model = savefile_model.into_active_model();
    match savefile_active_model.insert(&state.conn).await {
        Ok(_) => (),
        Err(e) => {
            error!("database error: {}", e);
            return HttpResponse::InternalServerError().json(APIErrorsResponse {
                errors: vec![
                    APIError {
                        code: "ERR_DB_ERROR".to_string(),
                        message: "database failure".to_string(),
                        path: None,
                    }
                ],
            });
        }
    }

    HttpResponse::Ok().json(BeamoutResponse {})
}