From e19d97663d411d8d7d4c0db1427f2fc13314f1f6 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sat, 11 Jan 2025 11:43:32 -0500 Subject: [PATCH] Change the data types of the internal interfaces, and switch password expiration to an AccountState --- visions/server/src/core.rs | 66 ++++--- visions/server/src/database/disk_db.rs | 164 +++++++--------- visions/server/src/database/mod.rs | 183 ++++++++---------- visions/server/src/database/types.rs | 12 +- .../server/src/handlers/user_management.rs | 28 +-- visions/server/src/routes.rs | 6 +- visions/server/src/types.rs | 60 ++++-- 7 files changed, 264 insertions(+), 255 deletions(-) diff --git a/visions/server/src/core.rs b/visions/server/src/core.rs index ae0cb2e..64e3c4a 100644 --- a/visions/server/src/core.rs +++ b/visions/server/src/core.rs @@ -13,6 +13,10 @@ use crate::{ asset_db::{self, AssetId, Assets}, database::{CharacterId, Database, GameId, SessionId, UserId}, types::{AppError, FatalError, GameOverview, Message, Rgb, Tabletop, User, UserOverview}, + types::{ + AccountState, AppError, FatalError, GameOverview, Message, Rgb, Tabletop, User, + UserOverview, + }, }; const DEFAULT_BACKGROUND_COLOR: Rgb = Rgb { @@ -171,7 +175,7 @@ impl Core { Some(_) => error(AppError::UsernameUnavailable), None => match state .db - .save_user(None, username, "", false, true, Utc::now()) + .create_user(username, "", false, AccountState::PasswordReset(Utc::now())) .await { Ok(user_id) => ok(user_id), @@ -180,7 +184,7 @@ impl Core { } } - pub async fn disable_user(&self, userid: UserId) -> ResultExt<(), AppError, FatalError> { + pub async fn disable_user(&self, _userid: UserId) -> ResultExt<(), AppError, FatalError> { unimplemented!(); } @@ -188,7 +192,14 @@ impl Core { let games = self.0.read().await.db.games().await; match games { // Ok(games) => ok(games.into_iter().map(|g| Game::from(g)).collect()), - Ok(games) => ok(games.into_iter().map(GameOverview::from).collect()), + Ok(games) => ok(games.into_iter().map(|game| + GameOverview{ + id: game.id, + game_type: "".to_owned(), + game_name: game.name, + gm: game.gm, + players: game.players, + }).collect::>()), Err(err) => fatal(err), } } @@ -200,7 +211,7 @@ impl Core { game_name: &str, ) -> ResultExt { let state = self.0.read().await; - match state.db.save_game(None, gm, game_type, game_name).await { + match state.db.create_game(gm, game_type, game_name).await { Ok(game_id) => ok(game_id), Err(err) => fatal(err), } @@ -283,16 +294,22 @@ impl Core { pub async fn save_user( &self, - uuid: Option, - username: &str, + id: UserId, + name: &str, password: &str, admin: bool, - enabled: bool, + account_state: AccountState, ) -> ResultExt { let state = self.0.read().await; match state .db - .save_user(uuid, username, password, admin, enabled, Utc::now()) + .save_user(User { + id, + name: name.to_owned(), + password: password.to_owned(), + admin, + state: account_state, + }) .await { Ok(uuid) => ok(uuid), @@ -313,14 +330,7 @@ impl Core { }; match state .db - .save_user( - Some(uuid), - &user.name, - &password, - user.admin, - user.enabled, - Utc::now(), - ) + .save_user(User{ password, state: AccountState::Normal, ..user }) .await { Ok(_) => ok(()), @@ -330,9 +340,10 @@ impl Core { pub async fn auth( &self, - username: &str, - password: &str, + _username: &str, + _password: &str, ) -> ResultExt { + /* let now = Utc::now(); let state = self.0.read().await; match state.db.user_by_username(username).await { @@ -355,6 +366,8 @@ impl Core { Ok(_) => error(AppError::AuthFailed), Err(err) => fatal(err), } + */ + unimplemented!(); } pub async fn session( @@ -414,19 +427,22 @@ mod test { ]); let memory_db: Option = None; let conn = DbConn::new(memory_db); - conn.save_user( - Some(UserId::from("admin")), + conn.create_user( "admin", "aoeu", true, - true, - Utc::now(), + AccountState::PasswordReset(Utc::now()), + ) + .await + .unwrap(); + conn.create_user( + "gm_1", + "aoeu", + false, + AccountState::PasswordReset(Utc::now()), ) .await .unwrap(); - conn.save_user(None, "gm_1", "aoeu", false, true, Utc::now()) - .await - .unwrap(); Core::new(assets, conn) } diff --git a/visions/server/src/database/disk_db.rs b/visions/server/src/database/disk_db.rs index 670ed82..d81c2b0 100644 --- a/visions/server/src/database/disk_db.rs +++ b/visions/server/src/database/disk_db.rs @@ -1,7 +1,6 @@ use std::path::Path; use async_std::channel::Receiver; -use chrono::Utc; use include_dir::{include_dir, Dir}; use lazy_static::lazy_static; use rusqlite::Connection; @@ -9,13 +8,10 @@ use rusqlite_migration::Migrations; use crate::{ database::{DatabaseResponse, Request}, - types::FatalError, + types::{AccountState, FatalError, Game, User}, }; -use super::{ - types::{DateTime, GameId}, - CharacterId, CharsheetRow, DatabaseRequest, GameRow, SessionId, UserId, UserRow, -}; +use super::{types::GameId, CharacterId, CharsheetRow, DatabaseRequest, SessionId, UserId}; static MIGRATIONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/migrations"); @@ -47,24 +43,23 @@ impl DiskDb { Ok(DiskDb { conn }) } - pub fn user(&self, id: &UserId) -> Result, FatalError> { + pub fn user(&self, id: &UserId) -> Result, FatalError> { let mut stmt = self .conn .prepare("SELECT * FROM users WHERE uuid=?") .map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?; - let items: Vec = stmt + let items: Vec = stmt .query_map([id.as_str()], |row| { - Ok(UserRow { + Ok(User { id: row.get(0).unwrap(), name: row.get(1).unwrap(), password: row.get(2).unwrap(), admin: row.get(3).unwrap(), - enabled: row.get(4).unwrap(), - password_expires: row.get(5).unwrap(), + state: row.get(4).unwrap(), }) }) .unwrap() - .collect::, rusqlite::Error>>() + .collect::, rusqlite::Error>>() .unwrap(); match &items[..] { [] => Ok(None), @@ -73,24 +68,23 @@ impl DiskDb { } } - pub fn user_by_username(&self, username: &str) -> Result, FatalError> { + pub fn user_by_username(&self, username: &str) -> Result, FatalError> { let mut stmt = self .conn .prepare("SELECT * FROM users WHERE name=?") .map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?; - let items: Vec = stmt + let items: Vec = stmt .query_map([username], |row| { - Ok(UserRow { + Ok(User { id: row.get(0).unwrap(), name: row.get(1).unwrap(), password: row.get(2).unwrap(), admin: row.get(3).unwrap(), - enabled: row.get(4).unwrap(), - password_expires: row.get(5).unwrap(), + state: row.get(4).unwrap(), }) }) .unwrap() - .collect::, rusqlite::Error>>() + .collect::, rusqlite::Error>>() .unwrap(); match &items[..] { [] => Ok(None), @@ -99,81 +93,72 @@ impl DiskDb { } } - pub fn save_user( + pub fn create_user( &self, - user_id: Option, name: &str, password: &str, admin: bool, - enabled: bool, - expiration: chrono::DateTime, + state: AccountState, ) -> Result { - match user_id { - None => { - let user_id = UserId::default(); - let mut stmt = self - .conn - .prepare("INSERT INTO users VALUES (?, ?, ?, ?, ?, ?)") - .map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?; - stmt.execute(( - user_id.as_str(), - name, - password, - admin, - enabled, - format!("{}", expiration.format("%Y-%m-%d %H:%M:%S")), - )) - .unwrap(); - Ok(user_id) - } - Some(user_id) => { - let mut stmt = self - .conn - .prepare("UPDATE users SET name=?, password=?, admin=?, enabled=?, password_expires=? WHERE uuid=?") - .map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?; - stmt.execute(( - name, - password, - admin, - enabled, - format!("{}", expiration.format("%Y-%m-%d %H:%M:%S")), - user_id.as_str(), - )) - .unwrap(); - Ok(user_id) - } - } + let user_id = UserId::default(); + let mut stmt = self + .conn + .prepare("INSERT INTO users VALUES (?, ?, ?, ?, ?)") + .map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?; + stmt.execute((user_id.as_str(), name, password, admin, state)) + .unwrap(); + Ok(user_id) } - pub fn users(&self) -> Result, FatalError> { + pub fn save_user(&self, user: User) -> Result { + let mut stmt = self + .conn + .prepare("UPDATE users SET name=?, password=?, admin=?, state=? WHERE uuid=?") + .map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?; + stmt.execute(( + user.name, + user.password, + user.admin, + user.state, + // format!("{}", expiration.format("%Y-%m-%d %H:%M:%S")), + user.id.as_str(), + )) + .unwrap(); + Ok(user.id) + } + + pub fn users(&self) -> Result, FatalError> { let mut stmt = self .conn .prepare("SELECT * FROM users") .map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?; let items = stmt .query_map([], |row| { - Ok(UserRow { + Ok(User { id: row.get(0).unwrap(), name: row.get(1).unwrap(), password: row.get(2).unwrap(), admin: row.get(3).unwrap(), - enabled: row.get(4).unwrap(), - password_expires: row.get(5).unwrap(), + state: row.get(4).unwrap(), }) }) .unwrap() - .collect::, rusqlite::Error>>() + .collect::, rusqlite::Error>>() .unwrap(); Ok(items) } - pub fn save_game( + pub fn create_game( &self, - game_id: Option, - gm: &UserId, - game_type: &str, - name: &str, + _gm: &UserId, + _game_type: &str, + _name: &str, ) -> Result { + unimplemented!(); + } + + pub fn save_game(&self, _game: Game) -> Result<(), FatalError> { + /* match game_id { None => { let game_id = GameId::new(); @@ -195,46 +180,47 @@ impl DiskDb { Ok(game_id) } } + */ + unimplemented!() } - pub fn games(&self) -> Result, FatalError> { + pub fn games(&self) -> Result, FatalError> { let mut stmt = self .conn .prepare("SELECT * FROM games") .map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?; let items = stmt .query_map([], |row| { - Ok(GameRow { + Ok(Game { id: row.get(0).unwrap(), gm: row.get(1).unwrap(), - game_type: row.get(2).unwrap(), name: row.get(3).unwrap(), + players: vec![], }) }) .unwrap() - .collect::, rusqlite::Error>>() + .collect::, rusqlite::Error>>() .unwrap(); Ok(items) } - pub fn session(&self, session_id: &SessionId) -> Result, FatalError> { + pub fn session(&self, session_id: &SessionId) -> Result, FatalError> { let mut stmt = self.conn .prepare("SELECT u.uuid, u.name, u.password, u.admin, u.enabled FROM sessions s INNER JOIN users u ON u.uuid = s.user_id WHERE s.id = ?") .map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?; - let items: Vec = stmt + let items: Vec = stmt .query_map([session_id.as_str()], |row| { - Ok(UserRow { + Ok(User { id: row.get(0).unwrap(), name: row.get(1).unwrap(), password: row.get(2).unwrap(), admin: row.get(3).unwrap(), - enabled: row.get(4).unwrap(), - password_expires: row.get(5).unwrap(), + state: row.get(4).unwrap(), }) }) .unwrap() - .collect::, rusqlite::Error>>() + .collect::, rusqlite::Error>>() .unwrap(); match &items[..] { [] => Ok(None), @@ -331,6 +317,8 @@ pub async fn db_handler(db: DiskDb, requestor: Receiver) { _ => unimplemented!("errors for Charsheet"), } } + Request::CreateUser(_, _, _, _) => {} + Request::CreateGame(_, _, _) => {} Request::CreateSession(id) => { let session_id = db.create_session(&id).unwrap(); tx.send(DatabaseResponse::CreateSession(session_id)) @@ -344,11 +332,12 @@ pub async fn db_handler(db: DiskDb, requestor: Receiver) { Request::Game(_game_id) => { unimplemented!("Request::Game handler"); } - Request::SaveGame(game_id, user_id, game_type, game_name) => { - let game_id = db.save_game(game_id, &user_id, &game_type, &game_name); - match game_id { - Ok(game_id) => { - tx.send(DatabaseResponse::SaveGame(game_id)).await.unwrap(); + Request::SaveGame(game) => { + let id = game.id.clone(); + let save_result = db.save_game(game); + match save_result { + Ok(_) => { + tx.send(DatabaseResponse::SaveGame(id)).await.unwrap(); } err => panic!("{:?}", err), } @@ -369,15 +358,8 @@ pub async fn db_handler(db: DiskDb, requestor: Receiver) { err => panic!("{:?}", err), } } - Request::SaveUser(user_id, username, password, admin, enabled, expiration) => { - let user_id = db.save_user( - user_id, - username.as_ref(), - password.as_ref(), - admin, - enabled, - expiration, - ); + Request::SaveUser(user) => { + let user_id = db.save_user(user); match user_id { Ok(user_id) => { tx.send(DatabaseResponse::SaveUser(user_id)).await.unwrap(); diff --git a/visions/server/src/database/mod.rs b/visions/server/src/database/mod.rs index 84133e2..cfc5d2c 100644 --- a/visions/server/src/database/mod.rs +++ b/visions/server/src/database/mod.rs @@ -5,20 +5,21 @@ use std::path::Path; use async_std::channel::{bounded, Sender}; use async_trait::async_trait; -use chrono::{DateTime, Utc}; use disk_db::{db_handler, DiskDb}; -pub use types::{CharacterId, CharsheetRow, GameId, GameRow, SessionId, UserId, UserRow}; +pub use types::{CharacterId, CharsheetRow, GameId, SessionId, UserId}; -use crate::types::FatalError; +use crate::types::{AccountState, FatalError, Game, User}; #[derive(Debug)] enum Request { Charsheet(CharacterId), + CreateGame(UserId, String, String), CreateSession(UserId), - Games, + CreateUser(String, String, bool, AccountState), Game(GameId), - SaveGame(Option, UserId, String, String), - SaveUser(Option, String, String, bool, bool, DateTime), + Games, + SaveGame(Game), + SaveUser(User), Session(SessionId), User(UserId), UserByUsername(String), @@ -35,50 +36,43 @@ struct DatabaseRequest { enum DatabaseResponse { Charsheet(Option), CreateSession(SessionId), - Games(Vec), - Game(Option), + Games(Vec), + Game(Option), SaveGame(GameId), SaveUser(UserId), - Session(Option), - User(Option), - Users(Vec), + Session(Option), + User(Option), + Users(Vec), } #[async_trait] pub trait Database: Send + Sync { - async fn users(&self) -> Result, FatalError>; - - async fn user(&self, _: &UserId) -> Result, FatalError>; - - async fn user_by_username(&self, _: &str) -> Result, FatalError>; - - async fn save_user( - &self, - user_id: Option, - name: &str, - password: &str, - admin: bool, - enabled: bool, - expiration: DateTime, - ) -> Result; - - async fn games(&self) -> Result, FatalError>; - - async fn game(&self, _: &GameId) -> Result, FatalError>; - - async fn save_game( - &self, - game_id: Option, - gm: &UserId, - game_type: &str, - game_name: &str, - ) -> Result; + async fn create_session(&self, id: &UserId) -> Result; + async fn session(&self, id: &SessionId) -> Result, FatalError>; async fn character(&self, id: &CharacterId) -> Result, FatalError>; - async fn session(&self, id: &SessionId) -> Result, FatalError>; + async fn create_game( + &self, + gm: &UserId, + game_type: &str, + name: &str, + ) -> Result; + async fn save_game(&self, game: Game) -> Result; + async fn game(&self, _: &GameId) -> Result, FatalError>; + async fn games(&self) -> Result, FatalError>; - async fn create_session(&self, id: &UserId) -> Result; + async fn create_user( + &self, + name: &str, + password: &str, + admin: bool, + state: AccountState, + ) -> Result; + async fn save_user(&self, user: User) -> Result; + async fn user(&self, _: &UserId) -> Result, FatalError>; + async fn user_by_username(&self, _: &str) -> Result, FatalError>; + async fn users(&self) -> Result, FatalError>; } pub struct DbConn { @@ -125,67 +119,58 @@ macro_rules! send_request { #[async_trait] impl Database for DbConn { - async fn users(&self) -> Result, FatalError> { - send_request!(self, Request::Users, DatabaseResponse::Users(lst) => Ok(lst)) + async fn create_session(&self, id: &UserId) -> Result { + send_request!(self, Request::CreateSession(id.to_owned()), DatabaseResponse::CreateSession(session_id) => Ok(session_id)) } - - async fn user(&self, uid: &UserId) -> Result, FatalError> { - send_request!(self, Request::User(uid.clone()), DatabaseResponse::User(user) => Ok(user)) + async fn session(&self, id: &SessionId) -> Result, FatalError> { + send_request!(self, Request::Session(id.to_owned()), DatabaseResponse::Session(row) => Ok(row)) } - - async fn user_by_username(&self, username: &str) -> Result, FatalError> { - send_request!(self, Request::UserByUsername(username.to_owned()), DatabaseResponse::User(user) => Ok(user)) - } - - async fn save_user( - &self, - user_id: Option, - name: &str, - password: &str, - admin: bool, - enabled: bool, - expiration: DateTime, - ) -> Result { - send_request!(self, - Request::SaveUser( - user_id, - name.to_owned(), - password.to_owned(), - admin, - enabled, - expiration, - ), - DatabaseResponse::SaveUser(user_id) => Ok(user_id)) - } - - async fn games(&self) -> Result, FatalError> { - send_request!(self, Request::Games, DatabaseResponse::Games(lst) => Ok(lst)) - } - - async fn game(&self, game_id: &GameId) -> Result, FatalError> { - send_request!(self, Request::Game(game_id.clone()), DatabaseResponse::Game(game) => Ok(game)) - } - - async fn save_game( - &self, - game_id: Option, - user_id: &UserId, - game_type: &str, - game_name: &str, - ) -> Result { - send_request!(self, Request::SaveGame(game_id, user_id.to_owned(), game_type.to_owned(), game_name.to_owned()), DatabaseResponse::SaveGame(game_id) => Ok(game_id)) - } - async fn character(&self, id: &CharacterId) -> Result, FatalError> { send_request!(self, Request::Charsheet(id.to_owned()), DatabaseResponse::Charsheet(row) => Ok(row)) } - async fn session(&self, id: &SessionId) -> Result, FatalError> { - send_request!(self, Request::Session(id.to_owned()), DatabaseResponse::Session(row) => Ok(row)) + async fn create_game( + &self, + user_id: &UserId, + game_type: &str, + game_name: &str, + ) -> Result { + send_request!(self, Request::CreateGame(user_id.to_owned(), game_type.to_owned(), game_name.to_owned()), DatabaseResponse::SaveGame(game_id) => Ok(game_id)) + } + async fn save_game(&self, game: Game) -> Result { + send_request!(self, Request::SaveGame(game), DatabaseResponse::SaveGame(game_id) => Ok(game_id)) + } + async fn game(&self, game_id: &GameId) -> Result, FatalError> { + send_request!(self, Request::Game(game_id.clone()), DatabaseResponse::Game(game) => Ok(game)) + } + async fn games(&self) -> Result, FatalError> { + send_request!(self, Request::Games, DatabaseResponse::Games(lst) => Ok(lst)) } - async fn create_session(&self, id: &UserId) -> Result { - send_request!(self, Request::CreateSession(id.to_owned()), DatabaseResponse::CreateSession(session_id) => Ok(session_id)) + async fn create_user( + &self, + name: &str, + password: &str, + admin: bool, + state: AccountState, + ) -> Result { + send_request!(self, + Request::CreateUser(name.to_owned(), password.to_owned(), admin, state), + DatabaseResponse::SaveUser(user_id) => Ok(user_id)) + } + async fn save_user(&self, user: User) -> Result { + send_request!(self, + Request::SaveUser(user), + DatabaseResponse::SaveUser(user_id) => Ok(user_id)) + } + async fn user(&self, uid: &UserId) -> Result, FatalError> { + send_request!(self, Request::User(uid.clone()), DatabaseResponse::User(user) => Ok(user)) + } + async fn user_by_username(&self, username: &str) -> Result, FatalError> { + send_request!(self, Request::UserByUsername(username.to_owned()), DatabaseResponse::User(user) => Ok(user)) + } + async fn users(&self) -> Result, FatalError> { + send_request!(self, Request::Users, DatabaseResponse::Users(lst) => Ok(lst)) } } @@ -205,11 +190,15 @@ mod test { let no_path: Option = None; let db = DiskDb::new(no_path).unwrap(); - let now = Utc::now(); - - db.save_user(Some(UserId::from("admin")), "admin", "abcdefg", true, true, now) + db.create_user("admin", "abcdefg", true, AccountState::Normal) + .unwrap(); + let game_id = db + .create_game( + &UserId::from("admin"), + "Candela", + "Circle of the Winter Solstice", + ) .unwrap(); - let game_id = db.save_game(None, &UserId::from("admin"), "Candela", "Circle of the Winter Solstice").unwrap(); (db, game_id) } diff --git a/visions/server/src/database/types.rs b/visions/server/src/database/types.rs index 680ee87..cc8b955 100644 --- a/visions/server/src/database/types.rs +++ b/visions/server/src/database/types.rs @@ -160,16 +160,6 @@ impl FromSql for CharacterId { } } -#[derive(Clone, Debug)] -pub struct UserRow { - pub id: UserId, - pub name: String, - pub password: String, - pub admin: bool, - pub enabled: bool, - pub password_expires: DateTime, -} - #[derive(Clone, Debug)] pub struct Role { userid: UserId, @@ -177,6 +167,7 @@ pub struct Role { role: String, } +/* #[derive(Clone, Debug)] pub struct GameRow { pub id: GameId, @@ -184,6 +175,7 @@ pub struct GameRow { pub game_type: String, pub name: String, } +*/ #[derive(Clone, Debug)] pub struct CharsheetRow { diff --git a/visions/server/src/handlers/user_management.rs b/visions/server/src/handlers/user_management.rs index d211053..a7c02b8 100644 --- a/visions/server/src/handlers/user_management.rs +++ b/visions/server/src/handlers/user_management.rs @@ -1,9 +1,6 @@ -use axum::{ - http::HeaderMap, - Json, -}; +use axum::{http::HeaderMap, Json}; use futures::Future; -use result_extended::{error, fatal, ok, return_error, ResultExt}; +use result_extended::{error, ok, return_error, ResultExt}; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -97,12 +94,15 @@ where } pub async fn check_password( - core: Core, + _core: Core, req: Json, ) -> ResultExt { - let Json(AuthRequest { username, password }) = req; + let Json(AuthRequest { + username: _, + password: _, + }) = req; unimplemented!() - /* + /* match core.auth(&username, &password).await { } */ @@ -118,7 +118,8 @@ pub async fn get_user( Some(user_id) => core.user(user_id).await, None => core.user(user.id).await, } - }).await + }) + .await } pub async fn get_users( @@ -127,7 +128,8 @@ pub async fn get_users( ) -> ResultExt, AppError, FatalError> { auth_required(core.clone(), headers, |_user| async move { core.list_users().await - }).await + }) + .await } pub async fn create_user( @@ -137,7 +139,8 @@ pub async fn create_user( ) -> ResultExt { admin_required(core.clone(), headers, |_admin| async { core.create_user(&req.username).await - }).await + }) + .await } pub async fn set_password( @@ -151,5 +154,6 @@ pub async fn set_password( } else { error(AppError::BadRequest) } - }).await + }) + .await } diff --git a/visions/server/src/routes.rs b/visions/server/src/routes.rs index 9c2a231..139e9c3 100644 --- a/visions/server/src/routes.rs +++ b/visions/server/src/routes.rs @@ -128,7 +128,7 @@ mod test { use crate::{ asset_db::FsAssets, core::{AuthResponse, Core}, - database::{Database, DbConn, GameId, SessionId, UserId}, + database::{DbConn, GameId, SessionId, UserId}, handlers::CreateGameRequest, types::UserOverview, }; @@ -240,13 +240,13 @@ mod test { #[tokio::test] async fn it_refuses_to_authenticate_a_disabled_user() { - let (_core, server) = setup_with_disabled_user().await; + let (_core, _server) = setup_with_disabled_user().await; unimplemented!() } #[tokio::test] async fn it_forces_changing_expired_password() { - let (_core, server) = setup_with_user().await; + let (_core, _server) = setup_with_user().await; unimplemented!() } diff --git a/visions/server/src/types.rs b/visions/server/src/types.rs index afe183e..d423196 100644 --- a/visions/server/src/types.rs +++ b/visions/server/src/types.rs @@ -1,4 +1,5 @@ use chrono::{DateTime, Utc}; +use rusqlite::{types::{FromSql, FromSqlError, ToSqlOutput, ValueRef}, ToSql}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -6,7 +7,7 @@ use typeshare::typeshare; use crate::{ asset_db::AssetId, - database::{GameId, GameRow, UserId, UserRow}, + database::{GameId, UserId}, }; #[derive(Debug, Error)] @@ -71,6 +72,43 @@ pub struct Rgb { pub blue: u32, } +#[derive(Clone, Debug, Deserialize, Serialize)] +#[typeshare] +pub enum AccountState { + Normal, + PasswordReset(DateTime), + Locked, +} + +impl FromSql for AccountState { + fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult { + if let ValueRef::Text(text) = value { + let text = String::from_utf8(text.to_vec()).unwrap(); + if text.starts_with("Normal") { + Ok(AccountState::Normal) + } else if text.starts_with("PasswordReset") { + unimplemented!() + } else if text.starts_with("Locked") { + Ok(AccountState::Locked) + } else { + Err(FromSqlError::InvalidType) + } + } else { + Err(FromSqlError::InvalidType) + } + } +} + +impl ToSql for AccountState { + fn to_sql(&self) -> rusqlite::Result> { + match self { + AccountState::Normal => Ok(ToSqlOutput::Borrowed(ValueRef::Text("Normal".as_bytes()))), + AccountState::PasswordReset(_expiration) => unimplemented!(), + AccountState::Locked => Ok(ToSqlOutput::Borrowed(ValueRef::Text("Locked".as_bytes()))), + } + } +} + #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] #[typeshare] @@ -79,21 +117,7 @@ pub struct User { pub name: String, pub password: String, pub admin: bool, - pub enabled: bool, - pub expiration: DateTime, -} - -impl From for User { - fn from(row: UserRow) -> Self { - Self { - id: row.id, - name: row.name.to_owned(), - password: row.password.to_owned(), - admin: row.admin, - enabled: row.enabled, - expiration: row.password_expires.0, - } - } + pub state: AccountState, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -115,7 +139,7 @@ pub struct Player { #[serde(rename_all = "camelCase")] #[typeshare] pub struct Game { - pub id: String, + pub id: GameId, pub name: String, pub gm: UserId, pub players: Vec, @@ -155,6 +179,7 @@ pub struct GameOverview { pub players: Vec, } +/* impl From for GameOverview { fn from(row: GameRow) -> Self { Self { @@ -166,3 +191,4 @@ impl From for GameOverview { } } } +*/