diff --git a/migration/src/refactor_platform_rs.sql b/migration/src/refactor_platform_rs.sql index 5f73804..ac200ce 100644 --- a/migration/src/refactor_platform_rs.sql +++ b/migration/src/refactor_platform_rs.sql @@ -1,6 +1,6 @@ --- SQL dump generated using DBML (dbml-lang.org) +-- SQL dump generated using DBML (dbml.dbdiagram.io) -- Database: PostgreSQL --- Generated at: 2024-10-31T16:04:10.825Z +-- Generated at: 2024-12-03T17:35:25.615Z CREATE TYPE "status" AS ENUM ( diff --git a/web/src/protect/coaching_relationships.rs b/web/src/protect/coaching_relationships.rs index 6fb8056..57d078a 100644 --- a/web/src/protect/coaching_relationships.rs +++ b/web/src/protect/coaching_relationships.rs @@ -1,14 +1,34 @@ -use axum::extract::Request; -use entity_api::user::AuthSession; +use crate::{extractors::authenticated_user::AuthenticatedUser, AppState}; +use axum::{ + extract::{Path, Request, State}, + http::StatusCode, + middleware::Next, + response::IntoResponse, +}; -struct AuthorizationError {} +use entity::Id; +use entity_api::organization; +use std::collections::HashSet; -pub(crate) async fn protect( - auth_session: AuthSession, +/// Checks that the organization record referenced by `organization_id` +/// exists and that the authenticated user is associated with i.t +/// Intended to be given to axum::middleware::from_fn_with_state in the router +pub(crate) async fn index( + State(app_state): State, + AuthenticatedUser(user): AuthenticatedUser, + Path(organization_id): Path, request: Request, -) -> Result { - // here we have access to the current user (actor) making the request - // as well as the request itself (from which we can determine which resource is being acted upon). - // We can use both pieces of information to determine if the actor is allowed to operate on the resource. - Ok(request) + next: Next, +) -> impl IntoResponse { + let user_organization_ids = organization::find_by_user(app_state.db_conn_ref(), user.id) + .await + .unwrap_or(vec![]) + .into_iter() + .map(|org| org.id) + .collect::>(); + if user_organization_ids.contains(&organization_id) { + next.run(request).await + } else { + (StatusCode::UNAUTHORIZED, "UNAUTHORIZED").into_response() + } } diff --git a/web/src/protect/mod.rs b/web/src/protect/mod.rs index b317515..366f546 100644 --- a/web/src/protect/mod.rs +++ b/web/src/protect/mod.rs @@ -1,22 +1 @@ pub(crate) mod coaching_relationships; - -#[macro_export] -macro_rules! protected_resource { - ($protect:expr, $alternative:expr) => {{ - use axum::{ - extract::Request, - middleware::{from_fn, Next}, - response::IntoResponse, - }; - use entity_api::user::AuthSession; - - from_fn( - |auth_session: AuthSession, req: Request, next: Next| async move { - match $protect(auth_session, req).await { - Ok(req) => next.run(req).await, - Err(_) => $alternative.into_response(), - } - }, - ) - }}; -} diff --git a/web/src/router.rs b/web/src/router.rs index ac2eb36..4b30195 100644 --- a/web/src/router.rs +++ b/web/src/router.rs @@ -1,6 +1,6 @@ -use crate::{protected_resource, AppState}; +use crate::{protect, AppState}; use axum::{ - http::StatusCode, + middleware::from_fn_with_state, routing::{delete, get, post, put}, Router, }; @@ -14,8 +14,6 @@ use crate::controller::{ user_session_controller, }; -use crate::protect::coaching_relationships; - use utoipa::{ openapi::security::{ApiKey, ApiKeyValue, SecurityScheme}, Modify, OpenApi, @@ -178,15 +176,15 @@ fn organization_coaching_relationship_routes(app_state: AppState) -> Router { "/organizations/:organization_id/coaching_relationships", get(organization::coaching_relationship_controller::index), ) + .route_layer(from_fn_with_state( + app_state.clone(), + protect::coaching_relationships::index, + )) .route( "/organizations/:organization_id/coaching_relationships/:relationship_id", get(organization::coaching_relationship_controller::read), ) .route_layer(login_required!(Backend, login_url = "/login")) - .route_layer(protected_resource!( - coaching_relationships::protect, - StatusCode::UNAUTHORIZED - )) .with_state(app_state) }