hyperion/models/
users.rs

1use std::convert::TryFrom;
2
3use serde_derive::{Deserialize, Serialize};
4use sha2::Digest;
5use thiserror::Error;
6
7use super::default_none;
8use crate::db::models as db_models;
9
10#[derive(Debug, Error)]
11pub enum UserError {
12    #[error("error parsing date: {0}")]
13    Chrono(#[from] chrono::ParseError),
14    #[error("error parsing uuid: {0}")]
15    Uuid(#[from] uuid::Error),
16    #[error("error decoding hex data: {0}")]
17    Hex(#[from] hex::FromHexError),
18    #[error("error decoding UTF-8 data: {0}")]
19    Utf8(#[from] std::string::FromUtf8Error),
20}
21
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23#[serde(rename_all = "camelCase", deny_unknown_fields)]
24pub struct User {
25    pub name: String,
26    #[serde(
27        serialize_with = "hex::serialize",
28        deserialize_with = "hex::deserialize"
29    )]
30    pub password: Vec<u8>,
31    #[serde(
32        serialize_with = "hex::serialize",
33        deserialize_with = "hex::deserialize"
34    )]
35    pub token: Vec<u8>,
36    pub salt: String,
37    #[serde(default = "default_none")]
38    pub comment: Option<String>,
39    #[serde(default = "default_none")]
40    pub id: Option<String>,
41    #[serde(default = "chrono::Utc::now")]
42    pub created_at: chrono::DateTime<chrono::Utc>,
43    #[serde(default = "chrono::Utc::now")]
44    pub last_use: chrono::DateTime<chrono::Utc>,
45}
46
47impl User {
48    pub fn hyperion() -> Self {
49        let name = "Hyperion".to_owned();
50        let salt = Self::generate_salt();
51        let token = Self::generate_token();
52        let password = Self::hash_password("hyperion", salt.as_bytes());
53        let created_at = chrono::Utc::now();
54        let last_use = created_at;
55
56        Self {
57            name,
58            password,
59            token,
60            salt,
61            comment: None,
62            id: None,
63            created_at,
64            last_use,
65        }
66    }
67
68    pub fn generate_token() -> Vec<u8> {
69        let mut hasher = sha2::Sha512::default();
70        hasher.update(uuid::Uuid::new_v4().as_bytes());
71        hasher.finalize().to_vec()
72    }
73
74    pub fn generate_salt() -> String {
75        hex::encode(Self::generate_token())
76    }
77
78    pub fn hash_password(password: &str, salt: &[u8]) -> Vec<u8> {
79        let mut hasher = sha2::Sha512::default();
80        hasher.update(password.as_bytes());
81        hasher.update(salt);
82        hasher.finalize().to_vec()
83    }
84}
85
86impl TryFrom<db_models::DbUser> for User {
87    type Error = UserError;
88
89    fn try_from(db: db_models::DbUser) -> Result<Self, Self::Error> {
90        Ok(Self {
91            name: db.user,
92            password: hex::decode(db.password)?,
93            token: hex::decode(db.token)?,
94            salt: String::from_utf8(db.salt)?,
95            comment: db.comment,
96            id: db.id,
97            created_at: chrono::DateTime::parse_from_rfc3339(&db.created_at)?
98                .with_timezone(&chrono::Utc),
99            last_use: chrono::DateTime::parse_from_rfc3339(&db.last_use)?
100                .with_timezone(&chrono::Utc),
101        })
102    }
103}