rust/hedgewars-server/src/server/database.rs
author alfadur
Sat, 19 Jun 2021 17:48:10 +0300
changeset 15795 40929af15167
parent 15532 f1205f33bf5b
child 15833 3511bacbd763
permissions -rw-r--r--
find excuses to use shiny new 🦀 features
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
     1
use mysql;
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
     2
use mysql::{error::DriverError, error::Error, from_row_opt, params};
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
     3
use openssl::sha::sha1;
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
     4
15075
e935b1ad23f3 normalize type names
alfadur
parents: 15074
diff changeset
     5
use crate::handlers::{AccountInfo, Sha1Digest};
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
     6
15103
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
     7
const CHECK_ACCOUNT_EXISTS_QUERY: &str =
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
     8
    r"SELECT 1 FROM users WHERE users.name = :username LIMIT 1";
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
     9
15517
abd5eb807166 add ip ban check
alfadur <mail@none>
parents: 15163
diff changeset
    10
const GET_ACCOUNT_QUERY: &str = r"SELECT CASE WHEN users.status = 1 THEN users.pass ELSE '' END,
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    11
     (SELECT COUNT(users_roles.rid) FROM users_roles WHERE users.uid = users_roles.uid AND users_roles.rid = 3),
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    12
     (SELECT COUNT(users_roles.rid) FROM users_roles WHERE users.uid = users_roles.uid AND users_roles.rid = 13)
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    13
     FROM users WHERE users.name = :username";
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    14
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    15
const STORE_STATS_QUERY: &str = r"INSERT INTO gameserver_stats
15103
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    16
      (players, rooms, last_update)
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    17
      VALUES
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    18
      (:players, :rooms, UNIX_TIMESTAMP())";
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    19
14785
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
    20
const GET_REPLAY_NAME_QUERY: &str = r"SELECT filename FROM achievements WHERE id = :id";
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
    21
15121
1a43b570cbe4 Fix build errors in certain configurations
unc0rr
parents: 15110
diff changeset
    22
pub struct ServerStatistics {
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    23
    rooms: u32,
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    24
    players: u32,
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    25
}
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    26
15121
1a43b570cbe4 Fix build errors in certain configurations
unc0rr
parents: 15110
diff changeset
    27
pub struct Achievements {}
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    28
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    29
pub struct Database {
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    30
    pool: Option<mysql::Pool>,
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    31
}
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    32
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    33
impl Database {
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    34
    pub fn new() -> Self {
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    35
        Self { pool: None }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    36
    }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    37
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    38
    pub fn connect(&mut self, url: &str) -> Result<(), Error> {
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    39
        self.pool = Some(mysql::Pool::new(url)?);
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    40
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    41
        Ok(())
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    42
    }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    43
15103
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    44
    pub fn is_registered(&mut self, nick: &str) -> Result<bool, Error> {
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    45
        if let Some(pool) = &self.pool {
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    46
            let is_registered = pool
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    47
                .first_exec(CHECK_ACCOUNT_EXISTS_QUERY, params! { "username" => nick })?
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    48
                .is_some();
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    49
            Ok(is_registered)
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    50
        } else {
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    51
            Err(DriverError::SetupError.into())
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    52
        }
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    53
    }
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    54
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    55
    pub fn get_account(
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    56
        &mut self,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    57
        nick: &str,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    58
        protocol: u16,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    59
        password_hash: &str,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    60
        client_salt: &str,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    61
        server_salt: &str,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    62
    ) -> Result<Option<AccountInfo>, Error> {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    63
        if let Some(pool) = &self.pool {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    64
            if let Some(row) = pool.first_exec(GET_ACCOUNT_QUERY, params! { "username" => nick })? {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    65
                let (mut password, is_admin, is_contributor) =
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    66
                    from_row_opt::<(String, i32, i32)>(row)?;
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    67
                let client_hash = get_hash(protocol, &password, &client_salt, &server_salt);
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    68
                let server_hash = get_hash(protocol, &password, &server_salt, &client_salt);
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    69
                password.replace_range(.., "🦔🦔🦔🦔🦔🦔🦔🦔");
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    70
15163
bcb98009ad39 avoid allocation in hash comparison
alfadur
parents: 15121
diff changeset
    71
                if client_hash == password_hash {
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    72
                    Ok(Some(AccountInfo {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    73
                        is_registered: true,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    74
                        is_admin: is_admin == 1,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    75
                        is_contributor: is_contributor == 1,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    76
                        server_hash,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    77
                    }))
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    78
                } else {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    79
                    Ok(None)
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    80
                }
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    81
            } else {
15110
6a8c294f49c9 fix hash comparison fix
alfadur
parents: 15109
diff changeset
    82
                Ok(None)
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    83
            }
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    84
        } else {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    85
            Err(DriverError::SetupError.into())
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    86
        }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    87
    }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    88
15532
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
    89
    pub fn get_checker_account(
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
    90
        &mut self,
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
    91
        nick: &str,
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
    92
        checker_password: &str,
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
    93
    ) -> Result<bool, Error> {
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
    94
        if let Some(pool) = &self.pool {
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
    95
            if let Some(row) = pool.first_exec(GET_ACCOUNT_QUERY, params! { "username" => nick })? {
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
    96
                let (mut password, _, _) = from_row_opt::<(String, i32, i32)>(row)?;
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
    97
                Ok(checker_password == password)
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
    98
            } else {
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
    99
                Ok(false)
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
   100
            }
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
   101
        } else {
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
   102
            Err(DriverError::SetupError.into())
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
   103
        }
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
   104
    }
f1205f33bf5b complete checker login handling
alfadur <mail@none>
parents: 15531
diff changeset
   105
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   106
    pub fn store_stats(&mut self, stats: &ServerStatistics) -> Result<(), Error> {
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   107
        if let Some(pool) = &self.pool {
15795
40929af15167 find excuses to use shiny new 🦀 features
alfadur
parents: 15532
diff changeset
   108
            for mut stmt in pool.prepare(STORE_STATS_QUERY) {
14457
98ef2913ec73 Apply rustfmt to all files
unc0rr
parents: 14456
diff changeset
   109
                stmt.execute(params! {
98ef2913ec73 Apply rustfmt to all files
unc0rr
parents: 14456
diff changeset
   110
                    "players" => stats.players,
98ef2913ec73 Apply rustfmt to all files
unc0rr
parents: 14456
diff changeset
   111
                    "rooms" => stats.rooms,
98ef2913ec73 Apply rustfmt to all files
unc0rr
parents: 14456
diff changeset
   112
                })?;
98ef2913ec73 Apply rustfmt to all files
unc0rr
parents: 14456
diff changeset
   113
            }
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   114
            Ok(())
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   115
        } else {
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   116
            Err(DriverError::SetupError.into())
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   117
        }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   118
    }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   119
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   120
    pub fn store_achievements(&mut self, achievements: &Achievements) -> Result<(), ()> {
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   121
        Ok(())
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   122
    }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   123
14785
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   124
    pub fn get_replay_name(&mut self, replay_id: u32) -> Result<Option<String>, Error> {
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   125
        if let Some(pool) = &self.pool {
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   126
            if let Some(row) =
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   127
                pool.first_exec(GET_REPLAY_NAME_QUERY, params! { "id" => replay_id })?
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   128
            {
15531
ede5f4ec48f3 fix official server
alfadur <mail@none>
parents: 15517
diff changeset
   129
                let filename = from_row_opt::<String>(row)?;
14785
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   130
                Ok(Some(filename))
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   131
            } else {
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   132
                Ok(None)
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   133
            }
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   134
        } else {
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   135
            Err(DriverError::SetupError.into())
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   136
        }
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   137
    }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   138
}
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   139
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   140
fn get_hash(protocol_number: u16, web_password: &str, salt1: &str, salt2: &str) -> Sha1Digest {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   141
    let s = format!(
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   142
        "{}{}{}{}{}",
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   143
        salt1, salt2, web_password, protocol_number, "!hedgewars"
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   144
    );
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   145
    Sha1Digest::new(sha1(s.as_bytes()))
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   146
}