diff --git a/Cargo.lock b/Cargo.lock index 841e351..9dd1ce9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -514,6 +514,7 @@ dependencies = [ "mailchecker", "mysql", "percent-encoding", + "rand", "serde", "sha1", "yaml-rust", diff --git a/Cargo.toml b/Cargo.toml index 76595ec..c31279f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,5 @@ futures = "0.3.5" encoding_rs = "0.8.23" percent-encoding = "2.1.0" mailchecker = "3.3.6" -sha1 = "0.6.0" \ No newline at end of file +sha1 = "0.6.0" +rand = "0.7.3" \ No newline at end of file diff --git a/src/data/mod.rs b/src/data/mod.rs index 3ca7b88..29dd6cf 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -4,4 +4,5 @@ pub mod config; pub mod http_request_handler; pub mod api_client; -pub mod user; \ No newline at end of file +pub mod user; +pub mod user_token; \ No newline at end of file diff --git a/src/data/user.rs b/src/data/user.rs index b446001..400045c 100644 --- a/src/data/user.rs +++ b/src/data/user.rs @@ -1,9 +1,12 @@ -/// User information -/// -/// @author Pierre Hubert +///! User information +///! +///! @author Pierre Hubert + +pub type UserID = i64; + #[derive(Debug)] pub struct User { - pub id: i64, + pub id: UserID, pub email: String, pub password: String, pub first_name: String, diff --git a/src/data/user_token.rs b/src/data/user_token.rs new file mode 100644 index 0000000..b0e96a3 --- /dev/null +++ b/src/data/user_token.rs @@ -0,0 +1,11 @@ +use crate::data::user::UserID; + +/// User access token information +/// +/// Author : Pierre Hubert +#[derive(Debug)] +pub struct UserAccessToken { + pub user_id: UserID, + pub client_id: u32, + pub token: String +} \ No newline at end of file diff --git a/src/database_structure.rs b/src/database_structure.rs index cd17142..d7adcce 100644 --- a/src/database_structure.rs +++ b/src/database_structure.rs @@ -5,5 +5,8 @@ /// API services tokens table pub const SERVICES_TABLES : &str = "comunic_api_services_tokens"; +/// User access tokens table +pub const USER_ACCESS_TOKENS_TABLE: &str = "comunic_api_users_tokens"; + /// User table pub const USERS_TABLE : &str = "utilisateurs"; \ No newline at end of file diff --git a/src/helpers/account_helper.rs b/src/helpers/account_helper.rs index 3e835dc..09409ef 100644 --- a/src/helpers/account_helper.rs +++ b/src/helpers/account_helper.rs @@ -1,6 +1,11 @@ use crate::data::api_client::APIClient; -use crate::data::error::ResultBoxError; -use crate::helpers::user_helper; +use crate::data::error::{ExecError, ResultBoxError}; +use crate::data::user::UserID; +use crate::data::user_token::UserAccessToken; +use crate::database_structure::USER_ACCESS_TOKENS_TABLE; +use crate::helpers::{database, user_helper}; +use crate::helpers::database::{QueryInfo, InsertQuery}; +use crate::utils::crypt_utils::{crypt_pass, rand_str}; /// Account helper /// @@ -13,9 +18,49 @@ use crate::helpers::user_helper; pub fn login_user(email: &str, password: &str, client: &APIClient) -> ResultBoxError { let user = user_helper::find_user_by_email(email)?; - // TODO : check user password + // Validate user password + let password = crypt_pass(password)?; + if !user.password.eq(&password) { + return Err(ExecError::boxed_new("The user gave an invalid password!")); + } - println!("{:#?}", user); + // Check if we already have a login token for this user + if let Ok(token) = get_client_tokens(user.id, client) { + return Ok(token.token); + } - Ok("d".to_string()) + + // Create new login tokens + let new_token = UserAccessToken { + user_id: user.id, + client_id: client.id, + token: rand_str(150) + }; + + // Save it + database::insert( + InsertQuery::new(USER_ACCESS_TOKENS_TABLE) + .add_i64("user_id", new_token.user_id) + .add_u32("service_id", client.id) + .add_str("token1", &new_token.token) + .add_str("token2", "dummy_data") + )?; + + Ok(new_token.token) +} + +/// Get user login tokens +fn get_client_tokens(user_id: UserID, client: &APIClient) -> ResultBoxError { + database::query_row( + QueryInfo::new(USER_ACCESS_TOKENS_TABLE) + .cond("user_id", user_id.to_string().as_ref()) + .cond("service_id", client.id.to_string().as_ref()), + |res| { + Ok(UserAccessToken { + user_id: res.get_int64("user_id")?, + client_id: res.get_int64("service_id")? as u32, + token: res.get_str("token1")?, + }) + }, + ) } \ No newline at end of file diff --git a/src/helpers/database.rs b/src/helpers/database.rs index b083dc0..84518aa 100644 --- a/src/helpers/database.rs +++ b/src/helpers/database.rs @@ -8,6 +8,7 @@ use mysql::prelude::Queryable; use crate::data::config::DatabaseConfig; use crate::data::error::{ExecError, ResultBoxError}; +use std::collections::HashMap; /// Database access helper /// @@ -224,4 +225,62 @@ pub fn query ProcessRowResult>(info: QueryInfo, proce Ok(res) +} + +/// Structure used to execute a insert query +pub struct InsertQuery { + pub table : String, + pub values: HashMap, +} + +impl InsertQuery { + + /// Construct a new InsertQuery instance + pub fn new(table: &str) -> InsertQuery { + InsertQuery { + table: table.to_string(), + values: HashMap::new() + } + } + + /// Add a string + pub fn add_str(mut self, key: &str, value: &str) -> InsertQuery { + self.values.insert(key.to_string(), Value::from(value)); + self + } + + /// Add an integer + pub fn add_i64(mut self, key: &str, value: i64) -> InsertQuery { + self.values.insert(key.to_string(), Value::from(value)); + self + } + + pub fn add_u32(mut self, key: &str, value: u32) -> InsertQuery { + self.values.insert(key.to_string(), Value::from(value)); + self + } +} + +/// Insert a new entry into the database +pub fn insert(query: InsertQuery) -> ResultBoxError<()> { + + // Collect keys + let keys = query.values + .keys() + .map(|f|f.to_string()) + .collect::>(); + + let query_sql = format!( + "INSERT INTO {} ({}) VALUES({})", + query.table, + keys.join(", "), + keys.iter().map(|_| "?").collect::>().join(", ") + ); + + get_connection()?.exec_drop( + query_sql, + query.values.values().collect::>() + )?; + + Ok(()) } \ No newline at end of file diff --git a/src/utils/crypt_utils.rs b/src/utils/crypt_utils.rs index 021c5f7..92ea1f2 100644 --- a/src/utils/crypt_utils.rs +++ b/src/utils/crypt_utils.rs @@ -5,6 +5,8 @@ extern crate sha1; use crate::data::error::{ResultBoxError, ExecError}; +use rand::{thread_rng, Rng}; +use rand::distributions::Alphanumeric; /// Generate a new sha1 string /// @@ -44,4 +46,20 @@ pub fn crypt_pass(pass: &str) -> ResultBoxError { } Ok(String::from_utf8(result.stdout)?) +} + +/// Generate a random string of a given size +/// +/// ``` +/// use comunic_server::utils::crypt_utils::rand_str; +/// +/// let size = 10; +/// let rand = rand_str(size); +/// assert_eq!(size, rand.len()); +/// ``` +pub fn rand_str(len: usize) -> String { + thread_rng() + .sample_iter(&Alphanumeric) + .take(len) + .collect() } \ No newline at end of file