diff --git a/docs/db_struct.sql b/docs/db_struct.sql index ef91fbe..949959f 100644 --- a/docs/db_struct.sql +++ b/docs/db_struct.sql @@ -39,9 +39,9 @@ CREATE TABLE `commentaires` ( PRIMARY KEY (`ID`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; -DROP TABLE IF EXISTS `comunic_client`; -CREATE TABLE `comunic_client` ( - `ID` INT NOT NULL AUTO_INCREMENT, +DROP TABLE IF EXISTS `comunic_clients`; +CREATE TABLE `comunic_clients` ( + `id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(45) NOT NULL, `domain` VARCHAR(45) NULL COMMENT 'Use to check Referer & define Access-Control-Allow-Origin', `comment` VARCHAR(45) NULL COMMENT 'Information about the client', @@ -49,10 +49,11 @@ CREATE TABLE `comunic_client` ( PRIMARY KEY (`ID`)); -DROP TABLE IF EXISTS `comunic_user_access_token`; -CREATE TABLE `comunic_user_access_token` ( +DROP TABLE IF EXISTS `comunic_user_access_tokens`; +CREATE TABLE `comunic_user_access_tokens` ( `id` INT NOT NULL AUTO_INCREMENT, `client_id` INT NOT NULL, + `user_id` INT NOT NULL, `token` VARCHAR(255) NOT NULL, `last_refresh` INT NOT NULL, `timeout` INT NOT NULL, diff --git a/docs/migration.sql b/docs/migration.sql index 7e5e11a..40ef8ed 100644 --- a/docs/migration.sql +++ b/docs/migration.sql @@ -3,4 +3,21 @@ DROP TABLE IF EXISTS `comunic_api_limit_count`; DROP TABLE IF EXISTS `comunic_api_users_tokens`; DROP TABLE IF EXISTS `comunic_api_services_tokens`; DROP TABLE IF EXISTS `comunic_calls_members`; -DROP TABLE IF EXISTS `comunic_calls`; \ No newline at end of file +DROP TABLE IF EXISTS `comunic_calls`; + +CREATE TABLE `comunic_clients` ( + `id` INT NOT NULL AUTO_INCREMENT, + `name` VARCHAR(45) NOT NULL, + `domain` VARCHAR(45) NULL COMMENT 'Use to check Referer & define Access-Control-Allow-Origin', + `comment` VARCHAR(45) NULL COMMENT 'Information about the client', + `default_expiration_time` INT GENERATED ALWAYS AS (2592000) COMMENT '2592000 = 1 month', + PRIMARY KEY (`ID`)); + +CREATE TABLE `comunic_user_access_tokens` ( + `id` INT NOT NULL AUTO_INCREMENT, + `client_id` INT NOT NULL, + `user_id` INT NOT NULL, + `token` VARCHAR(255) NOT NULL, + `last_refresh` INT NOT NULL, + `timeout` INT NOT NULL, + PRIMARY KEY (`id`)); \ No newline at end of file diff --git a/src/constants.rs b/src/constants.rs index d564fdd..61ea9ea 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -9,10 +9,10 @@ use std::time::Duration; /// The name of the tables pub mod database_tables_names { /// API services tokens table - pub const SERVICES_TABLES: &str = "comunic_api_services_tokens"; + pub const CLIENTS_TABLE: &str = "comunic_clients"; /// User access tokens table - pub const USER_ACCESS_TOKENS_TABLE: &str = "comunic_api_users_tokens"; + pub const USER_ACCESS_TOKENS_TABLE: &str = "comunic_user_access_tokens"; /// User table pub const USERS_TABLE: &str = "utilisateurs"; @@ -108,4 +108,7 @@ pub const PASSWORD_MIN_LENGTH: usize = 3; pub const SUPPORTED_LANGUAGES: &'static [&'static str] = &["en", "fr"]; /// Interval at which last active time of user should be recorded -pub const USER_LAST_ACTIVITY_REFRESH: Duration = Duration::from_secs(60); \ No newline at end of file +pub const USER_LAST_ACTIVITY_REFRESH: Duration = Duration::from_secs(60); + +/// Interval at which last activity of an access token should be recorded +pub const USER_ACCESS_TOKEN_ACTIVITY_REFRESH: Duration = Duration::from_secs(60*60); \ No newline at end of file diff --git a/src/controllers/account_controller.rs b/src/controllers/account_controller.rs index cbf5ba8..82f57f1 100644 --- a/src/controllers/account_controller.rs +++ b/src/controllers/account_controller.rs @@ -86,10 +86,9 @@ 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(), - )?; + if let Some(token) = request.user_access_token() { + account_helper::destroy_login_tokens(token)?; + } request.success("User disconnected.") } diff --git a/src/controllers/calls_controller.rs b/src/controllers/calls_controller.rs index 1471ed0..1f2223b 100644 --- a/src/controllers/calls_controller.rs +++ b/src/controllers/calls_controller.rs @@ -50,7 +50,7 @@ impl UserWsRequestHandler { let mut found = false; user_ws_controller::foreach_connection(|p| { - if !found && p.user_id == peer_id && p.is_having_call_with_conversation(call_id) { + if !found && p.user_id() == peer_id && p.is_having_call_with_conversation(call_id) { found = true; } @@ -148,7 +148,7 @@ pub fn join_call(r: &mut UserWsRequestHandler) -> RequestResult { // Remove any other active connection to current call of current user user_ws_controller::foreach_connection(|conn| { - if &conn.user_id != r.user_id_ref()? || conn.session.eq(&r.get_conn().session) { + if conn.user_id() != r.user_id_ref()? || conn.session.eq(&r.get_conn().session) { return Ok(()); } @@ -197,7 +197,7 @@ pub fn get_members_list(r: &mut UserWsRequestHandler) -> RequestResult { user_ws_controller::foreach_connection(|conn| { if conn.is_having_call_with_conversation(&conv_id) { - list.push(CallMemberInfo::new(&conn.user_id, &conn.active_call.as_ref().unwrap())); + list.push(CallMemberInfo::new(conn.user_id(), &conn.active_call.as_ref().unwrap())); } Ok(()) @@ -297,7 +297,7 @@ pub fn mark_user_ready(r: &mut UserWsRequestHandler) -> Res { r.update_call(|call| call.ready = true)?; user_ws_controller::send_message_to_specific_connections( - |c| c.user_id != &user_id && c.is_having_call_with_conversation(&call_id), + |c| c.user_id() != &user_id && c.is_having_call_with_conversation(&call_id), |_| UserWsMessage::no_id_message("call_peer_ready", CallPeerReadyAPI::new(&call_id, r.user_id_ref()?)), None:: _>, )?; @@ -335,7 +335,7 @@ pub fn stop_streaming(r: &mut UserWsRequestHandler) -> Res { // Notify all other users user_ws_controller::send_message_to_specific_connections( - |c| c.is_having_call_with_conversation(&call_id) && c.user_id != &user_id, + |c| c.is_having_call_with_conversation(&call_id) && c.user_id() != &user_id, |_| UserWsMessage::no_id_message("call_peer_interrupted_streaming", CallPeerInterruptedStreamingAPI::new(&call_id, &user_id)), None:: _>, )?; @@ -362,17 +362,17 @@ pub fn make_user_leave_call(conv_id: &ConvID, connection: &UserWsConnection) -> // Close main stream (sender) events_helper::propagate_event(&Event::CloseCallStream(&CloseCallStream { - call_hash: gen_call_hash(&conv_id, &connection.user_id), + call_hash: gen_call_hash(&conv_id, connection.user_id()), peer_id: None, }))?; // Close receiver streams (other users streams) user_ws_controller::foreach_connection( |peer_conn| { - if peer_conn.is_having_call_with_conversation(conv_id) && &peer_conn.user_id != &connection.user_id { + if peer_conn.is_having_call_with_conversation(conv_id) && peer_conn.user_id() != connection.user_id() { events_helper::propagate_event(&Event::CloseCallStream(&CloseCallStream { - call_hash: gen_call_hash(&conv_id, &peer_conn.user_id), - peer_id: Some(connection.user_id.clone()), + call_hash: gen_call_hash(&conv_id, peer_conn.user_id()), + peer_id: Some(connection.user_id().clone()), }))?; } @@ -381,7 +381,7 @@ pub fn make_user_leave_call(conv_id: &ConvID, connection: &UserWsConnection) -> )?; // Create a notification - events_helper::propagate_event(&Event::UserLeftCall(conv_id, &connection.user_id))?; + events_helper::propagate_event(&Event::UserLeftCall(conv_id, connection.user_id()))?; Ok(()) } @@ -391,7 +391,7 @@ pub fn handle_event(e: &events_helper::Event) -> Res { match e { Event::UserJoinedCall(conv_id, user_id) => { user_ws_controller::send_message_to_specific_connections( - |c| c.is_having_call_with_conversation(conv_id) && &c.user_id != user_id, + |c| c.is_having_call_with_conversation(conv_id) && c.user_id() != user_id, |_| UserWsMessage::no_id_message("user_joined_call", JoinedCallMessage::new(conv_id, user_id)), None:: _>, )?; @@ -429,7 +429,7 @@ pub fn handle_event(e: &events_helper::Event) -> Res { user_ws_controller::send_message_to_specific_connections( - |c| c.user_id == target_user && c.is_having_call_with_conversation(&call_id), + |c| c.user_id() == target_user && c.is_having_call_with_conversation(&call_id), |_| UserWsMessage::no_id_message("new_call_signal", NewCallSignalAPI::new(&call_id, &peer_id, &msg.data)?), None:: _>, )?; diff --git a/src/controllers/comments_controller.rs b/src/controllers/comments_controller.rs index 3766943..5b5dd11 100644 --- a/src/controllers/comments_controller.rs +++ b/src/controllers/comments_controller.rs @@ -94,7 +94,7 @@ pub fn handle_event(e: &events_helper::Event) -> Res { |c| c.posts.contains(&comment.post_id), |c| UserWsMessage::no_id_message( "new_comment", - CommentAPI::new(comment, &Some(c.user_id.clone()))?, + CommentAPI::new(comment, &Some(c.user_id().clone()))?, ), None:: _>, )?; @@ -105,7 +105,7 @@ pub fn handle_event(e: &events_helper::Event) -> Res { |c| c.posts.contains(&comment.post_id), |c| UserWsMessage::no_id_message( "comment_updated", - CommentAPI::new(comment, &Some(c.user_id.clone()))?, + CommentAPI::new(comment, &Some(c.user_id().clone()))?, ), None:: _>, )?; diff --git a/src/controllers/conversations_controller.rs b/src/controllers/conversations_controller.rs index bc3d05b..0aa3d8d 100644 --- a/src/controllers/conversations_controller.rs +++ b/src/controllers/conversations_controller.rs @@ -362,7 +362,7 @@ pub fn handle_event(e: &events_helper::Event) -> Res { user_ws_controller::send_message_to_specific_connections( |f| f.conversations.contains(&msg.conv_id), |_| UserWsMessage::no_id_message("new_conv_message", ConversationMessageAPI::new(msg)), - Some(|conn: &UserWsConnection| conversations_helper::mark_user_seen(msg.conv_id, &conn.user_id)), + Some(|conn: &UserWsConnection| conversations_helper::mark_user_seen(msg.conv_id, conn.user_id())), )?; } diff --git a/src/controllers/user_ws_controller.rs b/src/controllers/user_ws_controller.rs index bb5c721..dda6d31 100644 --- a/src/controllers/user_ws_controller.rs +++ b/src/controllers/user_ws_controller.rs @@ -15,12 +15,12 @@ use crate::api_data::res_get_ws_token::ResGetWsToken; use crate::constants::{USER_LAST_ACTIVITY_REFRESH, WS_ACCESS_TOKEN_LENGTH}; use crate::controllers::user_ws_controller::ws_connections_list::{add_connection, find_connection, get_ws_connections_list, remove_connection}; use crate::controllers::user_ws_routes::find_user_ws_route; -use crate::data::api_client::APIClient; use crate::data::base_request_handler::BaseRequestHandler; use crate::data::config::conf; use crate::data::error::{ExecError, Res, ResultBoxError}; use crate::data::http_request_handler::HttpRequestHandler; use crate::data::user::UserID; +use crate::data::user_token::UserAccessToken; use crate::data::user_ws_connection::UserWsConnection; use crate::data::user_ws_message::UserWsMessage; use crate::data::user_ws_request_handler::{UserWsRequestHandler, UserWsResponseType}; @@ -41,15 +41,14 @@ mod ws_tokens_list { use std::sync::Mutex; use crate::constants::WS_ACCESS_TOKEN_LIFETIME; - use crate::data::user::UserID; + use crate::data::user_token::UserAccessToken; use crate::utils::date_utils::time; #[derive(Debug)] pub struct WsToken { pub time: u64, - pub client_id: u32, - pub user_id: UserID, - pub token: String, + pub user_token: UserAccessToken, + pub ws_token: String, pub incognito: bool, pub remote_ip: String, } @@ -89,7 +88,7 @@ mod ws_tokens_list { let list = get_list(); let mut list = list.lock().unwrap(); for i in 0..list.len() { - if list[i].token == t { + if list[i].ws_token == t { return Some(list.remove(i)); } } @@ -178,10 +177,9 @@ pub fn get_token(r: &mut HttpRequestHandler) -> ResultBoxError { let access_token = rand_str(WS_ACCESS_TOKEN_LENGTH); let token = ws_tokens_list::WsToken { - user_id: r.user_id()?, - client_id: r.api_client().id, + user_token: r.some_or_internal_error(r.user_access_token().cloned(), "No access token!")?, time: time(), - token: access_token.to_string(), + ws_token: access_token.to_string(), incognito: r.post_bool_opt("incognito", false), remote_ip: r.remote_ip(), }; @@ -195,10 +193,8 @@ pub fn get_token(r: &mut HttpRequestHandler) -> ResultBoxError { pub struct WsSession { // NOTE : apart from hb, the values here won't change ! - user_id: UserID, - - // Client used for the connection - client_id: u32, + // Information about user connection + user_token: UserAccessToken, // Remote IP address remote_ip: String, @@ -232,16 +228,16 @@ impl WsSession { }); } - /// helper method that update user last activity at every specified amount of time + /// Helper method that update user last activity at every specified amount of time fn user_activity(&self, ctx: &mut actix_web_actors::ws::WebsocketContext) { - if !self.incognito && account_helper::update_last_activity(&self.user_id).is_err() { - eprintln!("Failed to do initial refresh of last activity for user {} !", self.user_id.id()); + if !self.incognito && account_helper::update_last_activity(&self.user_token.user_id).is_err() { + eprintln!("Failed to do initial refresh of last activity for user {} !", self.user_token.user_id.id()); } ctx.run_interval(USER_LAST_ACTIVITY_REFRESH, |_, ctx| { if let Some(conn) = find_connection(ctx.address()) { - if !conn.incognito && account_helper::update_last_activity(&conn.user_id).is_err() { - eprintln!("Failed to refresh last activity for user {} !", conn.user_id.id()); + if !conn.incognito && account_helper::update_last_activity(conn.user_id()).is_err() { + eprintln!("Failed to refresh last activity for user {} !", conn.user_id().id()); } } }); @@ -312,8 +308,7 @@ impl Actor for WsSession { self.user_activity(ctx); add_connection(UserWsConnection { - user_id: self.user_id.clone(), - client_id: self.client_id, + user_token: self.user_token.clone(), remote_ip: self.remote_ip.clone(), session: ctx.address(), incognito: self.incognito, @@ -445,10 +440,9 @@ pub async fn ws_route( actix_web_actors::ws::start( WsSession { remote_ip: token.remote_ip, - user_id: token.user_id, + user_token: token.user_token, hb: std::time::Instant::now(), incognito: token.incognito, - client_id: token.client_id, }, &req, stream, @@ -467,7 +461,7 @@ pub fn send_message_to_users(msg: &UserWsMessage, users: &Vec) -> Res { .lock() .unwrap() .iter() - .filter(|f| users.contains(&f.user_id)) + .filter(|f| users.contains(f.user_id())) .map(|f| f.session.clone()) .collect::>>(); @@ -484,7 +478,7 @@ pub fn send_message_to_user(msg: &UserWsMessage, user: &UserID) -> Res { .lock() .unwrap() .iter() - .filter(|f| user == &f.user_id) + .filter(|f| user == f.user_id()) .map(|f| f.session.clone()) .collect::>>(); @@ -527,22 +521,22 @@ pub fn send_message_to_specific_connections(filter: F, msg_generator: M /// Check out whether user is connected or not pub fn is_user_connected(user_id: &UserID) -> bool { - get_ws_connections_list().lock().unwrap().iter().any(|c| &c.user_id == user_id) + get_ws_connections_list().lock().unwrap().iter().any(|c| c.user_id() == user_id) } /// Check out whether user is connected or not and has at list one not incognito connection pub fn is_user_connected_not_incognito(user_id: &UserID) -> bool { - get_ws_connections_list().lock().unwrap().iter().any(|c| &c.user_id == user_id && !c.incognito) + get_ws_connections_list().lock().unwrap().iter().any(|c| c.user_id() == user_id && !c.incognito) } -/// Disconnect a user from all the WebSockets of a given client -pub fn disconnect_user_from_client(user_id: &UserID, client: &APIClient) -> Res { +/// Disconnect a user from all the WebSockets of a given access token +pub fn disconnect_from_user_token(token: &UserAccessToken) -> Res { let connections = get_ws_connections_list() .lock() .unwrap() .iter() - .filter(|f| &f.user_id == user_id && f.client_id == client.id) + .filter(|f| &f.user_token == token) .map(|f| f.session.clone()) .collect::>>(); @@ -559,7 +553,7 @@ pub fn disconnect_user_from_all_sockets(user_id: &UserID) -> Res { .lock() .unwrap() .iter() - .filter(|f| &f.user_id == user_id) + .filter(|f| f.user_id() == user_id) .map(|f| f.session.clone()) .collect::>>(); @@ -585,8 +579,8 @@ pub fn foreach_connection(mut f: F) -> Res /// Events handler pub fn handle_event(e: &events_helper::Event) -> Res { match e { - Event::DestroyedLoginToken(user_id, client) => { - disconnect_user_from_client(user_id, client)?; + Event::DestroyedLoginToken(token) => { + disconnect_from_user_token(token)?; } _ => {} } diff --git a/src/data/api_client.rs b/src/data/api_client.rs index 682b1ea..ef9afe6 100644 --- a/src/data/api_client.rs +++ b/src/data/api_client.rs @@ -1,11 +1,11 @@ /// API client information /// /// @author Pierre HUBERT - #[derive(Debug)] pub struct APIClient { - pub id: u32, + pub id: u64, pub name: String, - pub token: String, pub domain: Option, + pub comment: Option, + pub default_expiration_time: u64, } \ No newline at end of file diff --git a/src/data/base_request_handler.rs b/src/data/base_request_handler.rs index 29d06ca..9db0eae 100644 --- a/src/data/base_request_handler.rs +++ b/src/data/base_request_handler.rs @@ -15,11 +15,12 @@ use crate::controllers::routes::RequestResult; use crate::data::comment::Comment; use crate::data::conversation::ConvID; use crate::data::custom_emoji::CustomEmoji; -use crate::data::error::{ExecError, ResultBoxError}; +use crate::data::error::{ExecError, Res, ResultBoxError}; use crate::data::group::GroupAccessLevel; use crate::data::group_id::GroupID; use crate::data::post::{Post, PostAccessLevel}; use crate::data::user::UserID; +use crate::data::user_token::UserAccessToken; use crate::helpers::{account_helper, comments_helper, conversations_helper, custom_emojies_helper, friends_helper, groups_helper, posts_helper, user_helper, virtual_directory_helper}; use crate::helpers::virtual_directory_helper::VirtualDirType; use crate::utils::pdf_utils::is_valid_pdf; @@ -58,8 +59,13 @@ pub trait BaseRequestHandler { /// Get remote IP address fn remote_ip(&self) -> String; + /// Current user access token + fn user_access_token(&self) -> Option<&UserAccessToken>; + /// Current user ID - fn user_id_opt_ref(&self) -> Option<&UserID>; + fn user_id_opt_ref(&self) -> Option<&UserID> { + self.user_access_token().map(|u| &u.user_id) + } /// Success message @@ -144,6 +150,17 @@ pub trait BaseRequestHandler { } } + /// Unwrap an option, returning an error if none is returned + fn some_or_internal_error(&mut self, opt: Option, msg: &str) -> Res { + match opt { + None => { + self.internal_error(ExecError::boxed_new(msg))?; + unreachable!() + } + Some(e) => Ok(e) + } + } + /// Get a user ID, if available fn user_id_opt(&self) -> Option { @@ -189,6 +206,18 @@ pub trait BaseRequestHandler { self.post_string_opt(name, 1, true) } + /// Get a post string with a given name. If the value is not found, attempt to get the value + /// with another name + /// + /// This function is useful to upgrade system + fn post_string_with_fallback(&mut self, name: &str, fallback: &str) -> Res { + if self.has_post_parameter(name) { + self.post_string(name) + } else { + self.post_string(fallback) + } + } + /// Get a post string, specifying minimum length fn post_string_opt(&mut self, name: &str, min_length: usize, required: bool) -> ResultBoxError { diff --git a/src/data/http_request_handler.rs b/src/data/http_request_handler.rs index 120eb91..1555cfc 100644 --- a/src/data/http_request_handler.rs +++ b/src/data/http_request_handler.rs @@ -11,8 +11,8 @@ use crate::controllers::routes::RequestResult; use crate::data::api_client::APIClient; use crate::data::base_request_handler::{BaseRequestHandler, RequestValue}; use crate::data::config::conf; -use crate::data::error::ResultBoxError; -use crate::data::user::UserID; +use crate::data::error::{Res, ResultBoxError}; +use crate::data::user_token::UserAccessToken; use crate::helpers::{account_helper, api_helper}; /// Http request handler @@ -25,7 +25,7 @@ pub struct HttpRequestHandler { response: Option, headers: HashMap, client: Option, - curr_user_id: Option, + curr_user_token: Option, } impl HttpRequestHandler { @@ -37,7 +37,7 @@ impl HttpRequestHandler { response: None, headers: HashMap::new(), client: None, - curr_user_id: None, + curr_user_token: None, } } @@ -78,11 +78,10 @@ impl HttpRequestHandler { /// Check API client tokens pub fn check_client_token(&mut self) -> RequestResult { - let api_name = self.post_string("serviceName")?; - let api_token = self.post_string("serviceToken")?; + let client_name = self.post_string_with_fallback("client", "serviceName")?; let client = self.ok_or_bad_request( - api_helper::get_client(&api_name, &api_token), + api_helper::get_client(&client_name), "Client not recognized!", )?; @@ -111,13 +110,17 @@ impl HttpRequestHandler { } /// Check login token - pub fn check_user_token(&mut self) -> RequestResult { + pub fn check_user_token(&mut self) -> Res { let token = self.post_string("userToken1")?; // Find user - match account_helper::get_user_by_login_token(&token, self.api_client()) { - Ok(id) => { - self.curr_user_id = Some(id); + match account_helper::find_user_by_login_token(&token, self.api_client()) { + Ok(token) => { + if token.need_refresh() { + account_helper::refresh_access_token(&token)?; + } + + self.curr_user_token = Some(token); Ok(()) } @@ -177,7 +180,7 @@ impl BaseRequestHandler for HttpRequestHandler { ip } - fn user_id_opt_ref(&self) -> Option<&UserID> { - self.curr_user_id.as_ref() + fn user_access_token(&self) -> Option<&UserAccessToken> { + self.curr_user_token.as_ref() } } \ No newline at end of file diff --git a/src/data/user.rs b/src/data/user.rs index 19f4ce5..f252583 100644 --- a/src/data/user.rs +++ b/src/data/user.rs @@ -50,6 +50,12 @@ impl PartialEq<&UserID> for UserID { } } +impl PartialEq for &UserID { + fn eq(&self, other: &UserID) -> bool { + self.0 == other.0 + } +} + #[derive(Debug, PartialEq)] pub enum UserPageStatus { OPEN, diff --git a/src/data/user_token.rs b/src/data/user_token.rs index b0e96a3..4584992 100644 --- a/src/data/user_token.rs +++ b/src/data/user_token.rs @@ -1,11 +1,29 @@ +use crate::constants::USER_ACCESS_TOKEN_ACTIVITY_REFRESH; use crate::data::user::UserID; +use crate::utils::date_utils::time; /// User access token information /// /// Author : Pierre Hubert -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct UserAccessToken { + pub id: u64, + pub client_id: u64, pub user_id: UserID, - pub client_id: u32, - pub token: String + pub token: String, + pub last_refresh: u64, + pub timeout: u64, +} + +impl UserAccessToken { + /// Check out whether access token should be refreshed + pub fn need_refresh(&self) -> bool { + self.last_refresh + USER_ACCESS_TOKEN_ACTIVITY_REFRESH.as_secs() < time() + } +} + +impl PartialEq for UserAccessToken { + fn eq(&self, other: &UserAccessToken) -> bool { + self.id == other.id + } } \ No newline at end of file diff --git a/src/data/user_ws_connection.rs b/src/data/user_ws_connection.rs index 7f0c983..cd6274e 100644 --- a/src/data/user_ws_connection.rs +++ b/src/data/user_ws_connection.rs @@ -4,6 +4,7 @@ use crate::controllers::user_ws_controller::WsSession; use crate::data::conversation::ConvID; use crate::data::post::PostID; use crate::data::user::UserID; +use crate::data::user_token::UserAccessToken; #[derive(Clone, Debug)] pub struct ActiveCall { @@ -14,8 +15,7 @@ pub struct ActiveCall { /// This structure contains information about an active connection #[derive(Clone, Debug)] pub struct UserWsConnection { - pub user_id: UserID, - pub client_id: u32, + pub user_token: UserAccessToken, pub remote_ip: String, pub session: actix::Addr, pub incognito: bool, @@ -25,6 +25,11 @@ pub struct UserWsConnection { } impl UserWsConnection { + pub fn user_id(&self) -> &UserID { + &self.user_token.user_id + } + + /// Check out whether a connection is being used to make a call in a specific conversation or not pub fn is_having_call_with_conversation(&self, conv_id: &ConvID) -> bool { if let Some(call_info) = &self.active_call { diff --git a/src/data/user_ws_request_handler.rs b/src/data/user_ws_request_handler.rs index 6840a8e..1e09ca5 100644 --- a/src/data/user_ws_request_handler.rs +++ b/src/data/user_ws_request_handler.rs @@ -8,7 +8,7 @@ use crate::api_data::http_error::HttpError; use crate::controllers::routes::RequestResult; use crate::data::base_request_handler::{BaseRequestHandler, RequestValue}; use crate::data::error::ResultBoxError; -use crate::data::user::UserID; +use crate::data::user_token::UserAccessToken; use crate::data::user_ws_connection::UserWsConnection; pub enum UserWsResponseType { @@ -88,11 +88,7 @@ impl BaseRequestHandler for UserWsRequestHandler { self.connection.remote_ip.to_string() } - fn user_id_opt_ref(&self) -> Option<&UserID> { - Some(&self.connection.user_id) - } - - fn user_id(&self) -> ResultBoxError { - Ok(self.connection.user_id.clone()) + fn user_access_token(&self) -> Option<&UserAccessToken> { + Some(&self.connection.user_token) } } \ No newline at end of file diff --git a/src/helpers/account_helper.rs b/src/helpers/account_helper.rs index 8da2abe..dace58c 100644 --- a/src/helpers/account_helper.rs +++ b/src/helpers/account_helper.rs @@ -13,7 +13,7 @@ use crate::data::security_settings::SecuritySettings; use crate::data::user::{AccountImageVisibility, User, UserID, UserPageStatus}; use crate::data::user_token::UserAccessToken; use crate::helpers::{comments_helper, conversations_helper, custom_emojies_helper, database, events_helper, friends_helper, groups_helper, likes_helper, notifications_helper, posts_helper, survey_helper, user_helper}; -use crate::helpers::database::{DeleteQuery, InsertQuery, QueryInfo}; +use crate::helpers::database::{DeleteQuery, InsertQuery, QueryInfo, UpdateInfo}; use crate::helpers::events_helper::Event; use crate::helpers::likes_helper::LikeType; use crate::utils::crypt_utils::{legacy_crypt_pass, rand_str}; @@ -47,56 +47,45 @@ pub fn login_user(email: &str, password: &str, client: &APIClient) -> ResultBoxE return Err(ExecError::boxed_new("The user gave an invalid password!")); } - // 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); - } - - // Create new login tokens let new_token = UserAccessToken { + id: 0, user_id: user.id.clone(), client_id: client.id, token: rand_str(150), + last_refresh: time(), + timeout: client.default_expiration_time, }; // Save it - database::insert( - InsertQuery::new(USER_ACCESS_TOKENS_TABLE) - .add_user_id("user_id", &new_token.user_id) - .add_u32("service_id", client.id) - .add_str("token1", &new_token.token) - .add_str("token2", "dummy_data") - )?; + InsertQuery::new(USER_ACCESS_TOKENS_TABLE) + .add_u64("client_id", client.id) + .add_user_id("user_id", &new_token.user_id) + .add_str("token", &new_token.token) + .add_u64("last_refresh", new_token.last_refresh) + .add_u64("timeout", new_token.timeout) + .insert_drop_result()?; 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", user_id) - .cond_u32("service_id", client.id), - |res| { - Ok(UserAccessToken { - user_id: res.get_user_id("user_id")?, - client_id: res.get_int64("service_id")? as u32, - token: res.get_str("token1")?, - }) - }, - ) -} - /// Find a user ID based on login token -pub fn get_user_by_login_token(token: &str, client: &APIClient) -> ResultBoxError { - database::query_row( - QueryInfo::new(USER_ACCESS_TOKENS_TABLE) - .cond_u32("service_id", client.id) - .cond("token1", token) - .add_field("user_id"), - |res| res.get_user_id("user_id"), - ) +pub fn find_user_by_login_token(token: &str, client: &APIClient) -> ResultBoxError { + QueryInfo::new(USER_ACCESS_TOKENS_TABLE) + .cond_u64("client_id", client.id) + .cond("token", token) + .set_custom_where("last_refresh + timeout > ?") + .add_custom_where_argument_u64(time()) + .query_row(|res| { + Ok(UserAccessToken { + id: res.get_u64("id")?, + client_id: res.get_u64("client_id")?, + user_id: res.get_user_id("user_id")?, + token: res.get_str("token")?, + last_refresh: res.get_u64("last_refresh")?, + timeout: res.get_u64("timeout")?, + }) + }) } /// Check out whether an email address exists or not @@ -107,15 +96,22 @@ pub fn exists_mail(mail: &str) -> ResultBoxError { .map(|r| r > 0) } +/// Refresh a user access token +pub fn refresh_access_token(token: &UserAccessToken) -> Res { + UpdateInfo::new(USER_ACCESS_TOKENS_TABLE) + .cond_u64("id", token.id) + .set_u64("last_refresh", time()) + .exec() +} + /// 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_user_id("user_id", id) - )?; +pub fn destroy_login_tokens(access_tokens: &UserAccessToken) -> Res { + DeleteQuery::new(USER_ACCESS_TOKENS_TABLE) + .cond_u64("id", access_tokens.id) + .exec()?; // Send an event (destroyed_login_tokens) - events_helper::propagate_event(&Event::DestroyedLoginToken(id.clone(), client))?; + events_helper::propagate_event(&Event::DestroyedLoginToken(access_tokens))?; Ok(()) } diff --git a/src/helpers/api_helper.rs b/src/helpers/api_helper.rs index ba00713..876d14c 100644 --- a/src/helpers/api_helper.rs +++ b/src/helpers/api_helper.rs @@ -1,27 +1,23 @@ +use crate::constants::database_tables_names::CLIENTS_TABLE; use crate::data::api_client::APIClient; -use crate::helpers::database; -use crate::helpers::database::QueryInfo; -use crate::constants::database_tables_names::SERVICES_TABLES; use crate::data::error::ResultBoxError; +use crate::helpers::database::QueryInfo; /// API helper /// /// @author Pierre Hubert /// Get information about a client -pub fn get_client(name: &str, token: &str) -> ResultBoxError { - database::query_row( - QueryInfo::new(SERVICES_TABLES) - .cond("service_name", name) - .cond("token", token), - - |res| { - Ok(APIClient { - id: res.get_int64("id")? as u32, - name: res.get_str("service_name")?, - token: res.get_str("token")?, - domain: res.get_optional_str("client_domain")?, - }) - } - ) +pub fn get_client(name: &str) -> ResultBoxError { + QueryInfo::new(CLIENTS_TABLE) + .cond("name", name) + .query_row(|res| { + Ok(APIClient { + id: res.get_u64("id")?, + name: res.get_str("name")?, + domain: res.get_optional_str("domain")?, + comment: res.get_optional_str("comment")?, + default_expiration_time: res.get_u64("default_expiration_time")?, + }) + }) } \ No newline at end of file diff --git a/src/helpers/events_helper.rs b/src/helpers/events_helper.rs index 7cb662d..1d859aa 100644 --- a/src/helpers/events_helper.rs +++ b/src/helpers/events_helper.rs @@ -5,13 +5,13 @@ use crate::controllers::{calls_controller, comments_controller, conversations_controller, notifications_controller, rtc_relay_controller, user_ws_controller}; -use crate::data::api_client::APIClient; use crate::data::call_signal::{CloseCallStream, NewRtcRelayMessage, NewUserCallSignal, UserCallOfferRequest}; use crate::data::comment::Comment; use crate::data::conversation::ConvID; use crate::data::conversation_message::ConversationMessage; use crate::data::error::Res; use crate::data::user::UserID; +use crate::data::user_token::UserAccessToken; use crate::data::user_ws_connection::UserWsConnection; pub enum Event<'a> { @@ -21,7 +21,7 @@ pub enum Event<'a> { UserWsClosed(&'a UserWsConnection), /// Destroyed a login token - DestroyedLoginToken(UserID, &'a APIClient), + DestroyedLoginToken(&'a UserAccessToken), /// Updated the number of notifications of one of multiple users user UpdatedNotificationsNumber(&'a Vec),