rust/hedgewars-server/src/server/database.rs
changeset 15833 3511bacbd763
parent 15795 40929af15167
child 15848 3d05bada4799
equal deleted inserted replaced
15832:a4d505a32879 15833:3511bacbd763
     1 use mysql;
     1 use mysql_async::{self, from_row_opt, params, prelude::*, Pool};
     2 use mysql::{error::DriverError, error::Error, from_row_opt, params};
     2 use sha1::{Digest, Sha1};
     3 use openssl::sha::sha1;
       
     4 
     3 
     5 use crate::handlers::{AccountInfo, Sha1Digest};
     4 use crate::handlers::{AccountInfo, Sha1Digest};
     6 
     5 
     7 const CHECK_ACCOUNT_EXISTS_QUERY: &str =
     6 const CHECK_ACCOUNT_EXISTS_QUERY: &str =
     8     r"SELECT 1 FROM users WHERE users.name = :username LIMIT 1";
     7     r"SELECT 1 FROM users WHERE users.name = :username LIMIT 1";
    25 }
    24 }
    26 
    25 
    27 pub struct Achievements {}
    26 pub struct Achievements {}
    28 
    27 
    29 pub struct Database {
    28 pub struct Database {
    30     pool: Option<mysql::Pool>,
    29     pool: Pool,
    31 }
    30 }
    32 
    31 
    33 impl Database {
    32 impl Database {
    34     pub fn new() -> Self {
    33     pub fn new(url: &str) -> Self {
    35         Self { pool: None }
    34         Self {
    36     }
    35             pool: Pool::new(url),
    37 
       
    38     pub fn connect(&mut self, url: &str) -> Result<(), Error> {
       
    39         self.pool = Some(mysql::Pool::new(url)?);
       
    40 
       
    41         Ok(())
       
    42     }
       
    43 
       
    44     pub fn is_registered(&mut self, nick: &str) -> Result<bool, Error> {
       
    45         if let Some(pool) = &self.pool {
       
    46             let is_registered = pool
       
    47                 .first_exec(CHECK_ACCOUNT_EXISTS_QUERY, params! { "username" => nick })?
       
    48                 .is_some();
       
    49             Ok(is_registered)
       
    50         } else {
       
    51             Err(DriverError::SetupError.into())
       
    52         }
    36         }
    53     }
    37     }
    54 
    38 
    55     pub fn get_account(
    39     pub async fn get_is_registered(&mut self, nick: &str) -> mysql_async::Result<bool> {
       
    40         let mut connection = self.pool.get_conn().await?;
       
    41         let result = CHECK_ACCOUNT_EXISTS_QUERY
       
    42             .with(params! { "username" => nick })
       
    43             .first(&mut connection)
       
    44             .await?;
       
    45         Ok(!result.is_empty())
       
    46     }
       
    47 
       
    48     pub async fn get_account(
    56         &mut self,
    49         &mut self,
    57         nick: &str,
    50         nick: &str,
    58         protocol: u16,
    51         protocol: u16,
    59         password_hash: &str,
    52         password_hash: &str,
    60         client_salt: &str,
    53         client_salt: &str,
    61         server_salt: &str,
    54         server_salt: &str,
    62     ) -> Result<Option<AccountInfo>, Error> {
    55     ) -> mysql_async::Result<Option<AccountInfo>> {
    63         if let Some(pool) = &self.pool {
    56         let mut connection = self.pool.get_conn().await?;
    64             if let Some(row) = pool.first_exec(GET_ACCOUNT_QUERY, params! { "username" => nick })? {
    57         if let Some((mut password, is_admin, is_contributor)) = GET_ACCOUNT_QUERY
    65                 let (mut password, is_admin, is_contributor) =
    58             .with(params! { "username" => nick })
    66                     from_row_opt::<(String, i32, i32)>(row)?;
    59             .first::<(String, i32, i32), _>(&mut connection)
    67                 let client_hash = get_hash(protocol, &password, &client_salt, &server_salt);
    60             .await?
    68                 let server_hash = get_hash(protocol, &password, &server_salt, &client_salt);
    61         {
    69                 password.replace_range(.., "🦔🦔🦔🦔🦔🦔🦔🦔");
    62             let client_hash = get_hash(protocol, &password, &client_salt, &server_salt);
       
    63             let server_hash = get_hash(protocol, &password, &server_salt, &client_salt);
       
    64             password.replace_range(.., "🦔🦔🦔🦔🦔🦔🦔🦔");
    70 
    65 
    71                 if client_hash == password_hash {
    66             if client_hash == password_hash {
    72                     Ok(Some(AccountInfo {
    67                 Ok(Some(AccountInfo {
    73                         is_registered: true,
    68                     is_registered: true,
    74                         is_admin: is_admin == 1,
    69                     is_admin: is_admin == 1,
    75                         is_contributor: is_contributor == 1,
    70                     is_contributor: is_contributor == 1,
    76                         server_hash,
    71                     server_hash,
    77                     }))
    72                 }))
    78                 } else {
       
    79                     Ok(None)
       
    80                 }
       
    81             } else {
    73             } else {
    82                 Ok(None)
    74                 Ok(None)
    83             }
    75             }
    84         } else {
    76         } else {
    85             Err(DriverError::SetupError.into())
    77             Ok(None)
    86         }
    78         }
    87     }
    79     }
    88 
    80 
    89     pub fn get_checker_account(
    81     pub async fn get_checker_account(
    90         &mut self,
    82         &mut self,
    91         nick: &str,
    83         nick: &str,
    92         checker_password: &str,
    84         checker_password: &str,
    93     ) -> Result<bool, Error> {
    85     ) -> mysql_async::Result<bool> {
    94         if let Some(pool) = &self.pool {
    86         let mut connection = self.pool.get_conn().await?;
    95             if let Some(row) = pool.first_exec(GET_ACCOUNT_QUERY, params! { "username" => nick })? {
    87         if let Some((password, _, _)) = GET_ACCOUNT_QUERY
    96                 let (mut password, _, _) = from_row_opt::<(String, i32, i32)>(row)?;
    88             .with(params! { "username" => nick })
    97                 Ok(checker_password == password)
    89             .first::<(String, i32, i32), _>(&mut connection)
    98             } else {
    90             .await?
    99                 Ok(false)
    91         {
   100             }
    92             Ok(checker_password == password)
   101         } else {
    93         } else {
   102             Err(DriverError::SetupError.into())
    94             Ok(false)
   103         }
    95         }
   104     }
    96     }
   105 
    97 
   106     pub fn store_stats(&mut self, stats: &ServerStatistics) -> Result<(), Error> {
    98     pub async fn store_stats(&mut self, stats: &ServerStatistics) -> mysql_async::Result<()> {
   107         if let Some(pool) = &self.pool {
    99         let mut connection = self.pool.get_conn().await?;
   108             for mut stmt in pool.prepare(STORE_STATS_QUERY) {
   100         STORE_STATS_QUERY
   109                 stmt.execute(params! {
   101             .with(params! {
   110                     "players" => stats.players,
   102                 "players" => stats.players,
   111                     "rooms" => stats.rooms,
   103                 "rooms" => stats.rooms,
   112                 })?;
   104             })
   113             }
   105             .ignore(&mut connection)
   114             Ok(())
   106             .await
   115         } else {
       
   116             Err(DriverError::SetupError.into())
       
   117         }
       
   118     }
   107     }
   119 
   108 
   120     pub fn store_achievements(&mut self, achievements: &Achievements) -> Result<(), ()> {
   109     pub async fn store_achievements(&mut self, achievements: &Achievements) -> mysql_async::Result<()> {
   121         Ok(())
   110         Ok(())
   122     }
   111     }
   123 
   112 
   124     pub fn get_replay_name(&mut self, replay_id: u32) -> Result<Option<String>, Error> {
   113     pub async fn get_replay_name(&mut self, replay_id: u32) -> mysql_async::Result<Option<String>> {
   125         if let Some(pool) = &self.pool {
   114         let mut connection = self.pool.get_conn().await?;
   126             if let Some(row) =
   115         GET_REPLAY_NAME_QUERY
   127                 pool.first_exec(GET_REPLAY_NAME_QUERY, params! { "id" => replay_id })?
   116             .with(params! { "id" => replay_id })
   128             {
   117             .first::<String, _>(&mut connection)
   129                 let filename = from_row_opt::<String>(row)?;
   118             .await
   130                 Ok(Some(filename))
       
   131             } else {
       
   132                 Ok(None)
       
   133             }
       
   134         } else {
       
   135             Err(DriverError::SetupError.into())
       
   136         }
       
   137     }
   119     }
   138 }
   120 }
   139 
   121 
   140 fn get_hash(protocol_number: u16, web_password: &str, salt1: &str, salt2: &str) -> Sha1Digest {
   122 fn get_hash(protocol_number: u16, web_password: &str, salt1: &str, salt2: &str) -> Sha1Digest {
   141     let s = format!(
   123     let data = format!(
   142         "{}{}{}{}{}",
   124         "{}{}{}{}{}",
   143         salt1, salt2, web_password, protocol_number, "!hedgewars"
   125         salt1, salt2, web_password, protocol_number, "!hedgewars"
   144     );
   126     );
   145     Sha1Digest::new(sha1(s.as_bytes()))
   127 
       
   128     let mut sha1 = Sha1::new();
       
   129     sha1.update(&data);
       
   130     Sha1Digest::new(sha1.finalize().try_into().unwrap())
   146 }
   131 }