actix_security_core/http/security/
middleware.rs1use std::rc::Rc;
7
8use actix_service::{Service, Transform};
9use actix_web::body::EitherBody;
10use actix_web::dev::{ServiceRequest, ServiceResponse};
11use actix_web::{Error, HttpMessage};
12use futures_util::future::{ok, LocalBoxFuture, Ready};
13
14use crate::http::security::config::{Authenticator, Authorizer};
15
16#[cfg(feature = "audit")]
18fn get_client_ip(req: &ServiceRequest) -> String {
19 req.connection_info()
20 .realip_remote_addr()
21 .unwrap_or("-")
22 .to_string()
23}
24
25pub struct SecurityTransform<Auth, Autho> {
39 authenticator: Option<fn() -> Auth>,
40 authorizer: Option<fn() -> Autho>,
41}
42
43impl<Auth, Autho> SecurityTransform<Auth, Autho> {
44 pub fn new() -> Self {
45 SecurityTransform {
46 authorizer: None,
47 authenticator: None,
48 }
49 }
50
51 pub fn config_authenticator(mut self, authenticator: fn() -> Auth) -> Self {
52 self.authenticator = Some(authenticator);
53 self
54 }
55
56 pub fn config_authorizer(mut self, authorizer: fn() -> Autho) -> Self {
57 self.authorizer = Some(authorizer);
58 self
59 }
60}
61
62impl<Auth, Autho> Default for SecurityTransform<Auth, Autho> {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68impl<S, B, Auth, Autho> Transform<S, ServiceRequest> for SecurityTransform<Auth, Autho>
69where
70 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
71 S::Future: 'static,
72 B: 'static,
73 Auth: Authenticator + 'static,
74 Autho: Authorizer<B> + 'static,
75{
76 type Response = ServiceResponse<EitherBody<B>>;
77 type Error = Error;
78 type Transform = SecurityService<Auth, Autho, S>;
79 type InitError = ();
80 type Future = Ready<Result<Self::Transform, Self::InitError>>;
81
82 fn new_transform(&self, service: S) -> Self::Future {
83 let authenticator = self.authenticator.map(|f| f());
84 let authorizer = self.authorizer.map(|f| f());
85
86 ok(SecurityService {
87 authenticator,
88 authorizer,
89 service: Rc::new(service),
90 })
91 }
92}
93
94pub struct SecurityService<Auth, Autho, S> {
99 authenticator: Option<Auth>,
100 authorizer: Option<Autho>,
101 service: Rc<S>,
102}
103
104impl<Auth, Autho, S, B> Service<ServiceRequest> for SecurityService<Auth, Autho, S>
105where
106 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
107 S::Future: 'static,
108 B: 'static,
109 Auth: Authenticator,
110 Autho: Authorizer<B>,
111{
112 type Response = ServiceResponse<EitherBody<B>>;
113 type Error = Error;
114 type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
115
116 actix_web::dev::forward_ready!(service);
117
118 fn call(&self, req: ServiceRequest) -> Self::Future {
119 let service = Rc::clone(&self.service);
120
121 let user = self
123 .authenticator
124 .as_ref()
125 .and_then(|auth| auth.get_user(&req));
126
127 #[cfg(feature = "audit")]
129 {
130 let path = req.path().to_string();
131 let method = req.method().to_string();
132 let ip = get_client_ip(&req);
133
134 if let Some(ref u) = user {
135 tracing::info!(
136 target: "actix_security::audit",
137 event_type = "AUTHENTICATION_SUCCESS",
138 user = %u.get_username(),
139 ip = %ip,
140 path = %path,
141 method = %method,
142 "User authenticated successfully"
143 );
144 } else if self.authenticator.is_some() {
145 tracing::debug!(
146 target: "actix_security::audit",
147 event_type = "AUTHENTICATION_ANONYMOUS",
148 ip = %ip,
149 path = %path,
150 method = %method,
151 "Anonymous request (no credentials provided)"
152 );
153 }
154 }
155
156 if let Some(ref u) = user {
159 req.extensions_mut().insert(u.clone());
160 }
161
162 if let Some(authorizer) = &self.authorizer {
164 let next = move |req: ServiceRequest| -> LocalBoxFuture<'static, Result<ServiceResponse<B>, Error>> {
166 let fut = service.call(req);
167 Box::pin(fut)
168 };
169
170 authorizer.process(req, user.as_ref(), next)
171 } else {
172 let fut = service.call(req);
174 Box::pin(async move {
175 let res = fut.await?;
176 Ok(res.map_into_left_body())
177 })
178 }
179 }
180}