1use axum::extract::Request;
89use axum::middleware::Next;
90use axum::response::{IntoResponse, Response};
91use std::sync::Arc;
92
93use sz_rust_http_facade::error::{BaseException, ErrorCode};
94use sz_rust_middleware_facade::auth::{base_exception_to_response, AuthenticatedUser};
95
96#[derive(Debug, Clone)]
119pub struct GuardError {
120 pub code: ErrorCode,
122 pub msg: String,
124}
125
126impl GuardError {
127 pub fn new(code: ErrorCode, msg: impl Into<String>) -> Self {
129 Self {
130 code,
131 msg: msg.into(),
132 }
133 }
134
135 pub fn not_login(msg: impl Into<String>) -> Self {
137 Self::new(ErrorCode::NotLogin, msg)
138 }
139
140 pub fn forbidden(msg: impl Into<String>) -> Self {
142 Self::new(ErrorCode::Forbidden, msg)
143 }
144
145 pub fn user_disabled(msg: impl Into<String>) -> Self {
147 Self::new(ErrorCode::UserDisabled, msg)
148 }
149}
150
151impl IntoResponse for GuardError {
152 fn into_response(self) -> Response {
158 let exc = BaseException::new(self.code, self.msg);
159 base_exception_to_response(exc)
160 }
161}
162
163impl From<GuardError> for BaseException {
164 fn from(err: GuardError) -> Self {
165 BaseException::new(err.code, err.msg)
166 }
167}
168
169impl std::fmt::Display for GuardError {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 write!(f, "[{}] {}", self.code.as_i32(), self.msg)
172 }
173}
174
175impl std::error::Error for GuardError {}
176
177pub struct UserContext {
208 pub user_id: i64,
210 pub is_super: bool,
212 pub roles: Vec<String>,
214 pub permissions: Vec<String>,
216}
217
218impl UserContext {
219 pub fn new(user_id: i64) -> Self {
221 Self {
222 user_id,
223 is_super: false,
224 roles: Vec::new(),
225 permissions: Vec::new(),
226 }
227 }
228
229 pub fn with_super(mut self, is_super: bool) -> Self {
231 self.is_super = is_super;
232 self
233 }
234
235 pub fn with_roles(mut self, roles: Vec<String>) -> Self {
237 self.roles = roles;
238 self
239 }
240
241 pub fn with_permissions(mut self, permissions: Vec<String>) -> Self {
243 self.permissions = permissions;
244 self
245 }
246
247 pub fn has_role(&self, role: &str) -> bool {
251 self.roles.iter().any(|r| r == role)
252 }
253
254 pub fn has_permission(&self, permission: &str) -> bool {
262 if self.permissions.iter().any(|p| p == permission) {
264 return true;
265 }
266 for perm in &self.permissions {
268 if perm.ends_with("/*") {
269 let prefix = &perm[..perm.len() - 1]; if permission.starts_with(prefix) {
271 return true;
272 }
273 }
274 }
275 false
276 }
277}
278
279impl std::fmt::Debug for UserContext {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 f.debug_struct("UserContext")
282 .field("user_id", &self.user_id)
283 .field("is_super", &self.is_super)
284 .field("roles", &self.roles)
285 .field("permissions", &self.permissions)
286 .finish()
287 }
288}
289
290impl Clone for UserContext {
291 fn clone(&self) -> Self {
292 Self {
293 user_id: self.user_id,
294 is_super: self.is_super,
295 roles: self.roles.clone(),
296 permissions: self.permissions.clone(),
297 }
298 }
299}
300
301impl Default for UserContext {
302 fn default() -> Self {
303 Self::new(0)
304 }
305}
306
307impl From<AuthenticatedUser> for UserContext {
308 fn from(user: AuthenticatedUser) -> Self {
313 Self::new(user.user_id)
314 }
315}
316
317pub trait Guard: Send + Sync {
342 fn check(&self, req: &Request) -> Result<(), GuardError>;
352}
353
354#[derive(Debug, Default)]
372pub struct AuthGuard;
373
374impl AuthGuard {
375 pub fn new() -> Self {
377 Self
378 }
379}
380
381impl Guard for AuthGuard {
382 fn check(&self, req: &Request) -> Result<(), GuardError> {
383 if req.extensions().get::<AuthenticatedUser>().is_some() {
384 Ok(())
385 } else {
386 Err(GuardError::not_login("not_login"))
387 }
388 }
389}
390
391#[derive(Debug, Default)]
407pub struct AdminGuard;
408
409impl AdminGuard {
410 pub fn new() -> Self {
412 Self
413 }
414}
415
416impl Guard for AdminGuard {
417 fn check(&self, req: &Request) -> Result<(), GuardError> {
418 let _user = req
420 .extensions()
421 .get::<AuthenticatedUser>()
422 .ok_or_else(|| GuardError::not_login("not_login"))?;
423
424 let user_ctx = req
426 .extensions()
427 .get::<UserContext>()
428 .ok_or_else(|| GuardError::forbidden("无权限访问"))?;
429
430 if user_ctx.is_super {
431 Ok(())
432 } else {
433 Err(GuardError::forbidden("无权限访问"))
434 }
435 }
436}
437
438#[derive(Debug)]
464pub struct PermissionGuard {
465 pub permission: String,
467}
468
469impl PermissionGuard {
470 pub fn new(permission: impl Into<String>) -> Self {
472 Self {
473 permission: permission.into(),
474 }
475 }
476}
477
478impl Guard for PermissionGuard {
479 fn check(&self, req: &Request) -> Result<(), GuardError> {
480 let _user = req
482 .extensions()
483 .get::<AuthenticatedUser>()
484 .ok_or_else(|| GuardError::not_login("not_login"))?;
485
486 let user_ctx = req
488 .extensions()
489 .get::<UserContext>()
490 .ok_or_else(|| GuardError::forbidden("无权限访问"))?;
491
492 if user_ctx.is_super {
494 return Ok(());
495 }
496
497 if user_ctx.has_permission(&self.permission) {
499 Ok(())
500 } else {
501 Err(GuardError::forbidden("无权限访问"))
502 }
503 }
504}
505
506#[derive(Debug)]
518pub struct RoleGuard {
519 pub role: String,
521}
522
523impl RoleGuard {
524 pub fn new(role: impl Into<String>) -> Self {
526 Self { role: role.into() }
527 }
528}
529
530impl Guard for RoleGuard {
531 fn check(&self, req: &Request) -> Result<(), GuardError> {
532 let _user = req
534 .extensions()
535 .get::<AuthenticatedUser>()
536 .ok_or_else(|| GuardError::not_login("not_login"))?;
537
538 let user_ctx = req
540 .extensions()
541 .get::<UserContext>()
542 .ok_or_else(|| GuardError::forbidden("无权限访问"))?;
543
544 if user_ctx.is_super {
546 return Ok(());
547 }
548
549 if user_ctx.has_role(&self.role) {
551 Ok(())
552 } else {
553 Err(GuardError::forbidden("无权限访问"))
554 }
555 }
556}
557
558pub struct GuardChain {
584 pub guards: Vec<Arc<dyn Guard>>,
586}
587
588impl GuardChain {
589 pub fn new() -> Self {
591 Self { guards: Vec::new() }
592 }
593
594 pub fn with_guard(mut self, guard: Arc<dyn Guard>) -> Self {
596 self.guards.push(guard);
597 self
598 }
599
600 pub fn from_guards(guards: Vec<Arc<dyn Guard>>) -> Self {
602 Self { guards }
603 }
604}
605
606impl Default for GuardChain {
607 fn default() -> Self {
608 Self::new()
609 }
610}
611
612impl Guard for GuardChain {
613 fn check(&self, req: &Request) -> Result<(), GuardError> {
614 for guard in &self.guards {
617 guard.check(req)?;
618 }
619 Ok(())
620 }
621}
622
623pub async fn guard_middleware(
648 axum::extract::State(guard): axum::extract::State<Arc<dyn Guard>>,
649 req: Request,
650 next: Next,
651) -> Response {
652 match guard.check(&req) {
653 Ok(()) => next.run(req).await,
654 Err(err) => err.into_response(),
655 }
656}
657
658pub fn check_guards(req: &Request, guards: &[Arc<dyn Guard>]) -> Result<(), GuardError> {
672 for guard in guards {
673 guard.check(req)?;
674 }
675 Ok(())
676}
677
678#[cfg(test)]
683mod tests {
684 use super::*;
685 use axum::body::Body;
686 use axum::http::StatusCode;
687 use axum::Router;
688 use http_body_util::BodyExt;
689 use tower::ServiceExt;
690
691 fn make_request() -> Request {
697 Request::builder()
698 .method("GET")
699 .uri("/test")
700 .body(Body::empty())
701 .unwrap()
702 }
703
704 fn make_request_with_user(user_id: i64) -> Request {
706 let mut req = make_request();
707 req.extensions_mut().insert(AuthenticatedUser { user_id });
708 req
709 }
710
711 fn make_request_with_context(user_ctx: UserContext) -> Request {
713 let mut req = make_request_with_user(user_ctx.user_id);
714 req.extensions_mut().insert(user_ctx);
715 req
716 }
717
718 async fn read_body(resp: Response) -> String {
720 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
721 String::from_utf8(bytes.to_vec()).unwrap()
722 }
723
724 fn build_app(guard: Arc<dyn Guard>) -> Router {
726 Router::new()
727 .route(
728 "/protected",
729 axum::routing::get(|| async { axum::http::StatusCode::OK }),
730 )
731 .layer(axum::middleware::from_fn_with_state(
732 guard,
733 guard_middleware,
734 ))
735 }
736
737 #[test]
742 fn test_guard_error_new() {
743 let err = GuardError::new(ErrorCode::Forbidden, "无权限");
744 assert_eq!(err.code, ErrorCode::Forbidden);
745 assert_eq!(err.msg, "无权限");
746 }
747
748 #[test]
749 fn test_guard_error_not_login() {
750 let err = GuardError::not_login("not_login");
751 assert_eq!(err.code, ErrorCode::NotLogin);
752 assert_eq!(err.msg, "not_login");
753 assert_eq!(err.code.as_i32(), -1);
755 }
756
757 #[test]
758 fn test_guard_error_forbidden() {
759 let err = GuardError::forbidden("无权限访问");
760 assert_eq!(err.code, ErrorCode::Forbidden);
761 assert_eq!(err.msg, "无权限访问");
762 assert_eq!(err.code.as_i32(), 403);
763 }
764
765 #[test]
766 fn test_guard_error_user_disabled() {
767 let err = GuardError::user_disabled("您已离职");
768 assert_eq!(err.code, ErrorCode::UserDisabled);
769 assert_eq!(err.msg, "您已离职");
770 assert_eq!(err.code.as_i32(), -3);
772 }
773
774 #[test]
775 fn test_guard_error_display() {
776 let err = GuardError::not_login("not_login");
777 assert_eq!(format!("{}", err), "[-1] not_login");
778 }
779
780 #[test]
781 fn test_guard_error_clone() {
782 let err = GuardError::forbidden("无权限");
783 let cloned = err.clone();
784 assert_eq!(err.code, cloned.code);
785 assert_eq!(err.msg, cloned.msg);
786 }
787
788 #[test]
789 fn test_guard_error_into_response_not_login() {
790 let err = GuardError::not_login("not_login");
791 let resp = err.into_response();
792 assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
794 }
795
796 #[test]
797 fn test_guard_error_into_response_forbidden() {
798 let err = GuardError::forbidden("无权限访问");
799 let resp = err.into_response();
800 assert_eq!(resp.status(), StatusCode::FORBIDDEN);
802 }
803
804 #[test]
805 fn test_guard_error_into_response_user_disabled() {
806 let err = GuardError::user_disabled("您已离职");
807 let resp = err.into_response();
808 assert_eq!(resp.status(), StatusCode::FORBIDDEN);
810 }
811
812 #[test]
813 fn test_guard_error_into_base_exception() {
814 let err = GuardError::not_login("not_login");
815 let exc: BaseException = err.into();
816 assert_eq!(exc.code, -1);
817 assert_eq!(exc.msg, "not_login");
818 }
819
820 #[tokio::test]
821 async fn test_guard_error_response_body_format() {
822 let err = GuardError::not_login("not_login");
824 let resp = err.into_response();
825 let body = read_body(resp).await;
826 let json: serde_json::Value = serde_json::from_str(&body).unwrap();
827 assert_eq!(json["code"], -1);
828 assert_eq!(json["msg"], "not_login");
829 assert_eq!(json["data"], serde_json::json!({}));
830 }
831
832 #[test]
837 fn test_user_context_new() {
838 let ctx = UserContext::new(100);
839 assert_eq!(ctx.user_id, 100);
840 assert!(!ctx.is_super);
841 assert!(ctx.roles.is_empty());
842 assert!(ctx.permissions.is_empty());
843 }
844
845 #[test]
846 fn test_user_context_with_super() {
847 let ctx = UserContext::new(1).with_super(true);
848 assert!(ctx.is_super);
849 }
850
851 #[test]
852 fn test_user_context_with_roles() {
853 let ctx = UserContext::new(1).with_roles(vec!["admin".to_string(), "editor".to_string()]);
854 assert_eq!(ctx.roles, vec!["admin", "editor"]);
855 }
856
857 #[test]
858 fn test_user_context_with_permissions() {
859 let ctx = UserContext::new(1)
860 .with_permissions(vec!["user/list".to_string(), "user/save".to_string()]);
861 assert_eq!(ctx.permissions, vec!["user/list", "user/save"]);
862 }
863
864 #[test]
865 fn test_user_context_has_role() {
866 let ctx = UserContext::new(1).with_roles(vec!["admin".to_string(), "editor".to_string()]);
867 assert!(ctx.has_role("admin"));
868 assert!(ctx.has_role("editor"));
869 assert!(!ctx.has_role("guest"));
870 }
871
872 #[test]
873 fn test_user_context_has_role_empty() {
874 let ctx = UserContext::new(1);
875 assert!(!ctx.has_role("admin"));
876 }
877
878 #[test]
879 fn test_user_context_has_permission_exact() {
880 let ctx = UserContext::new(1).with_permissions(vec!["user/list".to_string()]);
881 assert!(ctx.has_permission("user/list"));
882 assert!(!ctx.has_permission("user/save"));
883 }
884
885 #[test]
886 fn test_user_context_has_permission_wildcard() {
887 let ctx = UserContext::new(1).with_permissions(vec!["user/*".to_string()]);
889 assert!(ctx.has_permission("user/list"));
890 assert!(ctx.has_permission("user/save"));
891 assert!(ctx.has_permission("user/delete"));
892 assert!(!ctx.has_permission("order/list"));
894 }
895
896 #[test]
897 fn test_user_context_has_permission_empty() {
898 let ctx = UserContext::new(1);
899 assert!(!ctx.has_permission("user/list"));
900 }
901
902 #[test]
903 fn test_user_context_has_permission_multiple() {
904 let ctx = UserContext::new(1).with_permissions(vec![
905 "user/list".to_string(),
906 "order/*".to_string(),
907 "system/config".to_string(),
908 ]);
909 assert!(ctx.has_permission("user/list"));
911 assert!(ctx.has_permission("system/config"));
912 assert!(ctx.has_permission("order/list"));
914 assert!(ctx.has_permission("order/save"));
915 assert!(!ctx.has_permission("user/save"));
917 assert!(!ctx.has_permission("product/list"));
918 }
919
920 #[test]
921 fn test_user_context_from_authenticated_user() {
922 let user = AuthenticatedUser { user_id: 42 };
923 let ctx = UserContext::from(user);
924 assert_eq!(ctx.user_id, 42);
925 assert!(!ctx.is_super);
926 assert!(ctx.roles.is_empty());
927 assert!(ctx.permissions.is_empty());
928 }
929
930 #[test]
931 fn test_user_context_default() {
932 let ctx = UserContext::default();
933 assert_eq!(ctx.user_id, 0);
934 assert!(!ctx.is_super);
935 }
936
937 #[test]
938 fn test_user_context_clone() {
939 let ctx = UserContext::new(1)
940 .with_super(true)
941 .with_roles(vec!["admin".to_string()])
942 .with_permissions(vec!["user/list".to_string()]);
943 let cloned = ctx.clone();
944 assert_eq!(ctx.user_id, cloned.user_id);
945 assert_eq!(ctx.is_super, cloned.is_super);
946 assert_eq!(ctx.roles, cloned.roles);
947 assert_eq!(ctx.permissions, cloned.permissions);
948 }
949
950 #[test]
951 fn test_user_context_debug() {
952 let ctx = UserContext::new(1).with_super(true);
953 let debug_str = format!("{:?}", ctx);
954 assert!(debug_str.contains("UserContext"));
955 assert!(debug_str.contains("user_id"));
956 assert!(debug_str.contains("is_super"));
957 }
958
959 #[test]
960 fn test_user_context_builder_chain() {
961 let ctx = UserContext::new(1)
962 .with_super(false)
963 .with_roles(vec!["editor".to_string()])
964 .with_permissions(vec!["post/list".to_string(), "post/save".to_string()]);
965 assert_eq!(ctx.user_id, 1);
966 assert!(!ctx.is_super);
967 assert_eq!(ctx.roles, vec!["editor"]);
968 assert_eq!(ctx.permissions.len(), 2);
969 assert!(ctx.has_role("editor"));
970 assert!(ctx.has_permission("post/list"));
971 }
972
973 #[test]
978 fn test_auth_guard_new() {
979 let guard = AuthGuard::new();
980 let _ = format!("{:?}", guard);
982 }
983
984 #[test]
985 fn test_auth_guard_passes_when_authenticated() {
986 let guard = AuthGuard::new();
987 let req = make_request_with_user(1);
988 assert!(guard.check(&req).is_ok());
989 }
990
991 #[test]
992 fn test_auth_guard_fails_when_not_authenticated() {
993 let guard = AuthGuard::new();
994 let req = make_request();
995 let result = guard.check(&req);
996 assert!(result.is_err());
997 let err = result.unwrap_err();
998 assert_eq!(err.code, ErrorCode::NotLogin);
999 assert_eq!(err.msg, "not_login");
1000 }
1001
1002 #[test]
1007 fn test_admin_guard_new() {
1008 let _guard = AdminGuard::new();
1009 }
1010
1011 #[test]
1012 fn test_admin_guard_fails_when_not_logged_in() {
1013 let guard = AdminGuard::new();
1014 let req = make_request();
1015 let result = guard.check(&req);
1016 assert!(result.is_err());
1017 let err = result.unwrap_err();
1018 assert_eq!(err.code, ErrorCode::NotLogin);
1020 }
1021
1022 #[test]
1023 fn test_admin_guard_fails_when_logged_in_but_no_user_context() {
1024 let guard = AdminGuard::new();
1025 let req = make_request_with_user(1);
1026 let result = guard.check(&req);
1027 assert!(result.is_err());
1028 let err = result.unwrap_err();
1029 assert_eq!(err.code, ErrorCode::Forbidden);
1031 }
1032
1033 #[test]
1034 fn test_admin_guard_fails_when_not_super() {
1035 let guard = AdminGuard::new();
1036 let user_ctx = UserContext::new(1).with_super(false);
1037 let req = make_request_with_context(user_ctx);
1038 let result = guard.check(&req);
1039 assert!(result.is_err());
1040 let err = result.unwrap_err();
1041 assert_eq!(err.code, ErrorCode::Forbidden);
1042 }
1043
1044 #[test]
1045 fn test_admin_guard_passes_when_super() {
1046 let guard = AdminGuard::new();
1047 let user_ctx = UserContext::new(1).with_super(true);
1048 let req = make_request_with_context(user_ctx);
1049 assert!(guard.check(&req).is_ok());
1050 }
1051
1052 #[test]
1057 fn test_permission_guard_new() {
1058 let guard = PermissionGuard::new("user/list");
1059 assert_eq!(guard.permission, "user/list");
1060 }
1061
1062 #[test]
1063 fn test_permission_guard_fails_when_not_logged_in() {
1064 let guard = PermissionGuard::new("user/list");
1065 let req = make_request();
1066 let result = guard.check(&req);
1067 assert!(result.is_err());
1068 let err = result.unwrap_err();
1069 assert_eq!(err.code, ErrorCode::NotLogin);
1070 }
1071
1072 #[test]
1073 fn test_permission_guard_fails_when_no_user_context() {
1074 let guard = PermissionGuard::new("user/list");
1075 let req = make_request_with_user(1);
1076 let result = guard.check(&req);
1077 assert!(result.is_err());
1078 let err = result.unwrap_err();
1079 assert_eq!(err.code, ErrorCode::Forbidden);
1080 }
1081
1082 #[test]
1083 fn test_permission_guard_fails_when_no_permission() {
1084 let guard = PermissionGuard::new("user/delete");
1085 let user_ctx = UserContext::new(1).with_permissions(vec!["user/list".to_string()]);
1086 let req = make_request_with_context(user_ctx);
1087 let result = guard.check(&req);
1088 assert!(result.is_err());
1089 let err = result.unwrap_err();
1090 assert_eq!(err.code, ErrorCode::Forbidden);
1091 }
1092
1093 #[test]
1094 fn test_permission_guard_passes_when_has_exact_permission() {
1095 let guard = PermissionGuard::new("user/list");
1096 let user_ctx = UserContext::new(1).with_permissions(vec!["user/list".to_string()]);
1097 let req = make_request_with_context(user_ctx);
1098 assert!(guard.check(&req).is_ok());
1099 }
1100
1101 #[test]
1102 fn test_permission_guard_passes_when_has_wildcard_permission() {
1103 let guard = PermissionGuard::new("user/list");
1104 let user_ctx = UserContext::new(1).with_permissions(vec!["user/*".to_string()]);
1105 let req = make_request_with_context(user_ctx);
1106 assert!(guard.check(&req).is_ok());
1107 }
1108
1109 #[test]
1110 fn test_permission_guard_passes_when_super() {
1111 let guard = PermissionGuard::new("user/delete");
1113 let user_ctx = UserContext::new(1).with_super(true);
1114 let req = make_request_with_context(user_ctx);
1115 assert!(guard.check(&req).is_ok());
1116 }
1117
1118 #[test]
1119 fn test_permission_guard_passes_when_super_without_permissions() {
1120 let guard = PermissionGuard::new("system/config");
1122 let user_ctx = UserContext::new(1).with_super(true);
1123 let req = make_request_with_context(user_ctx);
1124 assert!(guard.check(&req).is_ok());
1125 }
1126
1127 #[test]
1132 fn test_role_guard_new() {
1133 let guard = RoleGuard::new("admin");
1134 assert_eq!(guard.role, "admin");
1135 }
1136
1137 #[test]
1138 fn test_role_guard_fails_when_not_logged_in() {
1139 let guard = RoleGuard::new("admin");
1140 let req = make_request();
1141 let result = guard.check(&req);
1142 assert!(result.is_err());
1143 let err = result.unwrap_err();
1144 assert_eq!(err.code, ErrorCode::NotLogin);
1145 }
1146
1147 #[test]
1148 fn test_role_guard_fails_when_no_user_context() {
1149 let guard = RoleGuard::new("admin");
1150 let req = make_request_with_user(1);
1151 let result = guard.check(&req);
1152 assert!(result.is_err());
1153 let err = result.unwrap_err();
1154 assert_eq!(err.code, ErrorCode::Forbidden);
1155 }
1156
1157 #[test]
1158 fn test_role_guard_fails_when_no_role() {
1159 let guard = RoleGuard::new("admin");
1160 let user_ctx = UserContext::new(1).with_roles(vec!["editor".to_string()]);
1161 let req = make_request_with_context(user_ctx);
1162 let result = guard.check(&req);
1163 assert!(result.is_err());
1164 }
1165
1166 #[test]
1167 fn test_role_guard_passes_when_has_role() {
1168 let guard = RoleGuard::new("admin");
1169 let user_ctx = UserContext::new(1).with_roles(vec!["admin".to_string()]);
1170 let req = make_request_with_context(user_ctx);
1171 assert!(guard.check(&req).is_ok());
1172 }
1173
1174 #[test]
1175 fn test_role_guard_passes_when_super() {
1176 let guard = RoleGuard::new("admin");
1178 let user_ctx = UserContext::new(1).with_super(true);
1179 let req = make_request_with_context(user_ctx);
1180 assert!(guard.check(&req).is_ok());
1181 }
1182
1183 #[test]
1188 fn test_guard_chain_new() {
1189 let chain = GuardChain::new();
1190 assert!(chain.guards.is_empty());
1191 }
1192
1193 #[test]
1194 fn test_guard_chain_default() {
1195 let chain = GuardChain::default();
1196 assert!(chain.guards.is_empty());
1197 }
1198
1199 #[test]
1200 fn test_guard_chain_with_guard() {
1201 let chain = GuardChain::new()
1202 .with_guard(Arc::new(AuthGuard))
1203 .with_guard(Arc::new(AdminGuard));
1204 assert_eq!(chain.guards.len(), 2);
1205 }
1206
1207 #[test]
1208 fn test_guard_chain_from_guards() {
1209 let guards: Vec<Arc<dyn Guard>> = vec![Arc::new(AuthGuard), Arc::new(AdminGuard)];
1210 let chain = GuardChain::from_guards(guards);
1211 assert_eq!(chain.guards.len(), 2);
1212 }
1213
1214 #[test]
1215 fn test_guard_chain_empty_passes() {
1216 let chain = GuardChain::new();
1218 let req = make_request();
1219 assert!(chain.check(&req).is_ok());
1220 }
1221
1222 #[test]
1223 fn test_guard_chain_single_guard_passes() {
1224 let chain = GuardChain::new().with_guard(Arc::new(AuthGuard));
1225 let req = make_request_with_user(1);
1226 assert!(chain.check(&req).is_ok());
1227 }
1228
1229 #[test]
1230 fn test_guard_chain_single_guard_fails() {
1231 let chain = GuardChain::new().with_guard(Arc::new(AuthGuard));
1232 let req = make_request();
1233 assert!(chain.check(&req).is_err());
1234 }
1235
1236 #[test]
1237 fn test_guard_chain_and_semantics_all_pass() {
1238 let chain = GuardChain::new()
1239 .with_guard(Arc::new(AuthGuard))
1240 .with_guard(Arc::new(AdminGuard));
1241 let user_ctx = UserContext::new(1).with_super(true);
1242 let req = make_request_with_context(user_ctx);
1243 assert!(chain.check(&req).is_ok());
1244 }
1245
1246 #[test]
1247 fn test_guard_chain_and_semantics_first_fails() {
1248 let chain = GuardChain::new()
1250 .with_guard(Arc::new(AuthGuard))
1251 .with_guard(Arc::new(AdminGuard));
1252 let req = make_request();
1253 let result = chain.check(&req);
1254 assert!(result.is_err());
1255 let err = result.unwrap_err();
1256 assert_eq!(err.code, ErrorCode::NotLogin);
1258 }
1259
1260 #[test]
1261 fn test_guard_chain_and_semantics_second_fails() {
1262 let chain = GuardChain::new()
1264 .with_guard(Arc::new(AuthGuard))
1265 .with_guard(Arc::new(AdminGuard));
1266 let user_ctx = UserContext::new(1).with_super(false);
1268 let req = make_request_with_context(user_ctx);
1269 let result = chain.check(&req);
1270 assert!(result.is_err());
1271 let err = result.unwrap_err();
1272 assert_eq!(err.code, ErrorCode::Forbidden);
1274 }
1275
1276 #[test]
1277 fn test_guard_chain_and_semantics_short_circuit() {
1278 struct FailGuard;
1280 impl Guard for FailGuard {
1281 fn check(&self, _req: &Request) -> Result<(), GuardError> {
1282 Err(GuardError::forbidden("fail_guard_called"))
1283 }
1284 }
1285 struct PanicGuard;
1286 impl Guard for PanicGuard {
1287 fn check(&self, _req: &Request) -> Result<(), GuardError> {
1288 panic!("PanicGuard should not be called due to short-circuit");
1289 }
1290 }
1291 let chain = GuardChain::new()
1292 .with_guard(Arc::new(FailGuard))
1293 .with_guard(Arc::new(PanicGuard));
1294 let req = make_request();
1295 let result = chain.check(&req);
1296 assert!(result.is_err());
1297 assert_eq!(result.unwrap_err().msg, "fail_guard_called");
1298 }
1299
1300 #[test]
1301 fn test_guard_chain_order_matters() {
1302 let chain = GuardChain::new()
1304 .with_guard(Arc::new(AuthGuard))
1305 .with_guard(Arc::new(PermissionGuard::new("user/list")));
1306 let req = make_request();
1308 let result = chain.check(&req);
1309 assert!(result.is_err());
1310 let err = result.unwrap_err();
1311 assert_eq!(err.code, ErrorCode::NotLogin);
1312 }
1313
1314 #[test]
1315 fn test_guard_chain_multiple_permissions() {
1316 let chain = GuardChain::new()
1317 .with_guard(Arc::new(AuthGuard))
1318 .with_guard(Arc::new(PermissionGuard::new("user/list")))
1319 .with_guard(Arc::new(PermissionGuard::new("user/save")));
1320 let user_ctx = UserContext::new(1)
1321 .with_permissions(vec!["user/list".to_string(), "user/save".to_string()]);
1322 let req = make_request_with_context(user_ctx);
1323 assert!(chain.check(&req).is_ok());
1324 }
1325
1326 #[test]
1327 fn test_guard_chain_mixed_guard_types() {
1328 let chain = GuardChain::new()
1329 .with_guard(Arc::new(AuthGuard))
1330 .with_guard(Arc::new(RoleGuard::new("editor")))
1331 .with_guard(Arc::new(PermissionGuard::new("post/list")));
1332 let user_ctx = UserContext::new(1)
1333 .with_roles(vec!["editor".to_string()])
1334 .with_permissions(vec!["post/list".to_string()]);
1335 let req = make_request_with_context(user_ctx);
1336 assert!(chain.check(&req).is_ok());
1337 }
1338
1339 #[test]
1344 fn test_check_guards_empty() {
1345 let req = make_request();
1346 assert!(check_guards(&req, &[]).is_ok());
1347 }
1348
1349 #[test]
1350 fn test_check_guards_all_pass() {
1351 let guards: Vec<Arc<dyn Guard>> = vec![Arc::new(AuthGuard)];
1352 let req = make_request_with_user(1);
1353 assert!(check_guards(&req, &guards).is_ok());
1354 }
1355
1356 #[test]
1357 fn test_check_guards_fails() {
1358 let guards: Vec<Arc<dyn Guard>> = vec![Arc::new(AuthGuard)];
1359 let req = make_request();
1360 assert!(check_guards(&req, &guards).is_err());
1361 }
1362
1363 #[tokio::test]
1368 async fn test_guard_middleware_passes() {
1369 let app = build_app(Arc::new(AuthGuard));
1370 let req = Request::builder()
1372 .method("GET")
1373 .uri("/protected")
1374 .extension(AuthenticatedUser { user_id: 1 })
1375 .body(Body::empty())
1376 .unwrap();
1377 let resp = app.oneshot(req).await.unwrap();
1378 assert_eq!(resp.status(), StatusCode::OK);
1379 }
1380
1381 #[tokio::test]
1382 async fn test_guard_middleware_fails_not_login() {
1383 let app = build_app(Arc::new(AuthGuard));
1384 let req = Request::builder()
1385 .method("GET")
1386 .uri("/protected")
1387 .body(Body::empty())
1388 .unwrap();
1389 let resp = app.oneshot(req).await.unwrap();
1390 assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
1392 let body = read_body(resp).await;
1393 let json: serde_json::Value = serde_json::from_str(&body).unwrap();
1394 assert_eq!(json["code"], -1);
1395 assert_eq!(json["msg"], "not_login");
1396 }
1397
1398 #[tokio::test]
1399 async fn test_guard_middleware_fails_forbidden() {
1400 let app = build_app(Arc::new(AdminGuard));
1401 let req = Request::builder()
1403 .method("GET")
1404 .uri("/protected")
1405 .extension(AuthenticatedUser { user_id: 1 })
1406 .extension(UserContext::new(1).with_super(false))
1407 .body(Body::empty())
1408 .unwrap();
1409 let resp = app.oneshot(req).await.unwrap();
1410 assert_eq!(resp.status(), StatusCode::FORBIDDEN);
1412 let body = read_body(resp).await;
1413 let json: serde_json::Value = serde_json::from_str(&body).unwrap();
1414 assert_eq!(json["code"], 403);
1415 }
1416
1417 #[tokio::test]
1418 async fn test_guard_middleware_with_chain() {
1419 let chain = GuardChain::new()
1420 .with_guard(Arc::new(AuthGuard))
1421 .with_guard(Arc::new(AdminGuard));
1422 let app = build_app(Arc::new(chain));
1423
1424 let req = Request::builder()
1426 .method("GET")
1427 .uri("/protected")
1428 .extension(AuthenticatedUser { user_id: 1 })
1429 .extension(UserContext::new(1).with_super(true))
1430 .body(Body::empty())
1431 .unwrap();
1432 let resp = app.clone().oneshot(req).await.unwrap();
1433 assert_eq!(resp.status(), StatusCode::OK);
1434
1435 let req = Request::builder()
1437 .method("GET")
1438 .uri("/protected")
1439 .extension(AuthenticatedUser { user_id: 2 })
1440 .extension(UserContext::new(2).with_super(false))
1441 .body(Body::empty())
1442 .unwrap();
1443 let resp = app.oneshot(req).await.unwrap();
1444 assert_eq!(resp.status(), StatusCode::FORBIDDEN);
1445 }
1446
1447 #[tokio::test]
1448 async fn test_guard_middleware_permission_guard() {
1449 let chain = GuardChain::new()
1450 .with_guard(Arc::new(AuthGuard))
1451 .with_guard(Arc::new(PermissionGuard::new("user/list")));
1452 let app = build_app(Arc::new(chain));
1453
1454 let req = Request::builder()
1456 .method("GET")
1457 .uri("/protected")
1458 .extension(AuthenticatedUser { user_id: 1 })
1459 .extension(UserContext::new(1).with_permissions(vec!["user/list".to_string()]))
1460 .body(Body::empty())
1461 .unwrap();
1462 let resp = app.clone().oneshot(req).await.unwrap();
1463 assert_eq!(resp.status(), StatusCode::OK);
1464
1465 let req = Request::builder()
1467 .method("GET")
1468 .uri("/protected")
1469 .extension(AuthenticatedUser { user_id: 2 })
1470 .extension(UserContext::new(2).with_permissions(vec!["order/list".to_string()]))
1471 .body(Body::empty())
1472 .unwrap();
1473 let resp = app.oneshot(req).await.unwrap();
1474 assert_eq!(resp.status(), StatusCode::FORBIDDEN);
1475 }
1476
1477 #[test]
1484 fn test_php_alignment_is_super_bypass() {
1485 let permission_guard = PermissionGuard::new("system/config");
1486 let role_guard = RoleGuard::new("admin");
1487 let admin_guard = AdminGuard::new();
1488
1489 let user_ctx = UserContext::new(1).with_super(true);
1491 let req = make_request_with_context(user_ctx);
1492
1493 assert!(permission_guard.check(&req).is_ok());
1494 assert!(role_guard.check(&req).is_ok());
1495 assert!(admin_guard.check(&req).is_ok());
1496 }
1497
1498 #[test]
1503 fn test_php_alignment_error_codes() {
1504 let err = GuardError::not_login("not_login");
1506 assert_eq!(err.code.as_i32(), -1);
1507
1508 let err = GuardError::user_disabled("您已离职");
1510 assert_eq!(err.code.as_i32(), -3);
1511
1512 let err = GuardError::forbidden("无权限访问");
1515 assert_eq!(err.code.as_i32(), 403);
1516 }
1517
1518 #[test]
1523 fn test_php_alignment_check_login() {
1524 let guard = AuthGuard::new();
1525
1526 let req = make_request();
1528 let result = guard.check(&req);
1529 assert!(matches!(
1530 result,
1531 Err(GuardError {
1532 code: ErrorCode::NotLogin,
1533 ..
1534 })
1535 ));
1536
1537 let req = make_request_with_user(1);
1539 assert!(guard.check(&req).is_ok());
1540 }
1541
1542 #[test]
1545 fn test_php_alignment_wildcard_permission() {
1546 let ctx = UserContext::new(1).with_permissions(vec!["user/*".to_string()]);
1547 assert!(ctx.has_permission("user/list"));
1548 assert!(ctx.has_permission("user/save"));
1549 assert!(ctx.has_permission("user/delete"));
1550 assert!(!ctx.has_permission("order/list"));
1551 }
1552
1553 #[test]
1556 fn test_php_alignment_multiple_roles() {
1557 let ctx = UserContext::new(1).with_roles(vec!["editor".to_string(), "viewer".to_string()]);
1558 assert!(ctx.has_role("editor"));
1559 assert!(ctx.has_role("viewer"));
1560 assert!(!ctx.has_role("admin"));
1561 }
1562
1563 #[tokio::test]
1566 async fn test_php_alignment_response_format() {
1567 let err = GuardError::user_disabled("您已离职,无权使用本系统!");
1568 let resp = err.into_response();
1569 let body = read_body(resp).await;
1570 let json: serde_json::Value = serde_json::from_str(&body).unwrap();
1571 assert_eq!(json["code"], -3);
1573 assert_eq!(json["msg"], "您已离职,无权使用本系统!");
1574 assert_eq!(json["data"], serde_json::json!({}));
1575 }
1576}