edit users
This commit is contained in:
@@ -145,6 +145,13 @@ impl AuthMetrics for MetricsDebugLogger {
|
||||
tracing::debug!("[Metrics] User creation failed");
|
||||
}
|
||||
|
||||
async fn record_user_edit_success(&self) {
|
||||
tracing::debug!("[Metrics] User edit succeeded");
|
||||
}
|
||||
async fn record_user_edit_failure(&self) {
|
||||
tracing::debug!("[Metrics] User edit failed");
|
||||
}
|
||||
|
||||
async fn record_user_deletion_success(&self) {
|
||||
tracing::debug!("[Metrics] User deletion succeeded");
|
||||
}
|
||||
@@ -152,13 +159,20 @@ impl AuthMetrics for MetricsDebugLogger {
|
||||
tracing::debug!("[Metrics] User deletion failed");
|
||||
}
|
||||
|
||||
async fn record_user_list_success(&self) -> () {
|
||||
async fn record_user_list_success(&self) {
|
||||
tracing::debug!("[Metrics] User list succeeded");
|
||||
}
|
||||
async fn record_user_list_failure(&self) -> () {
|
||||
async fn record_user_list_failure(&self) {
|
||||
tracing::debug!("[Metrics] User list failed");
|
||||
}
|
||||
|
||||
async fn record_list_all_users_and_warrens_success(&self) {
|
||||
tracing::debug!("[Metrics] Users, warrens and user warrens list succeeded");
|
||||
}
|
||||
async fn record_list_all_users_and_warrens_failure(&self) {
|
||||
tracing::debug!("[Metrics] Users, warrens and user warrens list failed");
|
||||
}
|
||||
|
||||
async fn record_auth_session_creation_success(&self) {
|
||||
tracing::debug!("[Metrics] Auth session creation succeeded");
|
||||
}
|
||||
@@ -180,10 +194,10 @@ impl AuthMetrics for MetricsDebugLogger {
|
||||
tracing::debug!("[Metrics] Auth warren list failed");
|
||||
}
|
||||
|
||||
async fn record_auth_fetch_user_warrens_success(&self) -> () {
|
||||
async fn record_auth_fetch_user_warrens_success(&self) {
|
||||
tracing::debug!("[Metrics] Auth user warren id fetch succeeded");
|
||||
}
|
||||
async fn record_auth_fetch_user_warrens_failure(&self) -> () {
|
||||
async fn record_auth_fetch_user_warrens_failure(&self) {
|
||||
tracing::debug!("[Metrics] Auth user warren id fetch failed");
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::domain::warren::{
|
||||
models::{
|
||||
auth_session::requests::FetchAuthSessionResponse,
|
||||
file::{File, FilePath},
|
||||
user::{LoginUserResponse, User},
|
||||
user::{ListAllUsersAndWarrensResponse, LoginUserResponse, User},
|
||||
user_warren::UserWarren,
|
||||
warren::{
|
||||
CreateWarrenDirectoryResponse, DeleteWarrenDirectoryResponse, DeleteWarrenFileResponse,
|
||||
@@ -129,6 +129,14 @@ impl AuthNotifier for NotifierDebugLogger {
|
||||
);
|
||||
}
|
||||
|
||||
async fn user_edited(&self, editor: &User, user: &User) {
|
||||
tracing::debug!(
|
||||
"[Notifier] Admin user {} edited user {}",
|
||||
editor.name(),
|
||||
user.id()
|
||||
);
|
||||
}
|
||||
|
||||
async fn user_deleted(&self, deleter: &User, user: &User) {
|
||||
tracing::debug!(
|
||||
"[Notifier] Admin user {} deleted user {}",
|
||||
@@ -145,6 +153,20 @@ impl AuthNotifier for NotifierDebugLogger {
|
||||
);
|
||||
}
|
||||
|
||||
async fn all_users_and_warrens_listed(
|
||||
&self,
|
||||
user: &User,
|
||||
response: &ListAllUsersAndWarrensResponse,
|
||||
) {
|
||||
tracing::debug!(
|
||||
"[Notifier] Admin user {} listed {} user(s), {} warren(s) and {} user warren(s)",
|
||||
user.name(),
|
||||
response.users().len(),
|
||||
response.warrens().len(),
|
||||
response.user_warrens().len(),
|
||||
);
|
||||
}
|
||||
|
||||
async fn user_logged_in(&self, response: &LoginUserResponse) {
|
||||
tracing::debug!("[Notifier] Logged in user {}", response.user().name());
|
||||
}
|
||||
|
||||
@@ -25,22 +25,23 @@ use crate::domain::warren::{
|
||||
},
|
||||
},
|
||||
user::{
|
||||
CreateUserError, CreateUserRequest, DeleteUserError, DeleteUserRequest, ListUsersError,
|
||||
ListUsersRequest, User, UserEmail, UserName, UserPassword, VerifyUserPasswordError,
|
||||
VerifyUserPasswordRequest,
|
||||
CreateUserError, CreateUserRequest, DeleteUserError, DeleteUserRequest, EditUserError,
|
||||
EditUserRequest, ListAllUsersAndWarrensError, ListAllUsersAndWarrensRequest,
|
||||
ListAllUsersAndWarrensResponse, ListUsersError, ListUsersRequest, User, UserEmail,
|
||||
UserName, UserPassword, VerifyUserPasswordError, VerifyUserPasswordRequest,
|
||||
},
|
||||
user_warren::{
|
||||
UserWarren,
|
||||
requests::{
|
||||
FetchUserWarrenError, FetchUserWarrenRequest, FetchUserWarrensError,
|
||||
FetchUserWarrensRequest,
|
||||
FetchUserWarrensRequest, ListUserWarrensError, ListUserWarrensRequest,
|
||||
},
|
||||
},
|
||||
warren::{
|
||||
FetchWarrenError, FetchWarrenRequest, FetchWarrensError, FetchWarrensRequest, Warren,
|
||||
},
|
||||
},
|
||||
ports::{AuthRepository, WarrenRepository},
|
||||
ports::{AuthRepository, WarrenRepository, WarrenService},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -138,12 +139,8 @@ impl Postgres {
|
||||
password: &UserPassword,
|
||||
is_admin: bool,
|
||||
) -> anyhow::Result<User> {
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
let argon2 = Argon2::default();
|
||||
let password_hash = argon2
|
||||
.hash_password(password.as_str().as_bytes(), &salt)
|
||||
.map_err(|_| anyhow!("Failed to hash password"))?
|
||||
.to_string();
|
||||
let password_hash =
|
||||
hash_password(password).map_err(|e| anyhow!("Failed to hash password: {e:?}"))?;
|
||||
|
||||
let mut tx = connection.begin().await?;
|
||||
|
||||
@@ -176,6 +173,73 @@ impl Postgres {
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
async fn edit_user(
|
||||
&self,
|
||||
connection: &mut PgConnection,
|
||||
id: &Uuid,
|
||||
name: &UserName,
|
||||
email: &UserEmail,
|
||||
password: Option<&UserPassword>,
|
||||
is_admin: bool,
|
||||
) -> anyhow::Result<User> {
|
||||
let password_hash = if let Some(password) = password {
|
||||
Some(hash_password(password).map_err(|e| anyhow!("Failed to hash password: {e:?}"))?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut tx = connection.begin().await?;
|
||||
|
||||
let user: User = sqlx::query_as(
|
||||
"UPDATE
|
||||
users
|
||||
SET
|
||||
name = $2,
|
||||
email = $3,
|
||||
hash = COALESCE($4, hash),
|
||||
admin = $5
|
||||
WHERE
|
||||
id = $1
|
||||
RETURNING
|
||||
*
|
||||
",
|
||||
)
|
||||
.bind(id)
|
||||
.bind(name)
|
||||
.bind(email)
|
||||
.bind(password_hash)
|
||||
.bind(is_admin)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
|
||||
self.delete_user_sessions(&mut *tx, id).await?;
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
async fn delete_user_sessions(
|
||||
&self,
|
||||
connection: &mut PgConnection,
|
||||
user_id: &Uuid,
|
||||
) -> Result<u64, sqlx::Error> {
|
||||
let rows_affected = sqlx::query(
|
||||
"
|
||||
DELETE FROM
|
||||
auth_sessions
|
||||
WHERE
|
||||
user_id = $1
|
||||
",
|
||||
)
|
||||
.bind(user_id)
|
||||
.execute(connection)
|
||||
.await?
|
||||
.rows_affected();
|
||||
|
||||
Ok(rows_affected)
|
||||
}
|
||||
|
||||
async fn delete_user_from_database(
|
||||
&self,
|
||||
connection: &mut PgConnection,
|
||||
@@ -307,7 +371,7 @@ impl Postgres {
|
||||
&self,
|
||||
connection: &mut PgConnection,
|
||||
session_id: &AuthSessionId,
|
||||
) -> anyhow::Result<AuthSession> {
|
||||
) -> Result<AuthSession, sqlx::Error> {
|
||||
let session: AuthSession = sqlx::query_as(
|
||||
"
|
||||
SELECT
|
||||
@@ -330,7 +394,7 @@ impl Postgres {
|
||||
connection: &mut PgConnection,
|
||||
user_id: &Uuid,
|
||||
) -> Result<Vec<UserWarren>, sqlx::Error> {
|
||||
let ids: Vec<UserWarren> = sqlx::query_as(
|
||||
let user_warrens: Vec<UserWarren> = sqlx::query_as(
|
||||
"
|
||||
SELECT
|
||||
*
|
||||
@@ -344,7 +408,25 @@ impl Postgres {
|
||||
.fetch_all(connection)
|
||||
.await?;
|
||||
|
||||
Ok(ids)
|
||||
Ok(user_warrens)
|
||||
}
|
||||
|
||||
async fn get_all_user_warrens(
|
||||
&self,
|
||||
connection: &mut PgConnection,
|
||||
) -> Result<Vec<UserWarren>, sqlx::Error> {
|
||||
let user_warrens: Vec<UserWarren> = sqlx::query_as(
|
||||
"
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
user_warrens
|
||||
",
|
||||
)
|
||||
.fetch_all(connection)
|
||||
.await?;
|
||||
|
||||
Ok(user_warrens)
|
||||
}
|
||||
|
||||
async fn get_user_warren(
|
||||
@@ -379,6 +461,8 @@ impl Postgres {
|
||||
*
|
||||
FROM
|
||||
users
|
||||
ORDER BY
|
||||
created_at ASC
|
||||
",
|
||||
)
|
||||
.fetch_all(connection)
|
||||
@@ -453,6 +537,28 @@ impl AuthRepository for Postgres {
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
async fn edit_user(&self, request: EditUserRequest) -> Result<User, EditUserError> {
|
||||
let mut connection = self
|
||||
.pool
|
||||
.acquire()
|
||||
.await
|
||||
.context("Failed to get a PostgreSQL connection")?;
|
||||
|
||||
let user = self
|
||||
.edit_user(
|
||||
&mut connection,
|
||||
request.user_id(),
|
||||
request.name(),
|
||||
request.email(),
|
||||
request.password(),
|
||||
request.admin(),
|
||||
)
|
||||
.await
|
||||
.context(format!("Failed to edit user"))?;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
async fn delete_user(&self, request: DeleteUserRequest) -> Result<User, DeleteUserError> {
|
||||
let mut connection = self
|
||||
.pool
|
||||
@@ -528,7 +634,13 @@ impl AuthRepository for Postgres {
|
||||
let session = self
|
||||
.get_auth_session(&mut connection, request.session_id())
|
||||
.await
|
||||
.context("Failed to get auth session")?;
|
||||
.map_err(|e| {
|
||||
if is_not_found_error(&e) {
|
||||
FetchAuthSessionError::NotFound
|
||||
} else {
|
||||
anyhow!("Failed to get auth session: {e:?}").into()
|
||||
}
|
||||
})?;
|
||||
let user = self
|
||||
.get_user_from_id(&mut connection, session.user_id())
|
||||
.await
|
||||
@@ -547,12 +659,30 @@ impl AuthRepository for Postgres {
|
||||
.await
|
||||
.context("Failed to get a PostgreSQL connection")?;
|
||||
|
||||
let warren_ids = self
|
||||
let user_warrens = self
|
||||
.get_user_warrens(&mut connection, request.user_id())
|
||||
.await
|
||||
.context("Failed to get user warrens")?;
|
||||
|
||||
Ok(warren_ids)
|
||||
Ok(user_warrens)
|
||||
}
|
||||
|
||||
async fn list_user_warrens(
|
||||
&self,
|
||||
_request: ListUserWarrensRequest,
|
||||
) -> Result<Vec<UserWarren>, ListUserWarrensError> {
|
||||
let mut connection = self
|
||||
.pool
|
||||
.acquire()
|
||||
.await
|
||||
.context("Failed to get a PostgreSQL connection")?;
|
||||
|
||||
let user_warrens = self
|
||||
.get_all_user_warrens(&mut connection)
|
||||
.await
|
||||
.context("Failed to get all user warrens")?;
|
||||
|
||||
Ok(user_warrens)
|
||||
}
|
||||
|
||||
async fn fetch_user_warren(
|
||||
@@ -590,8 +720,53 @@ impl AuthRepository for Postgres {
|
||||
|
||||
Ok(users)
|
||||
}
|
||||
|
||||
async fn list_all_users_and_warrens<WS: WarrenService>(
|
||||
&self,
|
||||
_request: ListAllUsersAndWarrensRequest,
|
||||
warren_service: &WS,
|
||||
) -> Result<ListAllUsersAndWarrensResponse, ListAllUsersAndWarrensError> {
|
||||
let mut connection = self
|
||||
.pool
|
||||
.acquire()
|
||||
.await
|
||||
.context("Failed to get a PostgreSQL connection")?;
|
||||
|
||||
let users = self
|
||||
.fetch_users(&mut connection)
|
||||
.await
|
||||
.context("Failed to fetch all users")?;
|
||||
let user_warrens = self
|
||||
.get_all_user_warrens(&mut connection)
|
||||
.await
|
||||
.context("Failed to fetch all user warrens")?;
|
||||
let warrens = warren_service
|
||||
.list_warrens(FetchWarrensRequest::new(
|
||||
user_warrens
|
||||
.iter()
|
||||
.map(|uw| uw.warren_id().clone())
|
||||
.collect(),
|
||||
))
|
||||
.await
|
||||
.context("Failed to get warrens")?;
|
||||
|
||||
Ok(ListAllUsersAndWarrensResponse::new(
|
||||
users,
|
||||
user_warrens,
|
||||
warrens,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_not_found_error(err: &sqlx::Error) -> bool {
|
||||
matches!(err, sqlx::Error::RowNotFound)
|
||||
}
|
||||
|
||||
fn hash_password(password: &UserPassword) -> Result<String, argon2::password_hash::Error> {
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
let argon2 = Argon2::default();
|
||||
|
||||
argon2
|
||||
.hash_password(password.as_str().as_bytes(), &salt)
|
||||
.map(|h| h.to_string())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user