use crate::db::Db;
use crate::error::AuthError;
use crate::types::{Role, RoleId, RoleName, UserId};
fn map_unique_violation(err: sqlx::Error) -> AuthError {
if let sqlx::Error::Database(ref db_err) = err {
let msg = db_err.message();
if msg.contains("UNIQUE constraint failed") && msg.contains("name") {
return AuthError::Conflict("role name already exists".into());
}
}
AuthError::Database(err)
}
impl Db {
pub async fn create_role(
&self,
name: &RoleName,
description: Option<&str>,
) -> Result<Role, AuthError> {
let id = RoleId::new();
sqlx::query_as::<_, Role>(
"INSERT INTO allowthem_roles (id, name, description) \
VALUES (?, ?, ?) \
RETURNING id, name, description, created_at",
)
.bind(id)
.bind(name)
.bind(description)
.fetch_one(self.pool())
.await
.map_err(map_unique_violation)
}
pub async fn get_role(&self, id: &RoleId) -> Result<Option<Role>, AuthError> {
sqlx::query_as::<_, Role>(
"SELECT id, name, description, created_at FROM allowthem_roles WHERE id = ?",
)
.bind(*id)
.fetch_optional(self.pool())
.await
.map_err(AuthError::Database)
}
pub async fn get_role_by_name(&self, name: &RoleName) -> Result<Option<Role>, AuthError> {
sqlx::query_as::<_, Role>(
"SELECT id, name, description, created_at FROM allowthem_roles WHERE name = ?",
)
.bind(name)
.fetch_optional(self.pool())
.await
.map_err(AuthError::Database)
}
pub async fn list_roles(&self) -> Result<Vec<Role>, AuthError> {
sqlx::query_as::<_, Role>(
"SELECT id, name, description, created_at FROM allowthem_roles ORDER BY created_at",
)
.fetch_all(self.pool())
.await
.map_err(AuthError::Database)
}
pub async fn delete_role(&self, id: &RoleId) -> Result<bool, AuthError> {
let result = sqlx::query("DELETE FROM allowthem_roles WHERE id = ?")
.bind(*id)
.execute(self.pool())
.await
.map_err(AuthError::Database)?;
Ok(result.rows_affected() > 0)
}
pub async fn assign_role(&self, user_id: &UserId, role_id: &RoleId) -> Result<(), AuthError> {
sqlx::query("INSERT OR IGNORE INTO allowthem_user_roles (user_id, role_id) VALUES (?, ?)")
.bind(*user_id)
.bind(*role_id)
.execute(self.pool())
.await
.map_err(AuthError::Database)?;
Ok(())
}
pub async fn unassign_role(
&self,
user_id: &UserId,
role_id: &RoleId,
) -> Result<bool, AuthError> {
let result =
sqlx::query("DELETE FROM allowthem_user_roles WHERE user_id = ? AND role_id = ?")
.bind(*user_id)
.bind(*role_id)
.execute(self.pool())
.await
.map_err(AuthError::Database)?;
Ok(result.rows_affected() > 0)
}
pub async fn has_role(
&self,
user_id: &UserId,
role_name: &RoleName,
) -> Result<bool, AuthError> {
let count: i64 = sqlx::query_scalar(
"SELECT COUNT(*) \
FROM allowthem_user_roles ur \
JOIN allowthem_roles r ON r.id = ur.role_id \
WHERE ur.user_id = ? AND r.name = ?",
)
.bind(*user_id)
.bind(role_name)
.fetch_one(self.pool())
.await
.map_err(AuthError::Database)?;
Ok(count > 0)
}
pub async fn get_user_roles(&self, user_id: &UserId) -> Result<Vec<Role>, AuthError> {
sqlx::query_as::<_, Role>(
"SELECT r.id, r.name, r.description, r.created_at \
FROM allowthem_roles r \
JOIN allowthem_user_roles ur ON ur.role_id = r.id \
WHERE ur.user_id = ? \
ORDER BY r.created_at",
)
.bind(*user_id)
.fetch_all(self.pool())
.await
.map_err(AuthError::Database)
}
}