use crate::error::Error; use crate::password_reset::PasswordResetToken; use crate::User; use database::sqlx::SqliteExecutor; use database::Error as DatabaseError; use database::Users as DatabaseUsers; use email_address::EmailAddress; use hash::{PaperKey, Password, Secret}; use id::{UserID, Username}; use std::str::FromStr; impl From for User { fn from(db: DatabaseUsers) -> Self { Self { id: UserID(db.id), created_at: db.created_at, updated_at: db.updated_at, is_admin: db.is_admin, username: Username(db.username), name: db.name, email: db.email, password: db.password, password_recover: db.password_recover, paper_key: db.paper_key, is_archived: db.is_archived, timezone: db.timezone, totp_secret: db.totp_secret, totp_backup: db.totp_backup, } } } impl User { pub async fn get_initial_admin(conn: impl SqliteExecutor<'_>) -> Result, Error> { Ok(DatabaseUsers::get_initial_admin(conn) .await? .map(Self::from)) } pub async fn insert( conn: impl SqliteExecutor<'_>, id: &UserID, is_admin: bool, username: &Username, password: Option<&Password>, ) -> Result<(), Error> { DatabaseUsers::insert( conn, &id.0, is_admin, username.as_ref(), password.map(|p| p.hash()), ) .await .map_err(|e| match e { DatabaseError::UniqueConstraintPrimaryKey => Error::IdNotAvailable(id.to_string()), DatabaseError::UniqueConstraint(column) => { if &column == "username" { Error::UsernameNotAvailable(username.into()) } else { Error::ColumnNotAvailable(column) } } _ => e.into(), })?; Ok(()) // DatabaseUsers::set_username(conn, self.id.as_ref(), username.as_ref()) // .await // .map_err(|e| match e { // DatabaseError::UniqueConstraint(column) => { // if &column == "username" { // Error::UsernameNotAvailable(username.into()) // } else { // Error::ColumnNotAvailable(column) // } // } // _ => e.into(), // })?; // // Ok(()) } pub async fn get_by_id( conn: impl SqliteExecutor<'_>, id: &UserID, ) -> Result, Error> { Ok(DatabaseUsers::get_one_by_id(conn, &id.0) .await? .map(Self::from)) } pub async fn get_by_email( conn: impl SqliteExecutor<'_>, email: &EmailAddress, ) -> Result, Error> { Ok(DatabaseUsers::get_one_by_email(conn, email.as_str()) .await? .map(Self::from)) } async fn get_by_username( conn: impl SqliteExecutor<'_>, username: &Username, ) -> Result, Error> { Ok(DatabaseUsers::get_one_by_username(conn, username.as_ref()) .await? .map(Self::from)) } /// Get by ID, Email, Username (in that order) pub async fn get_by_login( conn: impl SqliteExecutor<'_>, login: &str, ) -> Result, Error> { // Parse login as ID if let Ok(id) = UserID::from_str(login) { return Self::get_by_id(conn, &id).await; } // Parse login as email if let Ok(email) = EmailAddress::from_str(login) { return Self::get_by_email(conn, &email).await; } // Get user from username if let Ok(username) = Username::from_str(login) { return Self::get_by_username(conn, &username).await; } Err(Error::InvalidLogin(login.into())) } pub async fn get_one_from_authorization_code( conn: impl SqliteExecutor<'_>, code: &str, ) -> Result, Error> { Ok(DatabaseUsers::get_one_from_authorization_code(conn, code) .await? .map(Self::from)) } pub async fn get_one_from_refresh_token( conn: impl SqliteExecutor<'_>, token: &str, ) -> Result, Error> { Ok(DatabaseUsers::get_one_from_refresh_token(conn, token) .await? .map(Self::from)) } pub async fn get_one_from_password_reset_token( conn: impl SqliteExecutor<'_>, token: &PasswordResetToken, ) -> Result, Error> { Ok( DatabaseUsers::get_one_from_password_reset_token(conn, token.to_string().as_str()) .await? .map(Self::from), ) } pub async fn get_all(conn: impl SqliteExecutor<'_>) -> Result, Error> { Ok(DatabaseUsers::get_all(conn) .await? .into_iter() .map(Self::from) .collect::>()) } pub async fn set_username( &self, conn: impl SqliteExecutor<'_>, username: &Username, ) -> Result<(), Error> { DatabaseUsers::set_username(conn, self.id.as_ref(), username.as_ref()) .await .map_err(|e| match e { DatabaseError::UniqueConstraint(column) => { if &column == "username" { Error::UsernameNotAvailable(username.into()) } else { Error::ColumnNotAvailable(column) } } _ => e.into(), })?; Ok(()) } pub async fn set_name(&self, conn: impl SqliteExecutor<'_>, name: &str) -> Result<(), Error> { DatabaseUsers::set_name(conn, self.id.as_ref(), name).await?; Ok(()) } pub async fn set_email( &self, conn: impl SqliteExecutor<'_>, email: EmailAddress, ) -> Result<(), Error> { let email = email.as_str(); DatabaseUsers::set_email(conn, self.id.as_ref(), email) .await .map_err(|e| match e { DatabaseError::UniqueConstraint(column) => { if &column == "email" { Error::EmailNotAvailable(email.into()) } else { Error::ColumnNotAvailable(column) } } _ => e.into(), })?; Ok(()) } pub async fn set_paper_key( &self, conn: impl SqliteExecutor<'_>, paper_key: Option<&PaperKey>, ) -> Result<(), Error> { let paper_key = paper_key.map(|paper_key| paper_key.hash()); DatabaseUsers::set_paper_key(conn, self.id.as_ref(), paper_key).await?; Ok(()) } pub async fn set_password( &self, conn: impl SqliteExecutor<'_>, password: Option<&Password>, ) -> Result<(), Error> { let password = password.map(|password| password.hash()); DatabaseUsers::set_password(conn, self.id.as_ref(), password).await?; Ok(()) } pub async fn set_timezone( &self, conn: impl SqliteExecutor<'_>, timezone: &str, ) -> Result<(), Error> { DatabaseUsers::set_timezone(conn, self.id.as_ref(), timezone).await?; Ok(()) } pub async fn set_password_reset_token( &self, conn: impl SqliteExecutor<'_>, token: Option<&PasswordResetToken>, ) -> Result<(), Error> { DatabaseUsers::set_password_reset_token( conn, self.id.as_ref(), token.map(|t| t.to_string()).as_deref(), ) .await?; Ok(()) } pub async fn set_totp_secret( &self, conn: impl SqliteExecutor<'_>, secret: Option<&[u8]>, ) -> Result<(), Error> { DatabaseUsers::set_totp_secret(conn, self.id.as_ref(), secret).await?; Ok(()) } pub async fn set_totp_backup( &self, conn: impl SqliteExecutor<'_>, backup: Option<&Secret>, ) -> Result<(), Error> { let backup = backup.map(|backup| backup.hash()); DatabaseUsers::set_totp_backup(conn, self.id.as_ref(), backup).await?; Ok(()) } pub async fn set_archive_status( &self, conn: impl SqliteExecutor<'_>, value: bool, ) -> Result<(), Error> { DatabaseUsers::set_archive_status(conn, self.id.as_ref(), value).await?; Ok(()) } pub async fn set_admin_status( &self, conn: impl SqliteExecutor<'_>, value: bool, ) -> Result<(), Error> { DatabaseUsers::set_admin_status(conn, self.id.as_ref(), value).await?; Ok(()) } }