reinhardt_auth/
remote_user.rs1use crate::{AuthenticationBackend, AuthenticationError, SimpleUser, User};
7use reinhardt_http::Request;
8use uuid::Uuid;
9
10pub struct RemoteUserAuthentication {
50 header_name: String,
52 force_logout: bool,
54}
55
56impl RemoteUserAuthentication {
57 pub fn new() -> Self {
67 Self {
68 header_name: "REMOTE_USER".to_string(),
69 force_logout: true,
70 }
71 }
72
73 pub fn with_header(mut self, header: impl Into<String>) -> Self {
84 self.header_name = header.into();
85 self
86 }
87
88 pub fn force_logout(mut self, force: bool) -> Self {
90 self.force_logout = force;
91 self
92 }
93}
94
95impl Default for RemoteUserAuthentication {
96 fn default() -> Self {
97 Self::new()
98 }
99}
100
101#[async_trait::async_trait]
102impl AuthenticationBackend for RemoteUserAuthentication {
103 async fn authenticate(
104 &self,
105 request: &Request,
106 ) -> Result<Option<Box<dyn User>>, AuthenticationError> {
107 let header_value = request
109 .headers
110 .get(&self.header_name)
111 .and_then(|v| v.to_str().ok());
112
113 match header_value {
114 Some(username) if !username.is_empty() => {
115 Ok(Some(Box::new(SimpleUser {
117 id: Uuid::new_v4(),
118 username: username.to_string(),
119 email: format!("{}@example.com", username),
120 is_active: true,
121 is_admin: false,
122 is_staff: false,
123 is_superuser: false,
124 })))
125 }
126 _ => {
127 Ok(None)
129 }
130 }
131 }
132
133 async fn get_user(&self, _user_id: &str) -> Result<Option<Box<dyn User>>, AuthenticationError> {
134 Ok(None)
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use bytes::Bytes;
144 use hyper::{HeaderMap, Method};
145
146 #[tokio::test]
147 async fn test_remote_user_with_header() {
148 let auth = RemoteUserAuthentication::new();
149 let mut headers = HeaderMap::new();
150 headers.insert("REMOTE_USER", "testuser".parse().unwrap());
151
152 let request = Request::builder()
153 .method(Method::GET)
154 .uri("/")
155 .headers(headers)
156 .body(Bytes::new())
157 .build()
158 .unwrap();
159
160 let result = auth.authenticate(&request).await.unwrap();
161 assert!(result.is_some());
162 assert_eq!(result.unwrap().get_username(), "testuser");
163 }
164
165 #[tokio::test]
166 async fn test_remote_user_without_header() {
167 let auth = RemoteUserAuthentication::new();
168 let request = Request::builder()
169 .method(Method::GET)
170 .uri("/")
171 .body(Bytes::new())
172 .build()
173 .unwrap();
174
175 let result = auth.authenticate(&request).await.unwrap();
176 assert!(result.is_none());
177 }
178
179 #[tokio::test]
180 async fn test_custom_header() {
181 let auth = RemoteUserAuthentication::new().with_header("X-Auth-User");
182 let mut headers = HeaderMap::new();
183 headers.insert("X-Auth-User", "alice".parse().unwrap());
184
185 let request = Request::builder()
186 .method(Method::GET)
187 .uri("/")
188 .headers(headers)
189 .body(Bytes::new())
190 .build()
191 .unwrap();
192
193 let result = auth.authenticate(&request).await.unwrap();
194 assert!(result.is_some());
195 assert_eq!(result.unwrap().get_username(), "alice");
196 }
197
198 #[tokio::test]
199 async fn test_empty_header() {
200 let auth = RemoteUserAuthentication::new();
201 let mut headers = HeaderMap::new();
202 headers.insert("REMOTE_USER", "".parse().unwrap());
203
204 let request = Request::builder()
205 .method(Method::GET)
206 .uri("/")
207 .headers(headers)
208 .body(Bytes::new())
209 .build()
210 .unwrap();
211
212 let result = auth.authenticate(&request).await.unwrap();
213 assert!(result.is_none());
214 }
215}