From 6db47cbbbc705c9b81aa0769c4c30becae4fc575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ma=C4=87kowski?= Date: Thu, 21 May 2026 15:48:13 +0200 Subject: [PATCH 1/3] feat: add `set_username` and `set_password` to `DatabaseUser` --- cot/src/auth/db.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/cot/src/auth/db.rs b/cot/src/auth/db.rs index 8eb6190f9..f90a7fd16 100644 --- a/cot/src/auth/db.rs +++ b/cot/src/auth/db.rs @@ -113,6 +113,44 @@ impl DatabaseUser { Ok(user) } + /// Sets the username of the user. + /// + /// # Example + /// + /// ``` + /// use cot::auth::db::DatabaseUser; + /// use cot::db::LimitedString; + /// # use cot::common_types::Password; + /// # use cot::db::Auto; + /// + /// # let mut user = DatabaseUser::new(Auto::fixed(1), LimitedString::new("oldname").unwrap(), &Password::new("password")); + /// user.set_username(LimitedString::new("newname").unwrap()); + /// + /// use cot::auth::User; + /// assert_eq!(User::username(&user).unwrap(), "newname"); + /// ``` + pub fn set_username(&mut self, username: LimitedString) { + self.username = username; + } + + /// Sets the password of the user. + /// + /// The password will be automatically hashed. + /// + /// # Example + /// + /// ``` + /// use cot::auth::db::DatabaseUser; + /// use cot::common_types::Password; + /// # use cot::db::{Auto, LimitedString}; + /// + /// # let mut user = DatabaseUser::new(Auto::fixed(1), LimitedString::new("user").unwrap(), &Password::new("oldpassword")); + /// user.set_password(&Password::new("newpassword")); + /// ``` + pub fn set_password(&mut self, password: &Password) { + self.password = PasswordHash::from_password(password); + } + /// Retrieves a user by their integer ID. It returns [`None`] if the user /// does not exist. /// From ae1e5337b84ffb9b98edb48fa0d5a9e0275ede50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ma=C4=87kowski?= Date: Thu, 21 May 2026 15:58:09 +0200 Subject: [PATCH 2/3] better test --- cot/src/auth/db.rs | 69 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/cot/src/auth/db.rs b/cot/src/auth/db.rs index f90a7fd16..b0491f796 100644 --- a/cot/src/auth/db.rs +++ b/cot/src/auth/db.rs @@ -115,19 +115,38 @@ impl DatabaseUser { /// Sets the username of the user. /// + /// Remember to call [`save()`][cot::db::Model::save] after calling this + /// function - otherwise, the username will never be saved to the database. + /// /// # Example /// /// ``` - /// use cot::auth::db::DatabaseUser; - /// use cot::db::LimitedString; - /// # use cot::common_types::Password; - /// # use cot::db::Auto; + /// use cot::auth::db::{DatabaseUser, DatabaseUserCredentials}; + /// use cot::common_types::Password; + /// use cot::db::{Database, LimitedString, Model}; + /// use cot::html::Html; + /// + /// async fn view(db: Database) -> cot::Result { + /// let mut user = + /// DatabaseUser::create_user(&db, "testuser".to_string(), &Password::new("password123")) + /// .await?; + /// user.set_username(LimitedString::new("new_username").unwrap()); + /// user.save(&db).await?; /// - /// # let mut user = DatabaseUser::new(Auto::fixed(1), LimitedString::new("oldname").unwrap(), &Password::new("password")); - /// user.set_username(LimitedString::new("newname").unwrap()); + /// assert_eq!(user.username(), "new_username"); + /// + /// Ok(Html::new("Username changed!")) + /// } /// - /// use cot::auth::User; - /// assert_eq!(User::username(&user).unwrap(), "newname"); + /// # #[tokio::main] + /// # async fn main() -> cot::Result<()> { + /// # use cot::test::{TestDatabase, TestRequestBuilder}; + /// # let mut test_database = TestDatabase::new_sqlite().await?; + /// # test_database.with_auth().run_migrations().await; + /// # view(test_database.database()).await?; + /// # test_database.cleanup().await?; + /// # Ok(()) + /// # } /// ``` pub fn set_username(&mut self, username: LimitedString) { self.username = username; @@ -135,17 +154,43 @@ impl DatabaseUser { /// Sets the password of the user. /// + /// Remember to call [`save()`][cot::db::Model::save] after calling this + /// function - otherwise, the password will never be saved to the database. + /// /// The password will be automatically hashed. /// /// # Example /// /// ``` - /// use cot::auth::db::DatabaseUser; + /// use cot::auth::db::{DatabaseUser, DatabaseUserCredentials}; /// use cot::common_types::Password; - /// # use cot::db::{Auto, LimitedString}; + /// use cot::db::{Database, Model}; + /// use cot::html::Html; + /// + /// async fn view(db: Database) -> cot::Result { + /// let mut user = + /// DatabaseUser::create_user(&db, "testuser".to_string(), &Password::new("password123")) + /// .await?; + /// user.set_password(&Password::new("new_password")); + /// user.save(&db).await?; + /// + /// let credentials = + /// DatabaseUserCredentials::new(String::from("testuser"), Password::new("new_password")); + /// let authenticated = DatabaseUser::authenticate(&db, &credentials).await?; + /// assert!(authenticated.is_some()); + /// + /// Ok(Html::new("Password changed!")) + /// } /// - /// # let mut user = DatabaseUser::new(Auto::fixed(1), LimitedString::new("user").unwrap(), &Password::new("oldpassword")); - /// user.set_password(&Password::new("newpassword")); + /// # #[tokio::main] + /// # async fn main() -> cot::Result<()> { + /// # use cot::test::{TestDatabase, TestRequestBuilder}; + /// # let mut test_database = TestDatabase::new_sqlite().await?; + /// # test_database.with_auth().run_migrations().await; + /// # view(test_database.database()).await?; + /// # test_database.cleanup().await?; + /// # Ok(()) + /// # } /// ``` pub fn set_password(&mut self, password: &Password) { self.password = PasswordHash::from_password(password); From 5de5936b33144fcf75b40f1cd9b75d1417491abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ma=C4=87kowski?= Date: Thu, 21 May 2026 16:17:19 +0200 Subject: [PATCH 3/3] fixes --- cot/src/auth/db.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/cot/src/auth/db.rs b/cot/src/auth/db.rs index b0491f796..0077b68ab 100644 --- a/cot/src/auth/db.rs +++ b/cot/src/auth/db.rs @@ -101,11 +101,7 @@ impl DatabaseUser { username: T, password: U, ) -> Result { - let username = username.into(); - let username_length = username.len(); - let username = LimitedString::::new(username).map_err(|_| { - AuthError::backend_error(CreateUserError::UsernameTooLong(username_length)) - })?; + let username = Self::convert_username(username)?; let mut user = Self::new(Auto::auto(), username, &password.into()); user.insert(db).await.map_err(AuthError::backend_error)?; @@ -113,11 +109,26 @@ impl DatabaseUser { Ok(user) } + fn convert_username>( + username: T, + ) -> Result> { + let username = username.into(); + let username_length = username.len(); + let username = LimitedString::::new(username).map_err(|_| { + AuthError::backend_error(CreateUserError::UsernameTooLong(username_length)) + })?; + Ok(username) + } + /// Sets the username of the user. /// /// Remember to call [`save()`][cot::db::Model::save] after calling this /// function - otherwise, the username will never be saved to the database. /// + /// # Errors + /// + /// Returns an error if the user could not be saved. + /// /// # Example /// /// ``` @@ -130,7 +141,7 @@ impl DatabaseUser { /// let mut user = /// DatabaseUser::create_user(&db, "testuser".to_string(), &Password::new("password123")) /// .await?; - /// user.set_username(LimitedString::new("new_username").unwrap()); + /// user.set_username("new_username")?; /// user.save(&db).await?; /// /// assert_eq!(user.username(), "new_username"); @@ -148,8 +159,9 @@ impl DatabaseUser { /// # Ok(()) /// # } /// ``` - pub fn set_username(&mut self, username: LimitedString) { - self.username = username; + pub fn set_username(&mut self, username: impl Into) -> Result<()> { + self.username = Self::convert_username(username)?; + Ok(()) } /// Sets the password of the user.