re-implement guards using AppError returns instead of macros

This commit is contained in:
Brent Schroeter 2025-02-26 13:10:47 -08:00
parent 7b6a84f011
commit da38946dbd
3 changed files with 53 additions and 48 deletions

View file

@ -7,6 +7,7 @@ use axum::response::{IntoResponse, Response};
#[derive(Debug)]
pub enum AppError {
InternalServerError(Error),
ForbiddenError(String),
}
// Tell axum how to convert `AppError` into a response.
@ -14,9 +15,13 @@ impl IntoResponse for AppError {
fn into_response(self) -> Response {
match self {
Self::InternalServerError(err) => {
tracing::error!("Application error: {:#}", err);
tracing::error!("Application error: {}", err);
(StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong").into_response()
}
Self::ForbiddenError(client_message) => {
tracing::info!("Forbidden: {}", client_message);
(StatusCode::FORBIDDEN, client_message).into_response()
}
}
}
}

View file

@ -1,45 +1,45 @@
macro_rules! require_team_membership {
($current_user:expr, $team_id:expr, $db_conn:expr) => {{
let current_user_id = $current_user.id.clone();
match $db_conn
use deadpool_diesel::postgres::Connection;
use diesel::prelude::*;
use uuid::Uuid;
use crate::{
app_error::AppError, csrf::validate_csrf_token, team_memberships::TeamMembership, teams::Team,
users::User,
};
pub async fn require_team_membership(
current_user: &User,
team_id: &Uuid,
db_conn: &Connection,
) -> Result<Team, AppError> {
let current_user_id = current_user.id.clone();
let team_id = team_id.clone();
match db_conn
.interact(move |conn| {
crate::team_memberships::TeamMembership::all()
.filter(crate::team_memberships::TeamMembership::with_user_id(
current_user_id,
))
.filter(crate::team_memberships::TeamMembership::with_team_id(
$team_id,
))
TeamMembership::all()
.filter(TeamMembership::with_user_id(current_user_id))
.filter(TeamMembership::with_team_id(team_id))
.first(conn)
.optional()
})
.await
.unwrap()?
{
Some((team, _)) => team,
None => {
return Ok((
axum::http::StatusCode::FORBIDDEN,
Some((team, _)) => Ok(team),
None => Err(AppError::ForbiddenError(
"not a member of requested team".to_string(),
)
.into_response());
)),
}
}
}};
}
pub(crate) use require_team_membership;
macro_rules! require_valid_csrf_token {
($csrf_token:expr, $current_user:expr, $db_conn:expr) => {{
if !crate::csrf::validate_csrf_token(&$db_conn, &$csrf_token, Some($current_user.id))
.await?
{
return Ok((
axum::http::StatusCode::FORBIDDEN,
"invalid CSRF token".to_string(),
)
.into_response());
pub async fn require_valid_csrf_token(
csrf_token: &str,
current_user: &User,
db_conn: &Connection,
) -> Result<(), AppError> {
if validate_csrf_token(db_conn, csrf_token, Some(current_user.id.clone())).await? {
Ok(())
} else {
Err(AppError::ForbiddenError("invalid CSRF token".to_string()))
}
}};
}
pub(crate) use require_valid_csrf_token;

View file

@ -111,8 +111,8 @@ async fn post_new_api_key(
CurrentUser(current_user): CurrentUser,
Form(form): Form<PostNewApiKeyForm>,
) -> Result<impl IntoResponse, AppError> {
guards::require_valid_csrf_token!(form.csrf_token, current_user, db_conn);
let team = guards::require_team_membership!(current_user, team_id, db_conn);
guards::require_valid_csrf_token(&form.csrf_token, &current_user, &db_conn).await?;
let team = guards::require_team_membership(&current_user, &team_id, &db_conn).await?;
ApiKey::generate_for_team(&db_conn, team.id.clone()).await?;
Ok(Redirect::to(&format!(
@ -158,7 +158,7 @@ async fn post_new_team(
CurrentUser(current_user): CurrentUser,
Form(form): Form<PostNewTeamForm>,
) -> Result<impl IntoResponse, AppError> {
guards::require_valid_csrf_token!(form.csrf_token, current_user, db_conn);
guards::require_valid_csrf_token(&form.csrf_token, &current_user, &db_conn).await?;
let team_id = Uuid::now_v7();
let team = Team {
@ -195,7 +195,7 @@ async fn projects_page(
Path(team_id): Path<Uuid>,
CurrentUser(current_user): CurrentUser,
) -> Result<impl IntoResponse, AppError> {
let team = guards::require_team_membership!(current_user, team_id, db_conn);
let team = guards::require_team_membership(&current_user, &team_id, &db_conn).await?;
let team_id = team.id.clone();
let api_keys = db_conn