stormchaser_api/auth/
opa.rs1use crate::AppState;
2use anyhow::Result;
3use axum::{
4 extract::{Request, State},
5 http::{header, StatusCode},
6 middleware::Next,
7 response::Response,
8};
9use stormchaser_model::auth::ApiOpaContext;
10use tracing::{debug, error, info};
11
12pub async fn opa_middleware(
14 State(state): State<AppState>,
15 request: Request,
16 next: Next,
17) -> Result<Response, StatusCode> {
18 let authorizer = &*state.opa;
19 if !authorizer.is_configured() {
20 return Ok(next.run(request).await);
21 }
22
23 let path = request.uri().path().to_string();
24 let method = request.method().to_string();
25
26 let token = request
28 .headers()
29 .get(header::AUTHORIZATION)
30 .and_then(|h| h.to_str().ok())
31 .and_then(|s| s.strip_prefix("Bearer "));
32
33 let context = ApiOpaContext {
34 path: &path,
35 method: &method,
36 token,
37 };
38
39 match authorizer.check(context).await {
40 Ok(true) => {
41 debug!("OPA allowed access to {} {}", method, path);
42 Ok(next.run(request).await)
43 }
44 Ok(false) => {
45 info!("OPA denied access to {} {}", method, path);
46 Err(StatusCode::FORBIDDEN)
47 }
48 Err(e) => {
49 error!("OPA check failed for {} {}: {:?}", method, path, e);
50 Err(StatusCode::INTERNAL_SERVER_ERROR)
52 }
53 }
54}