this_env/middleware/actix/
service.rs1use std::rc::Rc;
4use std::task::{Context, Poll};
5use actix_service::Service;
6use actix_web::dev::{ServiceRequest, ServiceResponse};
7use actix_web::Error;
8use actix_web::body::{BoxBody, EitherBody};
9use futures_util::future::LocalBoxFuture;
10use crate::middleware::actix::ActixMwConfig;
11use serde_json::json;
12use crate::middleware::actix::env_request_parser::parse_env_request;
13use crate::middleware::env_request::EnvRequest;
14use actix_web::{HttpResponse, http::StatusCode};
15use actix_web::HttpMessage;
16pub struct ActixMiddlewareService<S> {
19 pub(crate) service: Rc<S>,
20 pub(crate) config: ActixMwConfig,
21 pub(crate) conn: std::sync::Arc<rusqlite::Connection>,
22}
23
24impl<S, B> Service<ServiceRequest> for ActixMiddlewareService<S>
25where
26 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
27 S::Future: 'static,
28 B: 'static,
29{
30 type Response = ServiceResponse<EitherBody<B, BoxBody>>;
31 type Error = Error;
32 type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
33 fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
34 self.service.poll_ready(ctx)
35 }
36
37 fn call(&self, req: ServiceRequest) -> Self::Future {
38 use crate::middleware::actix::mw_router::intercept_internal_routes;
39 use crate::env::{Env, EnvStatus};
40 let port = req.connection_info().host().split(':').nth(1).unwrap_or("unknown").to_string();
41 if self.config.manual_mode {
42 log::info!("this.env [{}] ActixMiddleware (manual): {} {}", port, req.method(), req.path());
43 } else {
44 log::info!("this.env [{}] ActixMiddleware: {} {}", port, req.method(), req.path());
45 }
46
47 let conn = self.conn.clone();
48 req.extensions_mut().insert(conn.clone());
49
50 if let Some(resp) = intercept_internal_routes(&req) {
51 return Box::pin(async {
52 Ok(req.into_response(resp.map_into_right_body()))
53 });
54 }
55
56 let svc = Rc::clone(&self.service);
57 let config = self.config.clone();
58 let env_request_result = parse_env_request(req.request());
59 let accepts_html = req
60 .headers()
61 .get("Accept")
62 .and_then(|v| v.to_str().ok())
63 .map(|s| s.contains("text/html"))
64 .unwrap_or(false);
65
66 if config.manual_mode {
67 if let Some(env_request) = &env_request_result {
68 match Env::resolve(env_request, &conn) {
69 status => log::info!("this.env status (manual mode): {:?}", status),
70 }
71 }
72 return Box::pin(async move {
73 let res = svc.call(req).await?;
74 Ok(res.map_into_left_body())
75 });
76 }
77
78 let decision_status = match &env_request_result {
79 Some(env_request) => {
80 if let Some(EnvRequest::Http(http)) = &env_request_result {
81 log::debug!("this.env request: [{}] {} {}", http.host, http.method, http.path);
82 }
83 EnvStatus::Approved {
85 env_request: crate::middleware::env_request::EnvRequestInfo::from(env_request),
86 }
87 }
88 None => {
89 EnvStatus::Approved {
91 env_request: crate::middleware::env_request::EnvRequestInfo::from(
92 &EnvRequest::Cli(Default::default())
93 ),
94 }
95 }
96 };
97
98 log::info!("this.env decision: {:?}", match &decision_status {
99 EnvStatus::Approved { .. } => "Approved",
100 EnvStatus::PendingApproval { .. } => "PendingApproval",
101 EnvStatus::Blocked { .. } => "Blocked",
102 });
103 let svc_clone = Rc::clone(&svc);
104 let req_clone = req;
105 Box::pin(async move {
107 if let Some(_env_request) = env_request_result {
108 match decision_status {
109EnvStatus::Approved { env_request: _ } => {
114 req_clone.extensions_mut().insert(decision_status.clone());
116 let res = svc_clone.call(req_clone).await?;
117 return Ok(res.map_into_left_body());
118 }
119EnvStatus::PendingApproval { env_request: env_req, reason: _ } => {
124 let info = crate::middleware::env_request::EnvRequestInfo::from(env_req);
126 let json_body = json!({
127 "status": "pending",
128 "message": "Awaiting user approval",
129 "env_request": info
130 }).to_string();
131 let resp = HttpResponse::build(StatusCode::UNAUTHORIZED)
132 .content_type("application/json")
133 .body(json_body);
134 return Ok(req_clone.into_response(resp.map_into_right_body()));
135 }
136EnvStatus::Blocked { env_request: _, reason: _ } => {
141 let wants_html = accepts_html || config.prefer_html;
142 if wants_html {
143 let resp = HttpResponse::build(StatusCode::FORBIDDEN)
144 .content_type("text/html")
145 .body(include_str!("../../html/blocked.html"));
146 return Ok(req_clone.into_response(resp.map_into_right_body()));
147 } else {
148 let resp = HttpResponse::build(StatusCode::FORBIDDEN)
149 .content_type("text/plain")
150 .body("Blocked");
151 return Ok(req_clone.into_response(resp.map_into_right_body()));
152 }
153 }
154 }
155 }
156 req_clone.extensions_mut().insert(decision_status.clone());
158 let res = svc_clone.call(req_clone).await?;
159 Ok(res.map_into_left_body())
160 })
161 }
162}