1use crate::core::AuthIdentity;
6use crate::internal_user::InternalUser;
7use crate::sessions::{Session, backends::SessionBackend};
8use crate::{AuthBackend, AuthenticationError};
9use reinhardt_http::Request;
10use sha2::{Digest, Sha256};
11use std::sync::Arc;
12use subtle::ConstantTimeEq;
13
14#[async_trait::async_trait]
18pub trait RestAuthentication: Send + Sync {
19 async fn authenticate(
21 &self,
22 request: &Request,
23 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError>;
24}
25
26#[derive(Debug, Clone)]
28pub struct BasicAuthConfig {
29 pub realm: String,
31}
32
33impl Default for BasicAuthConfig {
34 fn default() -> Self {
35 Self {
36 realm: "api".to_string(),
37 }
38 }
39}
40
41#[derive(Debug, Clone)]
43pub struct SessionAuthConfig {
44 pub cookie_name: String,
46 pub enforce_csrf: bool,
48}
49
50impl Default for SessionAuthConfig {
51 fn default() -> Self {
52 Self {
53 cookie_name: "sessionid".to_string(),
54 enforce_csrf: true,
55 }
56 }
57}
58
59#[derive(Debug, Clone)]
61pub struct TokenAuthConfig {
62 pub header_name: String,
64 pub prefix: String,
66}
67
68impl Default for TokenAuthConfig {
69 fn default() -> Self {
70 Self {
71 header_name: "Authorization".to_string(),
72 prefix: "Token".to_string(),
73 }
74 }
75}
76
77pub struct CompositeAuthentication {
93 backends: Vec<Arc<dyn AuthBackend>>,
94}
95
96impl CompositeAuthentication {
97 pub fn new() -> Self {
107 Self {
108 backends: Vec::new(),
109 }
110 }
111
112 pub fn with_backend<B: AuthBackend + 'static>(mut self, backend: B) -> Self {
126 self.backends.push(Arc::new(backend));
127 self
128 }
129
130 pub fn with_backends(mut self, backends: Vec<Arc<dyn AuthBackend>>) -> Self {
132 self.backends.extend(backends);
133 self
134 }
135}
136
137impl Default for CompositeAuthentication {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143#[async_trait::async_trait]
144impl RestAuthentication for CompositeAuthentication {
145 async fn authenticate(
156 &self,
157 request: &Request,
158 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
159 let mut errors: Vec<AuthenticationError> = Vec::new();
161
162 for backend in &self.backends {
163 match backend.authenticate(request).await {
164 Ok(Some(user)) => return Ok(Some(user)),
165 Ok(None) => continue,
166 Err(e) => {
167 tracing::warn!("Authentication backend error occurred");
168 tracing::debug!(error = %e, "Authentication backend error details");
169 errors.push(e);
170 }
171 }
172 }
173
174 if !errors.is_empty() && self.backends.len() == errors.len() {
177 return Err(errors.into_iter().next().expect("errors is non-empty"));
178 }
179
180 Ok(None)
181 }
182}
183
184#[async_trait::async_trait]
185impl AuthBackend for CompositeAuthentication {
186 async fn authenticate(
187 &self,
188 request: &Request,
189 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
190 <Self as RestAuthentication>::authenticate(self, request).await
191 }
192
193 async fn get_user(
194 &self,
195 user_id: &str,
196 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
197 let mut errors: Vec<AuthenticationError> = Vec::new();
199
200 for backend in &self.backends {
201 match backend.get_user(user_id).await {
202 Ok(Some(user)) => return Ok(Some(user)),
203 Ok(None) => continue,
204 Err(e) => {
205 tracing::warn!("get_user backend error occurred");
206 tracing::debug!(error = %e, "get_user backend error details");
207 errors.push(e);
208 }
209 }
210 }
211
212 if !errors.is_empty() && self.backends.len() == errors.len() {
215 return Err(errors.into_iter().next().expect("errors is non-empty"));
216 }
217
218 Ok(None)
219 }
220}
221
222pub struct TokenAuthentication {
224 tokens: std::collections::HashMap<String, String>,
226 token_index: std::collections::HashMap<[u8; 32], (String, String)>,
229 config: TokenAuthConfig,
231}
232
233impl TokenAuthentication {
234 pub fn new() -> Self {
244 Self {
245 tokens: std::collections::HashMap::new(),
246 token_index: std::collections::HashMap::new(),
247 config: TokenAuthConfig::default(),
248 }
249 }
250
251 pub fn with_config(config: TokenAuthConfig) -> Self {
253 Self {
254 tokens: std::collections::HashMap::new(),
255 token_index: std::collections::HashMap::new(),
256 config,
257 }
258 }
259
260 pub fn add_token(&mut self, token: impl Into<String>, user_id: impl Into<String>) {
264 let token = token.into();
265 let user_id = user_id.into();
266 let digest: [u8; 32] = Sha256::digest(token.as_bytes()).into();
267 self.token_index
268 .insert(digest, (token.clone(), user_id.clone()));
269 self.tokens.insert(token, user_id);
270 }
271
272 fn find_token_constant_time(&self, candidate: &str) -> Option<&String> {
278 let digest: [u8; 32] = Sha256::digest(candidate.as_bytes()).into();
279 if let Some((stored_token, user_id)) = self.token_index.get(&digest) {
280 let candidate_bytes = candidate.as_bytes();
281 let stored_bytes = stored_token.as_bytes();
282 if candidate_bytes.len() == stored_bytes.len()
283 && bool::from(candidate_bytes.ct_eq(stored_bytes))
284 {
285 Some(user_id)
286 } else {
287 None
288 }
289 } else {
290 None
291 }
292 }
293}
294
295impl Default for TokenAuthentication {
296 fn default() -> Self {
297 Self::new()
298 }
299}
300
301#[async_trait::async_trait]
302impl RestAuthentication for TokenAuthentication {
303 async fn authenticate(
304 &self,
305 request: &Request,
306 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
307 let auth_header = request
308 .headers
309 .get(&self.config.header_name)
310 .and_then(|h| h.to_str().ok());
311
312 if let Some(header) = auth_header {
313 let prefix = format!("{} ", self.config.prefix);
314 if let Some(token) = header.strip_prefix(&prefix)
315 && let Some(user_id) = self.find_token_constant_time(token)
316 {
317 let id = uuid::Uuid::parse_str(user_id).unwrap_or_else(|_| {
319 uuid::Uuid::new_v5(&crate::USER_ID_NAMESPACE, user_id.as_bytes())
320 });
321 return Ok(Some(Box::new(InternalUser {
322 id,
323 username: user_id.clone(),
324 email: String::new(),
325 is_active: true,
326 is_admin: false,
327 is_staff: false,
328 is_superuser: false,
329 })));
330 }
331 }
332
333 Ok(None)
334 }
335}
336
337#[async_trait::async_trait]
338impl AuthBackend for TokenAuthentication {
339 async fn authenticate(
340 &self,
341 request: &Request,
342 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
343 <Self as RestAuthentication>::authenticate(self, request).await
344 }
345
346 async fn get_user(
347 &self,
348 user_id: &str,
349 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
350 if self.tokens.values().any(|id| id == user_id) {
351 let id = uuid::Uuid::parse_str(user_id).unwrap_or_else(|_| {
353 uuid::Uuid::new_v5(&crate::USER_ID_NAMESPACE, user_id.as_bytes())
354 });
355 Ok(Some(Box::new(InternalUser {
356 id,
357 username: user_id.to_string(),
358 email: String::new(),
359 is_active: true,
360 is_admin: false,
361 is_staff: false,
362 is_superuser: false,
363 })))
364 } else {
365 Ok(None)
366 }
367 }
368}
369
370pub struct RemoteUserAuthentication {
372 header_name: String,
374}
375
376impl RemoteUserAuthentication {
377 pub fn new() -> Self {
379 Self {
380 header_name: "REMOTE_USER".to_string(),
381 }
382 }
383
384 pub fn with_header(mut self, header: impl Into<String>) -> Self {
386 self.header_name = header.into();
387 self
388 }
389}
390
391impl Default for RemoteUserAuthentication {
392 fn default() -> Self {
393 Self::new()
394 }
395}
396
397#[async_trait::async_trait]
398impl RestAuthentication for RemoteUserAuthentication {
399 async fn authenticate(
400 &self,
401 request: &Request,
402 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
403 let header_value = request
404 .headers
405 .get(&self.header_name)
406 .and_then(|v| v.to_str().ok());
407
408 if let Some(username) = header_value
409 && !username.is_empty()
410 {
411 return Ok(Some(Box::new(InternalUser {
412 id: uuid::Uuid::new_v5(&crate::USER_ID_NAMESPACE, username.as_bytes()),
413 username: username.to_string(),
414 email: String::new(),
415 is_active: true,
416 is_admin: false,
417 is_staff: false,
418 is_superuser: false,
419 })));
420 }
421
422 Ok(None)
423 }
424}
425
426#[async_trait::async_trait]
427impl AuthBackend for RemoteUserAuthentication {
428 async fn authenticate(
429 &self,
430 request: &Request,
431 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
432 <Self as RestAuthentication>::authenticate(self, request).await
433 }
434
435 async fn get_user(
436 &self,
437 _user_id: &str,
438 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
439 Ok(None)
440 }
441}
442
443#[derive(Clone)]
445pub struct SessionAuthentication<B: SessionBackend> {
446 config: SessionAuthConfig,
448 session_backend: B,
450}
451
452impl<B: SessionBackend> SessionAuthentication<B> {
453 pub fn new(session_backend: B) -> Self {
455 Self {
456 config: SessionAuthConfig::default(),
457 session_backend,
458 }
459 }
460
461 pub fn with_config(config: SessionAuthConfig, session_backend: B) -> Self {
463 Self {
464 config,
465 session_backend,
466 }
467 }
468}
469
470impl<B: SessionBackend + Default> Default for SessionAuthentication<B> {
471 fn default() -> Self {
472 Self::new(B::default())
473 }
474}
475
476#[async_trait::async_trait]
477impl<B: SessionBackend> RestAuthentication for SessionAuthentication<B> {
478 async fn authenticate(
479 &self,
480 request: &Request,
481 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
482 let cookie_header = request.headers.get("Cookie").and_then(|h| h.to_str().ok());
484
485 if let Some(cookies) = cookie_header {
486 for cookie in cookies.split(';') {
487 let parts: Vec<&str> = cookie.trim().splitn(2, '=').collect();
488 if parts.len() == 2 && parts[0] == self.config.cookie_name {
489 let session_key = parts[1];
490
491 let mut session =
493 Session::from_key(self.session_backend.clone(), session_key.to_string())
494 .await
495 .map_err(|_| AuthenticationError::SessionExpired)?;
496
497 let user_id: String = match session.get("_auth_user_id") {
499 Ok(Some(id)) => id,
500 Ok(None) => return Ok(None), Err(_) => return Err(AuthenticationError::SessionExpired),
502 };
503
504 let username: String = session
506 .get("_auth_user_name")
507 .ok()
508 .flatten()
509 .unwrap_or_else(|| user_id.clone());
510 let email: String = session
511 .get("_auth_user_email")
512 .ok()
513 .flatten()
514 .unwrap_or_default();
515 let is_active: bool = session
516 .get("_auth_user_is_active")
517 .ok()
518 .flatten()
519 .unwrap_or(true);
520 let is_admin: bool = session
521 .get("_auth_user_is_admin")
522 .ok()
523 .flatten()
524 .unwrap_or(false);
525 let is_staff: bool = session
526 .get("_auth_user_is_staff")
527 .ok()
528 .flatten()
529 .unwrap_or(false);
530 let is_superuser: bool = session
531 .get("_auth_user_is_superuser")
532 .ok()
533 .flatten()
534 .unwrap_or(false);
535
536 let user = InternalUser {
538 id: uuid::Uuid::parse_str(&user_id)
539 .map_err(|_| AuthenticationError::InvalidCredentials)?,
540 username,
541 email,
542 is_active,
543 is_admin,
544 is_staff,
545 is_superuser,
546 };
547
548 return Ok(Some(Box::new(user)));
549 }
550 }
551 }
552
553 Ok(None)
554 }
555}
556
557#[async_trait::async_trait]
558impl<B: SessionBackend> AuthBackend for SessionAuthentication<B> {
559 async fn authenticate(
560 &self,
561 request: &Request,
562 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
563 <Self as RestAuthentication>::authenticate(self, request).await
564 }
565
566 async fn get_user(
567 &self,
568 _user_id: &str,
569 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
570 Ok(None)
575 }
576}
577
578#[cfg(test)]
579mod tests {
580 use super::*;
581 use crate::AuthenticationError;
582 #[cfg(feature = "jwt")]
583 use crate::basic::BasicAuthentication;
584 use bytes::Bytes;
585 use hyper::{HeaderMap, Method};
586 use rstest::rstest;
587 use std::sync::Mutex;
588
589 #[tokio::test]
590 #[cfg(feature = "jwt")]
591 async fn test_composite_authentication() {
592 let mut basic = BasicAuthentication::new();
593 basic.add_user("user1", "pass1");
594
595 let composite = CompositeAuthentication::new().with_backend(basic);
596
597 let mut headers = HeaderMap::new();
599 headers.insert(
600 "Authorization",
601 "Basic dXNlcjE6cGFzczE=".parse().unwrap(), );
603
604 let request = Request::builder()
605 .method(Method::GET)
606 .uri("/")
607 .headers(headers)
608 .body(Bytes::new())
609 .build()
610 .unwrap();
611
612 let result = RestAuthentication::authenticate(&composite, &request)
613 .await
614 .unwrap();
615 assert!(result.is_some());
616 assert!(result.unwrap().is_authenticated());
617 }
618
619 #[tokio::test]
620 async fn test_token_authentication() {
621 let mut auth = TokenAuthentication::new();
622 auth.add_token("secret_token", "alice");
623
624 let mut headers = HeaderMap::new();
625 headers.insert("Authorization", "Token secret_token".parse().unwrap());
626
627 let request = Request::builder()
628 .method(Method::GET)
629 .uri("/")
630 .headers(headers)
631 .body(Bytes::new())
632 .build()
633 .unwrap();
634
635 let result = RestAuthentication::authenticate(&auth, &request)
636 .await
637 .unwrap();
638 let user = result.expect("token authentication should succeed");
639 assert!(user.is_authenticated());
640 let expected_id = uuid::Uuid::new_v5(&crate::USER_ID_NAMESPACE, b"alice").to_string();
641 assert_eq!(user.id(), expected_id);
642 }
643
644 #[tokio::test]
645 async fn test_remote_user_authentication() {
646 let auth = RemoteUserAuthentication::new();
647
648 let mut headers = HeaderMap::new();
649 headers.insert("REMOTE_USER", "bob".parse().unwrap());
650
651 let request = Request::builder()
652 .method(Method::GET)
653 .uri("/")
654 .headers(headers)
655 .body(Bytes::new())
656 .build()
657 .unwrap();
658
659 let result = RestAuthentication::authenticate(&auth, &request)
660 .await
661 .unwrap();
662 let user = result.expect("remote user authentication should succeed");
663 assert!(user.is_authenticated());
664 let expected_id = uuid::Uuid::new_v5(&crate::USER_ID_NAMESPACE, b"bob").to_string();
665 assert_eq!(user.id(), expected_id);
666 }
667
668 #[tokio::test]
669 async fn test_session_authentication() {
670 use crate::sessions::InMemorySessionBackend;
671 use crate::sessions::Session;
672
673 let session_backend = InMemorySessionBackend::new();
674
675 let mut session = Session::new(session_backend.clone());
677 session
678 .set("_auth_user_id", "550e8400-e29b-41d4-a716-446655440000")
679 .unwrap();
680 session.set("_auth_user_name", "testuser").unwrap();
681 session.set("_auth_user_email", "test@example.com").unwrap();
682 session.set("_auth_user_is_active", true).unwrap();
683 session.save().await.unwrap();
684
685 let session_key = session.get_or_create_key().to_string();
687
688 let auth = SessionAuthentication::new(session_backend);
689
690 let mut headers = HeaderMap::new();
691 let cookie_value = format!("sessionid={}", session_key);
692 headers.insert("Cookie", cookie_value.parse().unwrap());
693
694 let request = Request::builder()
695 .method(Method::GET)
696 .uri("/")
697 .headers(headers)
698 .body(Bytes::new())
699 .build()
700 .unwrap();
701
702 let result = RestAuthentication::authenticate(&auth, &request)
703 .await
704 .unwrap();
705 assert!(result.is_some());
706
707 let user = result.unwrap();
709 assert_eq!(user.id(), "550e8400-e29b-41d4-a716-446655440000");
710 assert!(user.is_authenticated());
711 }
712
713 #[rstest]
714 #[tokio::test]
715 async fn test_token_auth_same_username_produces_same_id() {
716 let mut auth = TokenAuthentication::new();
718 auth.add_token("token1", "alice");
719 auth.add_token("token2", "alice");
720
721 let mut headers1 = HeaderMap::new();
722 headers1.insert("Authorization", "Token token1".parse().unwrap());
723 let request1 = Request::builder()
724 .method(Method::GET)
725 .uri("/")
726 .headers(headers1)
727 .body(Bytes::new())
728 .build()
729 .unwrap();
730
731 let mut headers2 = HeaderMap::new();
732 headers2.insert("Authorization", "Token token2".parse().unwrap());
733 let request2 = Request::builder()
734 .method(Method::GET)
735 .uri("/")
736 .headers(headers2)
737 .body(Bytes::new())
738 .build()
739 .unwrap();
740
741 let user1 = RestAuthentication::authenticate(&auth, &request1)
743 .await
744 .unwrap()
745 .unwrap();
746 let user2 = RestAuthentication::authenticate(&auth, &request2)
747 .await
748 .unwrap()
749 .unwrap();
750
751 assert_eq!(
753 user1.id(),
754 user2.id(),
755 "same username must produce the same UUID"
756 );
757 }
758
759 #[rstest]
760 #[tokio::test]
761 async fn test_token_auth_user_has_default_privilege_flags() {
762 let mut auth = TokenAuthentication::new();
764 auth.add_token("secret_token", "alice");
765
766 let mut headers = HeaderMap::new();
767 headers.insert("Authorization", "Token secret_token".parse().unwrap());
768 let request = Request::builder()
769 .method(Method::GET)
770 .uri("/")
771 .headers(headers)
772 .body(Bytes::new())
773 .build()
774 .unwrap();
775
776 let user = RestAuthentication::authenticate(&auth, &request)
778 .await
779 .unwrap()
780 .unwrap();
781
782 assert!(user.is_authenticated());
784 assert!(!user.is_admin());
785 }
786
787 #[rstest]
788 #[tokio::test]
789 async fn test_remote_user_auth_same_username_produces_same_id() {
790 let auth = RemoteUserAuthentication::new();
792
793 let mut headers1 = HeaderMap::new();
794 headers1.insert("REMOTE_USER", "bob".parse().unwrap());
795 let request1 = Request::builder()
796 .method(Method::GET)
797 .uri("/")
798 .headers(headers1)
799 .body(Bytes::new())
800 .build()
801 .unwrap();
802
803 let mut headers2 = HeaderMap::new();
804 headers2.insert("REMOTE_USER", "bob".parse().unwrap());
805 let request2 = Request::builder()
806 .method(Method::GET)
807 .uri("/")
808 .headers(headers2)
809 .body(Bytes::new())
810 .build()
811 .unwrap();
812
813 let user1 = RestAuthentication::authenticate(&auth, &request1)
815 .await
816 .unwrap()
817 .unwrap();
818 let user2 = RestAuthentication::authenticate(&auth, &request2)
819 .await
820 .unwrap()
821 .unwrap();
822
823 assert_eq!(
825 user1.id(),
826 user2.id(),
827 "same username must produce the same UUID"
828 );
829 }
830
831 #[rstest]
832 #[tokio::test]
833 async fn test_token_auth_get_user_same_id_produces_same_uuid() {
834 let mut auth = TokenAuthentication::new();
836 auth.add_token("secret_token", "alice");
837
838 let user1 = auth.get_user("alice").await.unwrap().unwrap();
840 let user2 = auth.get_user("alice").await.unwrap().unwrap();
841
842 assert_eq!(
844 user1.id(),
845 user2.id(),
846 "same user_id must produce the same UUID via get_user"
847 );
848 }
849
850 #[rstest]
851 #[tokio::test]
852 async fn test_token_auth_unknown_token_returns_none() {
853 let mut auth = TokenAuthentication::new();
855 auth.add_token("known_token", "alice");
856
857 let mut headers = HeaderMap::new();
858 headers.insert("Authorization", "Token unknown_token".parse().unwrap());
859 let request = Request::builder()
860 .method(Method::GET)
861 .uri("/")
862 .headers(headers)
863 .body(Bytes::new())
864 .build()
865 .unwrap();
866
867 let result = RestAuthentication::authenticate(&auth, &request)
869 .await
870 .unwrap();
871
872 assert!(result.is_none());
874 }
875
876 #[rstest]
877 #[tokio::test]
878 async fn test_token_auth_get_user_unknown_returns_none() {
879 let mut auth = TokenAuthentication::new();
881 auth.add_token("secret_token", "alice");
882
883 let result = auth.get_user("nonexistent_user").await.unwrap();
885
886 assert!(result.is_none());
888 }
889
890 #[tokio::test]
891 async fn test_custom_token_config() {
892 let config = TokenAuthConfig {
893 header_name: "X-API-Key".to_string(),
894 prefix: "Bearer".to_string(),
895 };
896
897 let mut auth = TokenAuthentication::with_config(config);
898 auth.add_token("my_token", "charlie");
899
900 let mut headers = HeaderMap::new();
901 headers.insert("X-API-Key", "Bearer my_token".parse().unwrap());
902
903 let request = Request::builder()
904 .method(Method::GET)
905 .uri("/")
906 .headers(headers)
907 .body(Bytes::new())
908 .build()
909 .unwrap();
910
911 let result = RestAuthentication::authenticate(&auth, &request)
912 .await
913 .unwrap();
914 let user = result.expect("custom token authentication should succeed");
915 assert!(user.is_authenticated());
916 let expected_id = uuid::Uuid::new_v5(&crate::USER_ID_NAMESPACE, b"charlie").to_string();
917 assert_eq!(user.id(), expected_id);
918 }
919
920 struct MockAuthBackend {
921 auth_result: Mutex<Option<Result<Option<Box<dyn AuthIdentity>>, AuthenticationError>>>,
922 get_user_result: Mutex<Option<Result<Option<Box<dyn AuthIdentity>>, AuthenticationError>>>,
923 }
924
925 impl MockAuthBackend {
926 fn new(
927 auth_result: Result<Option<Box<dyn AuthIdentity>>, AuthenticationError>,
928 get_user_result: Result<Option<Box<dyn AuthIdentity>>, AuthenticationError>,
929 ) -> Self {
930 Self {
931 auth_result: Mutex::new(Some(auth_result)),
932 get_user_result: Mutex::new(Some(get_user_result)),
933 }
934 }
935 }
936
937 #[async_trait::async_trait]
938 impl AuthBackend for MockAuthBackend {
939 async fn authenticate(
940 &self,
941 _request: &Request,
942 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
943 self.auth_result.lock().unwrap().take().unwrap_or(Ok(None))
944 }
945
946 async fn get_user(
947 &self,
948 _user_id: &str,
949 ) -> Result<Option<Box<dyn AuthIdentity>>, AuthenticationError> {
950 self.get_user_result
951 .lock()
952 .unwrap()
953 .take()
954 .unwrap_or(Ok(None))
955 }
956 }
957
958 #[rstest]
959 #[tokio::test]
960 async fn test_composite_auth_all_backends_error() {
961 let composite = CompositeAuthentication::new()
963 .with_backend(MockAuthBackend::new(
964 Err(AuthenticationError::DatabaseError("db1 down".to_string())),
965 Err(AuthenticationError::DatabaseError("db1 down".to_string())),
966 ))
967 .with_backend(MockAuthBackend::new(
968 Err(AuthenticationError::DatabaseError("db2 down".to_string())),
969 Err(AuthenticationError::DatabaseError("db2 down".to_string())),
970 ));
971
972 let request = Request::builder()
973 .method(Method::GET)
974 .uri("/")
975 .body(Bytes::new())
976 .build()
977 .unwrap();
978
979 let result = RestAuthentication::authenticate(&composite, &request).await;
981
982 assert!(result.is_err());
984 }
985
986 #[rstest]
987 #[tokio::test]
988 async fn test_composite_auth_one_error_one_none() {
989 let composite = CompositeAuthentication::new()
994 .with_backend(MockAuthBackend::new(
995 Err(AuthenticationError::DatabaseError("db down".to_string())),
996 Err(AuthenticationError::DatabaseError("db down".to_string())),
997 ))
998 .with_backend(MockAuthBackend::new(Ok(None), Ok(None)));
999
1000 let request = Request::builder()
1001 .method(Method::GET)
1002 .uri("/")
1003 .body(Bytes::new())
1004 .build()
1005 .unwrap();
1006
1007 let result = RestAuthentication::authenticate(&composite, &request).await;
1009
1010 assert!(result.is_ok());
1012 assert!(result.unwrap().is_none());
1013 }
1014
1015 #[rstest]
1016 #[tokio::test]
1017 async fn test_composite_get_user_all_error() {
1018 let composite = CompositeAuthentication::new()
1020 .with_backend(MockAuthBackend::new(
1021 Ok(None),
1022 Err(AuthenticationError::DatabaseError("db1 down".to_string())),
1023 ))
1024 .with_backend(MockAuthBackend::new(
1025 Ok(None),
1026 Err(AuthenticationError::DatabaseError("db2 down".to_string())),
1027 ));
1028
1029 let result = AuthBackend::get_user(&composite, "some_user").await;
1031
1032 assert!(result.is_err());
1034 }
1035}