diff --git a/visions/server/src/core.rs b/visions/server/src/core.rs index 90ecdd7..753e642 100644 --- a/visions/server/src/core.rs +++ b/visions/server/src/core.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, sync::Arc}; use async_std::sync::RwLock; -use chrono::{DateTime, TimeDelta, Utc}; +use chrono::{DateTime, Duration, TimeDelta, Utc}; use mime::Mime; use result_extended::{error, fatal, ok, result_as_fatal, return_error, ResultExt}; use serde::{Deserialize, Serialize}; @@ -165,7 +165,7 @@ impl Core { Some(_) => error(AppError::UsernameUnavailable), None => match state .db - .create_user(username, "", false, AccountState::PasswordReset(Utc::now())) + .create_user(username, "", false, AccountState::PasswordReset(Utc::now() + Duration::minutes(60))) .await { Ok(user_id) => ok(user_id), @@ -341,6 +341,7 @@ impl Core { ) -> ResultExt { let now = Utc::now(); let state = self.0.read().await; + let user = state.db.user_by_username(username).await.unwrap().unwrap(); let user_info = return_error!(match state.db.user_by_username(username).await { Ok(Some(row)) if row.password == password => ok(row), Ok(_) => error(AppError::AuthFailed), @@ -375,6 +376,14 @@ impl Core { Err(fatal_error) => fatal(fatal_error), } } + + pub async fn delete_session(&self, session_id: &SessionId) -> ResultExt<(), AppError, FatalError> { + let state = self.0.read().await; + match state.db.delete_session(session_id).await { + Ok(_) => ok(()), + Err(err) => fatal(err), + } + } } fn create_expiration_date() -> DateTime { diff --git a/visions/server/src/database/disk_db.rs b/visions/server/src/database/disk_db.rs index 6df6b5b..e1d7e16 100644 --- a/visions/server/src/database/disk_db.rs +++ b/visions/server/src/database/disk_db.rs @@ -237,6 +237,21 @@ impl DiskDb { } } + fn delete_session(&self, session_id: &SessionId) -> Result<(), FatalError> { + match self.session(session_id) { + Ok(Some(_)) => { + let mut stmt = self.conn.prepare("DELETE FROM sessions WHERE id = ?") + .map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?; + + let session_id = SessionId::new(); + stmt.execute((session_id.as_str(),)).unwrap(); + Ok(()) + } + Ok(None) => Err(FatalError::DatabaseKeyMissing), + Err(err) => Err(err), + } + } + pub fn character(&self, id: CharacterId) -> Result, FatalError> { let mut stmt = self .conn @@ -316,6 +331,10 @@ pub async fn db_handler(db: DiskDb, requestor: Receiver) { .await .unwrap(); } + Request::DeleteSession(id) => { + db.delete_session(&id).unwrap(); + tx.send(DatabaseResponse::DeleteSession).await.unwrap(); + } Request::Games => match db.games() { Ok(games) => tx.send(DatabaseResponse::Games(games)).await.unwrap(), _ => unimplemented!("errors for Request::Games"), diff --git a/visions/server/src/database/mod.rs b/visions/server/src/database/mod.rs index 293db5e..b325f0f 100644 --- a/visions/server/src/database/mod.rs +++ b/visions/server/src/database/mod.rs @@ -16,6 +16,7 @@ enum Request { CreateGame(UserId, String, String), CreateSession(UserId), CreateUser(String, String, bool, AccountState), + DeleteSession(SessionId), Game(GameId), Games, SaveGame(Game), @@ -36,6 +37,7 @@ struct DatabaseRequest { enum DatabaseResponse { Charsheet(Option), CreateSession(SessionId), + DeleteSession, Games(Vec), Game(Option), SaveGame(GameId), @@ -49,6 +51,7 @@ enum DatabaseResponse { pub trait Database: Send + Sync { async fn create_session(&self, id: &UserId) -> Result; async fn session(&self, id: &SessionId) -> Result, FatalError>; + async fn delete_session(&self, id: &SessionId) -> Result<(), FatalError>; async fn character(&self, id: &CharacterId) -> Result, FatalError>; @@ -125,6 +128,9 @@ impl Database for DbConn { async fn session(&self, id: &SessionId) -> Result, FatalError> { send_request!(self, Request::Session(id.to_owned()), DatabaseResponse::Session(row) => Ok(row)) } + async fn delete_session(&self, id: &SessionId) -> Result<(), FatalError> { + send_request!(self, Request::DeleteSession(id.to_owned()), DatabaseResponse::DeleteSession => Ok(())) + } async fn character(&self, id: &CharacterId) -> Result, FatalError> { send_request!(self, Request::Charsheet(id.to_owned()), DatabaseResponse::Charsheet(row) => Ok(row)) } diff --git a/visions/server/src/handlers/user_management.rs b/visions/server/src/handlers/user_management.rs index 0461538..0c89897 100644 --- a/visions/server/src/handlers/user_management.rs +++ b/visions/server/src/handlers/user_management.rs @@ -38,10 +38,7 @@ pub struct SetAdminPasswordRequest { pub password: String, } -async fn check_session( - core: &Core, - headers: HeaderMap, -) -> ResultExt, AppError, FatalError> { +fn parse_session_header(headers: HeaderMap) -> ResultExt, AppError, FatalError> { match headers.get("Authorization") { Some(token) => { println!("check_session: {:?}", token); @@ -52,7 +49,7 @@ async fn check_session( .collect::>() .as_slice() { - [_schema, token] => core.session(&SessionId::from(token.to_owned())).await, + [_schema, token] => ok(Some(SessionId::from(*token))), _ => error(AppError::BadRequest), } } @@ -60,6 +57,16 @@ async fn check_session( } } +async fn check_session( + core: &Core, + headers: HeaderMap, +) -> ResultExt, AppError, FatalError> { + match return_error!(parse_session_header(headers)) { + Some(session_id) => core.session(&session_id).await, + None => ok(None), + } +} + pub async fn auth_required( core: Core, headers: HeaderMap, @@ -101,7 +108,27 @@ pub async fn check_password( req: Json, ) -> ResultExt { let Json(AuthRequest { username, password }) = req; - core.auth(&username, &password).await + println!("check_password: {} {}", username, password); + let result = core.auth(&username, &password).await; + println!("auth result: {:?}", result); + return result; +} + +pub async fn delete_session(core: Core, headers: HeaderMap,) -> ResultExt<(), AppError, FatalError> { + /* + auth_required(core.clone(), headers, |user| async move { + match user_id { + Some(user_id) => core.delete_session + None => (), + } + }).await + */ + + match return_error!(parse_session_header(headers)) { + Some(session_id) => core.delete_session(&session_id).await, + None => error(AppError::AuthFailed), + } + // await core.delete_session(session_id); } pub async fn get_user( diff --git a/visions/server/src/routes.rs b/visions/server/src/routes.rs index 6d62583..1900ab2 100644 --- a/visions/server/src/routes.rs +++ b/visions/server/src/routes.rs @@ -13,8 +13,7 @@ use crate::{ core::Core, database::UserId, handlers::{ - check_password, create_game, create_user, get_user, get_users, healthcheck, set_password, - wrap_handler, AuthRequest, CreateGameRequest, CreateUserRequest, SetPasswordRequest, + check_password, create_game, create_user, delete_session, get_user, get_users, healthcheck, set_password, wrap_handler, AuthRequest, CreateGameRequest, CreateUserRequest, SetPasswordRequest }, }; @@ -38,10 +37,14 @@ pub fn routes(core: Core) -> Router { let core = core.clone(); move |req: Json| wrap_handler(|| check_password(core, req)) }) + .delete({ + let core = core.clone(); + move |headers: HeaderMap| wrap_handler(|| delete_session(core, headers)) + }) .layer( CorsLayer::new() - .allow_methods([Method::POST]) - .allow_headers([CONTENT_TYPE]) + .allow_methods([Method::DELETE, Method::POST]) + .allow_headers([AUTHORIZATION, CONTENT_TYPE]) .allow_origin(Any), ), ) @@ -93,7 +96,13 @@ pub fn routes(core: Core) -> Router { let Json(req) = req; wrap_handler(|| set_password(core, headers, req)) } - }), + }) + .layer( + CorsLayer::new() + .allow_methods([Method::PUT]) + .allow_headers([AUTHORIZATION, CONTENT_TYPE]) + .allow_origin(Any), + ), ) .route( "/api/v1/user/:user_id",