diff --git a/lib/workload/stateless/stacks/filemanager/deploy/stack.ts b/lib/workload/stateless/stacks/filemanager/deploy/stack.ts index 239d926f6..646d9950c 100644 --- a/lib/workload/stateless/stacks/filemanager/deploy/stack.ts +++ b/lib/workload/stateless/stacks/filemanager/deploy/stack.ts @@ -9,7 +9,12 @@ import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { ProviderFunction } from '../../../../components/provider-function'; import { ApiGatewayConstruct, ApiGatewayConstructProps } from '../../../../components/api-gateway'; import { IQueue, Queue } from 'aws-cdk-lib/aws-sqs'; -import { HttpMethod, HttpRoute, HttpRouteKey } from 'aws-cdk-lib/aws-apigatewayv2'; +import { + HttpMethod, + HttpNoneAuthorizer, + HttpRoute, + HttpRouteKey, +} from 'aws-cdk-lib/aws-apigatewayv2'; import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations'; import { InventoryFunction } from './constructs/functions/inventory'; import { NamedLambdaRole } from '../../../../components/named-lambda-role'; @@ -150,24 +155,31 @@ export class Filemanager extends Stack { const apiGateway = new ApiGatewayConstruct(this, 'ApiGateway', props.apiGatewayCognitoProps); const httpApi = apiGateway.httpApi; - const apiIntegration = new HttpLambdaIntegration('ApiIntegration', apiLambda.function); + const integration = new HttpLambdaIntegration('ApiIntegration', apiLambda.function); + + new HttpRoute(this, 'GetSchemaHttpRoute', { + httpApi, + integration, + authorizer: new HttpNoneAuthorizer(), + routeKey: HttpRouteKey.with(`/schema/{proxy+}`, HttpMethod.GET), + }); new HttpRoute(this, 'GetHttpRoute', { - httpApi: httpApi, - integration: apiIntegration, + httpApi, + integration, routeKey: HttpRouteKey.with('/{proxy+}', HttpMethod.GET), }); new HttpRoute(this, 'PatchHttpRoute', { - httpApi: httpApi, - integration: apiIntegration, + httpApi, + integration, authorizer: apiGateway.authStackHttpLambdaAuthorizer, routeKey: HttpRouteKey.with('/{proxy+}', HttpMethod.PATCH), }); new HttpRoute(this, 'PostHttpRoute', { - httpApi: httpApi, - integration: apiIntegration, + httpApi, + integration, authorizer: apiGateway.authStackHttpLambdaAuthorizer, routeKey: HttpRouteKey.with('/{proxy+}', HttpMethod.POST), }); diff --git a/lib/workload/stateless/stacks/filemanager/filemanager-api-server/src/main.rs b/lib/workload/stateless/stacks/filemanager/filemanager-api-server/src/main.rs index 176d49a4c..e685a3357 100644 --- a/lib/workload/stateless/stacks/filemanager/filemanager-api-server/src/main.rs +++ b/lib/workload/stateless/stacks/filemanager/filemanager-api-server/src/main.rs @@ -20,6 +20,7 @@ use filemanager::error::Result; use filemanager::handlers::init_tracing_with_format; use filemanager::handlers::Format::Pretty; use filemanager::queries::EntriesBuilder; +use filemanager::routes::openapi::SWAGGER_UI_PATH; use filemanager::routes::{router, AppState}; /// Run the filemanager API server locally to explore the API. @@ -115,8 +116,7 @@ async fn main() -> Result<()> { .with_key_divisor(key_divisor) .with_shuffle(shuffle) .build(state.database_client()) - .await - .unwrap(); + .await?; } if args.migrate { @@ -132,7 +132,7 @@ async fn main() -> Result<()> { let docs = Uri::builder() .scheme("http") .authority(local_addr.to_string()) - .path_and_query("/swagger-ui") + .path_and_query(SWAGGER_UI_PATH) .build() .map_err(|err| IoError(io::Error::other(err)))?; diff --git a/lib/workload/stateless/stacks/filemanager/filemanager/src/routes/error.rs b/lib/workload/stateless/stacks/filemanager/filemanager/src/routes/error.rs index aa2707a1d..654cc0238 100644 --- a/lib/workload/stateless/stacks/filemanager/filemanager/src/routes/error.rs +++ b/lib/workload/stateless/stacks/filemanager/filemanager/src/routes/error.rs @@ -74,6 +74,18 @@ pub enum ErrorStatusCode { example = json!({"message": "JSON Error: parsing json"}), )] BadRequest(ErrorResponse), + #[response( + status = UNAUTHORIZED, + description = "the request lacked valid authentication credentials", + example = json!({"message": "Unauthorized"}), + )] + Unauthorized(ErrorResponse), + #[response( + status = FORBIDDEN, + description = "the request lacked valid permissions for the resource", + example = json!({"message": "Forbidden"}), + )] + Forbidden(ErrorResponse), } impl From for ErrorStatusCode { @@ -117,6 +129,8 @@ impl Display for ErrorStatusCode { ErrorStatusCode::BadRequest(err) => Display::fmt(err, f), ErrorStatusCode::NotFound(err) => Display::fmt(err, f), ErrorStatusCode::InternalServerError(err) => Display::fmt(err, f), + ErrorStatusCode::Forbidden(err) => Display::fmt(err, f), + ErrorStatusCode::Unauthorized(err) => Display::fmt(err, f), ErrorStatusCode::Rejection(_, message) => Display::fmt(message, f), } } @@ -130,6 +144,8 @@ impl IntoResponse for ErrorStatusCode { (StatusCode::INTERNAL_SERVER_ERROR, extract::Json(err)) } ErrorStatusCode::NotFound(err) => (StatusCode::NOT_FOUND, extract::Json(err)), + ErrorStatusCode::Forbidden(err) => (StatusCode::NOT_FOUND, extract::Json(err)), + ErrorStatusCode::Unauthorized(err) => (StatusCode::NOT_FOUND, extract::Json(err)), ErrorStatusCode::Rejection(status, err) => ( StatusCode::from_u16(status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR), extract::Json(err), diff --git a/lib/workload/stateless/stacks/filemanager/filemanager/src/routes/openapi.rs b/lib/workload/stateless/stacks/filemanager/filemanager/src/routes/openapi.rs index dcd25ce27..3855b8c59 100644 --- a/lib/workload/stateless/stacks/filemanager/filemanager/src/routes/openapi.rs +++ b/lib/workload/stateless/stacks/filemanager/filemanager/src/routes/openapi.rs @@ -20,6 +20,9 @@ use crate::routes::pagination::*; use crate::routes::presign::ContentDisposition; use crate::routes::update::*; +/// The path to the swagger ui. +pub const SWAGGER_UI_PATH: &str = "/schema/swagger-ui"; + /// A newtype equivalent to a `DateTime` with a time zone. #[derive(ToSchema)] #[schema(value_type = DateTime, format = DateTime)] @@ -102,7 +105,7 @@ impl Modify for SecurityAddon { /// Create the swagger ui endpoint. pub fn swagger_ui() -> SwaggerUi { - SwaggerUi::new("/swagger-ui").url("/schema/openapi.json", ApiDoc::openapi()) + SwaggerUi::new(SWAGGER_UI_PATH).url("/schema/openapi.json", ApiDoc::openapi()) } #[cfg(test)] @@ -113,6 +116,7 @@ mod tests { use sqlx::PgPool; use tower::util::ServiceExt; + use super::*; use crate::database::aws::migration::tests::MIGRATOR; use crate::routes::router; use crate::routes::AppState; @@ -123,7 +127,7 @@ mod tests { let response = app .oneshot( Request::builder() - .uri("/swagger-ui") + .uri(SWAGGER_UI_PATH) .body(Body::empty()) .unwrap(), )