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/controller/organization_controller.rs b/web/src/controller/organization_controller.rs index 97862ac..0b4907e 100644 --- a/web/src/controller/organization_controller.rs +++ b/web/src/controller/organization_controller.rs @@ -35,14 +35,16 @@ use log::debug; )] pub async fn index( CompareApiVersion(_v): CompareApiVersion, - AuthenticatedUser(_user): AuthenticatedUser, + AuthenticatedUser(user): AuthenticatedUser, // TODO: create a new Extractor to authorize the user to access // the data requested State(app_state): State, - Query(params): Query>, + Query(mut params): Query>, ) -> Result { debug!("GET all Organizations"); + params.insert("user_id".to_string(), user.id.to_string()); + let organizations = OrganizationApi::find_by(app_state.db_conn_ref(), params).await?; debug!("Found Organizations: {:?}", organizations); diff --git a/web/src/lib.rs b/web/src/lib.rs index e6ba453..74d25cc 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -19,6 +19,7 @@ use tower_http::cors::CorsLayer; mod controller; mod error; pub(crate) mod extractors; +pub(crate) mod protect; mod router; pub async fn init_server(app_state: AppState) -> Result<()> { diff --git a/web/src/protect/coaching_relationships.rs b/web/src/protect/coaching_relationships.rs new file mode 100644 index 0000000..57d078a --- /dev/null +++ b/web/src/protect/coaching_relationships.rs @@ -0,0 +1,34 @@ +use crate::{extractors::authenticated_user::AuthenticatedUser, AppState}; +use axum::{ + extract::{Path, Request, State}, + http::StatusCode, + middleware::Next, + response::IntoResponse, +}; + +use entity::Id; +use entity_api::organization; +use std::collections::HashSet; + +/// 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, + 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 new file mode 100644 index 0000000..366f546 --- /dev/null +++ b/web/src/protect/mod.rs @@ -0,0 +1 @@ +pub(crate) mod coaching_relationships; diff --git a/web/src/router.rs b/web/src/router.rs index f719520..4b30195 100644 --- a/web/src/router.rs +++ b/web/src/router.rs @@ -1,5 +1,6 @@ -use crate::AppState; +use crate::{protect, AppState}; use axum::{ + middleware::from_fn_with_state, routing::{delete, get, post, put}, Router, }; @@ -175,6 +176,10 @@ 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),