diff --git a/app/src/create_user.rs b/app/src/create_user.rs index 32ead31..71a23ea 100644 --- a/app/src/create_user.rs +++ b/app/src/create_user.rs @@ -67,7 +67,7 @@ impl Component for CreateUserForm { display_name: Some(get_element("displayname")), first_name: Some(get_element("firstname")), last_name: Some(get_element("lastname")), - password: get_element("password"), + password: Some(get_element("password")), }; self.create_user(req); } diff --git a/model/src/lib.rs b/model/src/lib.rs index d2b7d19..1a0746c 100644 --- a/model/src/lib.rs +++ b/model/src/lib.rs @@ -56,7 +56,7 @@ pub struct CreateUserRequest { pub display_name: Option, pub first_name: Option, pub last_name: Option, - pub password: String, + pub password: Option, } #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Default)] diff --git a/src/domain/sql_backend_handler.rs b/src/domain/sql_backend_handler.rs index 0a26e5d..f767ecd 100644 --- a/src/domain/sql_backend_handler.rs +++ b/src/domain/sql_backend_handler.rs @@ -110,14 +110,20 @@ impl BackendHandler for SqlBackendHandler { .and_where(Expr::col(Users::UserId).eq(request.name.as_str())) .to_string(DbQueryBuilder {}); if let Ok(row) = sqlx::query(&query).fetch_one(&self.sql_pool).await { - if let Err(e) = passwords_match( - &row.get::, _>(&*Users::PasswordHash.to_string()), - &request.password, - self.config.get_server_keys().private(), - ) { - debug!(r#"Invalid password for "{}": {}"#, request.name, e); + if let Some(password_hash) = + row.get::>, _>(&*Users::PasswordHash.to_string()) + { + if let Err(e) = passwords_match( + &&password_hash, + &request.password, + self.config.get_server_keys().private(), + ) { + debug!(r#"Invalid password for "{}": {}"#, request.name, e); + } else { + return Ok(()); + } } else { - return Ok(()); + debug!(r#"User "{}" has no password"#, request.name); } } else { debug!(r#"No user found for "{}""#, request.name); @@ -233,29 +239,34 @@ impl BackendHandler for SqlBackendHandler { } async fn create_user(&self, request: CreateUserRequest) -> Result<()> { - let password_hash = - get_password_file(&request.password, self.config.get_server_keys().public())? - .serialize(); + let mut columns = vec![ + Users::UserId, + Users::Email, + Users::DisplayName, + Users::FirstName, + Users::LastName, + Users::CreationDate, + ]; + let mut values = vec![ + request.user_id.into(), + request.email.into(), + request.display_name.map(Into::into).unwrap_or(Value::Null), + request.first_name.map(Into::into).unwrap_or(Value::Null), + request.last_name.map(Into::into).unwrap_or(Value::Null), + chrono::Utc::now().naive_utc().into(), + ]; + if let Some(pass) = request.password { + columns.push(Users::PasswordHash); + values.push( + get_password_file(&pass, self.config.get_server_keys().public())? + .serialize() + .into(), + ); + } let query = Query::insert() .into_table(Users::Table) - .columns(vec![ - Users::UserId, - Users::Email, - Users::DisplayName, - Users::FirstName, - Users::LastName, - Users::CreationDate, - Users::PasswordHash, - ]) - .values_panic(vec![ - request.user_id.into(), - request.email.into(), - request.display_name.map(Into::into).unwrap_or(Value::Null), - request.first_name.map(Into::into).unwrap_or(Value::Null), - request.last_name.map(Into::into).unwrap_or(Value::Null), - chrono::Utc::now().naive_utc().into(), - password_hash.into(), - ]) + .columns(columns) + .values_panic(values) .to_string(DbQueryBuilder {}); sqlx::query(&query).execute(&self.sql_pool).await?; Ok(()) @@ -325,7 +336,18 @@ mod tests { .create_user(CreateUserRequest { user_id: name.to_string(), email: "bob@bob.bob".to_string(), - password: pass.to_string(), + password: Some(pass.to_string()), + ..Default::default() + }) + .await + .unwrap(); + } + + async fn insert_user_no_password(handler: &SqlBackendHandler, name: &str) { + handler + .create_user(CreateUserRequest { + user_id: name.to_string(), + email: "bob@bob.bob".to_string(), ..Default::default() }) .await @@ -399,6 +421,22 @@ mod tests { .unwrap_err(); } + #[tokio::test] + async fn test_user_no_password() { + let sql_pool = get_initialized_db().await; + let config = get_default_config(); + let handler = SqlBackendHandler::new(config, sql_pool.clone()); + insert_user_no_password(&handler, "bob").await; + + handler + .bind(BindRequest { + name: "bob".to_string(), + password: "bob00".to_string(), + }) + .await + .unwrap_err(); + } + #[tokio::test] async fn test_list_users() { let sql_pool = get_initialized_db().await; diff --git a/src/domain/sql_tables.rs b/src/domain/sql_tables.rs index f0eeef3..14092c5 100644 --- a/src/domain/sql_tables.rs +++ b/src/domain/sql_tables.rs @@ -54,7 +54,7 @@ pub async fn init_table(pool: &Pool) -> sqlx::Result<()> { .col(ColumnDef::new(Users::LastName).string_len(255)) .col(ColumnDef::new(Users::Avatar).binary()) .col(ColumnDef::new(Users::CreationDate).date_time().not_null()) - .col(ColumnDef::new(Users::PasswordHash).binary().not_null()) + .col(ColumnDef::new(Users::PasswordHash).binary()) .col(ColumnDef::new(Users::TotpSecret).string_len(64)) .col(ColumnDef::new(Users::MfaType).string_len(64)) .to_string(DbQueryBuilder {}), diff --git a/src/infra/configuration.rs b/src/infra/configuration.rs index fcc9011..387d012 100644 --- a/src/infra/configuration.rs +++ b/src/infra/configuration.rs @@ -33,12 +33,7 @@ pub struct Configuration { impl ConfigurationBuilder { #[cfg(test)] pub fn build(self) -> Result { - let server_keys = get_server_keys( - &self - .key_file - .as_deref() - .unwrap_or("server_key"), - )?; + let server_keys = get_server_keys(&self.key_file.as_deref().unwrap_or("server_key"))?; Ok(self.server_keys(server_keys).private_build()?) } diff --git a/src/main.rs b/src/main.rs index bd696dc..99a4eca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ async fn create_admin_user(handler: &SqlBackendHandler, config: &Configuration) handler .create_user(lldap_model::CreateUserRequest { user_id: config.ldap_user_dn.clone(), - password: config.ldap_user_pass.clone(), + password: Some(config.ldap_user_pass.clone()), ..Default::default() }) .await