#![allow(async_fn_in_trait)] #![allow(unused)] use crate::Error; use somethings::{ DatabaseError, DatabaseHandle, auth::{AnonymousUser, HashedPassword, Session, SessionId, User, UserAuth, UserId}, params, }; pub trait AuthDatabase { async fn auth_create_tables(&self) -> Result<(), DatabaseError>; async fn insert_user(&self, id: &UserId, name: &str, hash: HashedPassword) -> Result<(), Error>; async fn insert_anonymous_user(&self, id: &UserId) -> Result<(), Error>; async fn user_exists(&self, id: &UserId) -> Result; async fn get_user_by_id(&self, id: &UserId) -> Result; async fn get_anonymous_user_by_id(&self, id: &UserId) -> Result; async fn get_userauth_by_name(&self, name: &str) -> Result; async fn insert_session(&self, id: &SessionId, uid: &UserId) -> Result<(), Error>; async fn get_session_by_id(&self, id: &SessionId) -> Result; async fn delete_session_by_id(&self, id: &SessionId) -> Result<(), Error>; } impl AuthDatabase for DatabaseHandle { async fn auth_create_tables(&self) -> Result<(), DatabaseError> { let lock = self.lock().await; lock.create_table("users", CREATE_TABLE_USERS)?; lock.create_table("sessions", CREATE_TABLE_SESSIONS) } async fn insert_user( &self, id: &UserId, name: &str, hash: HashedPassword, ) -> Result<(), Error> { self.lock().await.insert( "INSERT INTO users(id, name, hash) VALUES(?1, ?2, ?3)", params![id, name, hash.to_string()], )?; Ok(()) } async fn insert_anonymous_user(&self, id: &UserId) -> Result<(), Error> { self.lock() .await .insert("INSERT INTO users(id) VALUES(?1)", params![id])?; Ok(()) } async fn user_exists(&self, id: &UserId) -> Result { match get_dbuser_by_id(self, id).await { Ok(_) => Ok(true), Err(Error::DatabaseError(DatabaseError::NoRows)) => Ok(false), Err(e) => Err(e), } } async fn get_user_by_id(&self, id: &UserId) -> Result { get_dbuser_by_id(&self, id).await?.try_into() } async fn get_anonymous_user_by_id(&self, id: &UserId) -> Result { let dbuser = get_dbuser_by_id(&self, id).await?; match dbuser.name { Some(_) => Err(Error::UserIsRegistered), None => Ok(AnonymousUser::new(dbuser.id)), } } async fn get_userauth_by_name(&self, name: &str) -> Result { get_dbuser_by_name(&self, name).await?.try_into() } async fn insert_session(&self, id: &SessionId, uid: &UserId) -> Result<(), Error> { self.lock().await.insert( "INSERT INTO sessions(id, user_id) VALUES(?1, ?2)", params![id, uid], )?; Ok(()) } async fn get_session_by_id(&self, id: &SessionId) -> Result { let sql = "SELECT sessions.id, users.id, users.name \ FROM sessions \ INNER JOIN users ON sessions.user_id = users.id \ WHERE sessions.id = ?1"; self.lock() .await .query_one(sql, params![id], |row| { Ok(Session::new( row.get(0)?, User::new(row.get(1)?, row.get(2)?), )) }) .map_err(|dbe| Error::DatabaseError(dbe)) } async fn delete_session_by_id(&self, id: &SessionId) -> Result<(), Error> { self.lock() .await .execute("DELETE FROM sessions WHERE id = ?1", params![id])?; Ok(()) } } async fn get_dbuser_by_id(db: &DatabaseHandle, id: &UserId) -> Result { db.lock() .await .query_one( "SELECT id, name, hash FROM users WHERE id = ?1", params![id], |row| { Ok(DbUser { id: row.get(0)?, name: row.get(1)?, hash: row.get(2)?, }) }, ) .map_err(|e| Error::DatabaseError(e)) } async fn get_dbuser_by_name(db: &DatabaseHandle, name: &str) -> Result { db.lock() .await .query_one( "SELECT id, name, hash FROM users WHERE name = ?1", params![name], |row| { Ok(DbUser { id: row.get(0)?, name: row.get(1)?, hash: row.get(2)?, }) }, ) .map_err(|e| Error::DatabaseError(e)) } pub const CREATE_TABLE_USERS: &'static str = "\ CREATE TABLE IF NOT EXISTS users( id TEXT NOT NULL PRIMARY KEY, name TEXT, hash TEXT, created TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP );"; pub const CREATE_TABLE_SESSIONS: &'static str = "\ CREATE TABLE IF NOT EXISTS sessions( id TEXT NOT NULL PRIMARY KEY, user_id INTEGER NOT NULL, created TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) );"; struct DbUser { id: UserId, name: Option, hash: Option, } impl TryInto for DbUser { type Error = Error; fn try_into(self) -> Result { let DbUser { id, name, hash: _ } = self; match name { Some(name) => Ok(User::new(id, name)), None => Err(Error::UserIsAnonymous), } } } impl TryInto for DbUser { type Error = Error; fn try_into(self) -> Result { let DbUser { id, name, hash } = self; match (name, hash) { (Some(name), Some(hash)) => Ok(UserAuth::new( User::new(id, name), HashedPassword::new(hash), )), _ => Err(Error::UserIsAnonymous), } } }