diff --git a/src/controllers/account_controller.rs b/src/controllers/account_controller.rs index 9ee1a40..a4bd650 100644 --- a/src/controllers/account_controller.rs +++ b/src/controllers/account_controller.rs @@ -31,6 +31,16 @@ pub fn login_user(request: &mut HttpRequestHandler) -> RequestResult { } } +/// Sign out user +pub fn logout_user(request: &mut HttpRequestHandler) -> RequestResult { + account_helper::destroy_login_tokens( + request.user_id()?, + request.api_client() + )?; + + request.success("User disconnected.") +} + /// Get current user ID pub fn user_id(request: &mut HttpRequestHandler) -> RequestResult { request.set_response(CurrentUserID::new(request.user_id()?)) diff --git a/src/controllers/routes.rs b/src/controllers/routes.rs index faaac43..fbd45af 100644 --- a/src/controllers/routes.rs +++ b/src/controllers/routes.rs @@ -72,6 +72,9 @@ pub fn get_routes() -> Vec { Route::post_without_login("/account/login", Box::new(account_controller::login_user)), Route::post_without_login("/user/connectUSER", Box::new(account_controller::login_user)), + Route::post("/account/logout", Box::new(account_controller::logout_user)), + Route::post("/user/disconnectUSER", Box::new(account_controller::logout_user)), + Route::post("/account/id", Box::new(account_controller::user_id)), Route::post("/user/getCurrentUserID", Box::new(account_controller::user_id)), ] diff --git a/src/helpers/account_helper.rs b/src/helpers/account_helper.rs index 582e352..7861b7a 100644 --- a/src/helpers/account_helper.rs +++ b/src/helpers/account_helper.rs @@ -4,7 +4,7 @@ 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::helpers::database::{QueryInfo, InsertQuery, DeleteQuery}; use crate::utils::crypt_utils::{crypt_pass, rand_str}; /// Account helper @@ -74,4 +74,15 @@ pub fn get_user_by_login_token(token: &str, client: &APIClient) -> ResultBoxErro .add_field("user_id"), |res| res.get_int64("user_id"), ) +} + +/// Destroy a given user login tokens +pub fn destroy_login_tokens(id: UserID, client: &APIClient) -> ResultBoxError<()> { + + database::delete(DeleteQuery::new(USER_ACCESS_TOKENS_TABLE) + .cond_u32("service_id", client.id) + .cond_i64("user_id", id) + )?; + + Ok(()) } \ No newline at end of file diff --git a/src/helpers/database.rs b/src/helpers/database.rs index 4174174..62318f3 100644 --- a/src/helpers/database.rs +++ b/src/helpers/database.rs @@ -162,7 +162,7 @@ impl<'a> RowResult<'a> { pub fn get_optional_str(&self, name: &str) -> ResultBoxError> { match self.is_null(name)? { true => Ok(None), - false => Ok(Some(self.get_str(name)?).map_or(None, |d|{ + false => Ok(Some(self.get_str(name)?).map_or(None, |d| { match d.is_empty() { true => None, false => Some(d) @@ -247,17 +247,16 @@ pub fn query ProcessRowResult>(info: QueryInfo, proce /// Structure used to execute a insert query pub struct InsertQuery { - pub table : String, + 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() + values: HashMap::new(), } } @@ -285,7 +284,7 @@ pub fn insert(query: InsertQuery) -> ResultBoxError<()> { // Collect keys let keys = query.values .keys() - .map(|f|f.to_string()) + .map(|f| f.to_string()) .collect::>(); let query_sql = format!( @@ -297,7 +296,64 @@ pub fn insert(query: InsertQuery) -> ResultBoxError<()> { get_connection()?.exec_drop( query_sql, - query.values.values().collect::>() + query.values.values().collect::>(), + )?; + + Ok(()) +} + + +/// Structure used to build delete queries +pub struct DeleteQuery { + table: String, + conditions: HashMap, +} + +impl DeleteQuery { + /// Construct a new delete table query + pub fn new(table: &str) -> DeleteQuery { + DeleteQuery { + table: table.to_string(), + conditions: HashMap::new(), + } + } + + /// Add a string condition + pub fn cond_str(mut self, key: &str, value: &str) -> DeleteQuery { + self.conditions.insert(key.to_string(), Value::from(value)); + self + } + + /// Add an integer condition + pub fn cond_u32(mut self, key: &str, value: u32) -> DeleteQuery { + self.conditions.insert(key.to_string(), Value::from(value)); + self + } + + pub fn cond_i64(mut self, key: &str, value: i64) -> DeleteQuery { + self.conditions.insert(key.to_string(), Value::from(value)); + self + } +} + +/// Delete an entry from the database +pub fn delete(query: DeleteQuery) -> ResultBoxError<()> { + if query.conditions.is_empty() { + return Err(ExecError::boxed_new("DELETE without WHERE condition blocked for security reasons!")); + } + + let query_sql = format!( + "DELETE FROM {} WHERE {}", + query.table, + query.conditions.keys() + .map(|f| format!("{} = ?", f)) + .collect::>() + .join(" AND ") + ); + + get_connection()?.exec_drop( + query_sql, + query.conditions.values().collect::>() )?; Ok(())