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 dept_id: i64,
212 pub is_super: bool,
214 pub roles: Vec<String>,
216 pub permissions: Vec<String>,
218}
219
220impl UserContext {
221 pub fn new(user_id: i64) -> Self {
223 Self {
224 user_id,
225 dept_id: 0,
226 is_super: false,
227 roles: Vec::new(),
228 permissions: Vec::new(),
229 }
230 }
231
232 pub fn with_dept(mut self, dept_id: i64) -> Self {
234 self.dept_id = dept_id;
235 self
236 }
237
238 pub fn with_super(mut self, is_super: bool) -> Self {
240 self.is_super = is_super;
241 self
242 }
243
244 pub fn with_roles(mut self, roles: Vec<String>) -> Self {
246 self.roles = roles;
247 self
248 }
249
250 pub fn with_permissions(mut self, permissions: Vec<String>) -> Self {
252 self.permissions = permissions;
253 self
254 }
255
256 pub fn has_role(&self, role: &str) -> bool {
260 self.roles.iter().any(|r| r == role)
261 }
262
263 pub fn has_permission(&self, permission: &str) -> bool {
271 if self.permissions.iter().any(|p| p == permission) {
273 return true;
274 }
275 for perm in &self.permissions {
277 if perm.ends_with("/*") {
278 let prefix = &perm[..perm.len() - 1]; if permission.starts_with(prefix) {
280 return true;
281 }
282 }
283 }
284 false
285 }
286}
287
288impl std::fmt::Debug for UserContext {
289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290 f.debug_struct("UserContext")
291 .field("user_id", &self.user_id)
292 .field("dept_id", &self.dept_id)
293 .field("is_super", &self.is_super)
294 .field("roles", &self.roles)
295 .field("permissions", &self.permissions)
296 .finish()
297 }
298}
299
300impl Clone for UserContext {
301 fn clone(&self) -> Self {
302 Self {
303 user_id: self.user_id,
304 dept_id: self.dept_id,
305 is_super: self.is_super,
306 roles: self.roles.clone(),
307 permissions: self.permissions.clone(),
308 }
309 }
310}
311
312impl Default for UserContext {
313 fn default() -> Self {
314 Self::new(0)
315 }
316}
317
318impl From<AuthenticatedUser> for UserContext {
319 fn from(user: AuthenticatedUser) -> Self {
324 Self::new(user.user_id)
325 }
326}
327
328pub trait Guard: Send + Sync {
353 fn check(&self, req: &Request) -> Result<(), GuardError>;
363}
364
365#[derive(Debug, Default)]
383pub struct AuthGuard;
384
385impl AuthGuard {
386 pub fn new() -> Self {
388 Self
389 }
390}
391
392impl Guard for AuthGuard {
393 fn check(&self, req: &Request) -> Result<(), GuardError> {
394 if req.extensions().get::<AuthenticatedUser>().is_some() {
395 Ok(())
396 } else {
397 Err(GuardError::not_login("not_login"))
398 }
399 }
400}
401
402#[derive(Debug, Default)]
418pub struct AdminGuard;
419
420impl AdminGuard {
421 pub fn new() -> Self {
423 Self
424 }
425}
426
427impl Guard for AdminGuard {
428 fn check(&self, req: &Request) -> Result<(), GuardError> {
429 let _user = req
431 .extensions()
432 .get::<AuthenticatedUser>()
433 .ok_or_else(|| GuardError::not_login("not_login"))?;
434
435 let user_ctx = req
437 .extensions()
438 .get::<UserContext>()
439 .ok_or_else(|| GuardError::forbidden("无权限访问"))?;
440
441 if user_ctx.is_super {
442 Ok(())
443 } else {
444 Err(GuardError::forbidden("无权限访问"))
445 }
446 }
447}
448
449#[derive(Debug)]
475pub struct PermissionGuard {
476 pub permission: String,
478}
479
480impl PermissionGuard {
481 pub fn new(permission: impl Into<String>) -> Self {
483 Self {
484 permission: permission.into(),
485 }
486 }
487}
488
489impl Guard for PermissionGuard {
490 fn check(&self, req: &Request) -> Result<(), GuardError> {
491 let _user = req
493 .extensions()
494 .get::<AuthenticatedUser>()
495 .ok_or_else(|| GuardError::not_login("not_login"))?;
496
497 let user_ctx = req
499 .extensions()
500 .get::<UserContext>()
501 .ok_or_else(|| GuardError::forbidden("无权限访问"))?;
502
503 if user_ctx.is_super {
505 return Ok(());
506 }
507
508 if user_ctx.has_permission(&self.permission) {
510 Ok(())
511 } else {
512 Err(GuardError::forbidden("无权限访问"))
513 }
514 }
515}
516
517#[derive(Debug)]
529pub struct RoleGuard {
530 pub role: String,
532}
533
534impl RoleGuard {
535 pub fn new(role: impl Into<String>) -> Self {
537 Self { role: role.into() }
538 }
539}
540
541impl Guard for RoleGuard {
542 fn check(&self, req: &Request) -> Result<(), GuardError> {
543 let _user = req
545 .extensions()
546 .get::<AuthenticatedUser>()
547 .ok_or_else(|| GuardError::not_login("not_login"))?;
548
549 let user_ctx = req
551 .extensions()
552 .get::<UserContext>()
553 .ok_or_else(|| GuardError::forbidden("无权限访问"))?;
554
555 if user_ctx.is_super {
557 return Ok(());
558 }
559
560 if user_ctx.has_role(&self.role) {
562 Ok(())
563 } else {
564 Err(GuardError::forbidden("无权限访问"))
565 }
566 }
567}
568
569pub struct GuardChain {
595 pub guards: Vec<Arc<dyn Guard>>,
597}
598
599impl GuardChain {
600 pub fn new() -> Self {
602 Self { guards: Vec::new() }
603 }
604
605 pub fn with_guard(mut self, guard: Arc<dyn Guard>) -> Self {
607 self.guards.push(guard);
608 self
609 }
610
611 pub fn from_guards(guards: Vec<Arc<dyn Guard>>) -> Self {
613 Self { guards }
614 }
615}
616
617impl Default for GuardChain {
618 fn default() -> Self {
619 Self::new()
620 }
621}
622
623impl Guard for GuardChain {
624 fn check(&self, req: &Request) -> Result<(), GuardError> {
625 for guard in &self.guards {
628 guard.check(req)?;
629 }
630 Ok(())
631 }
632}
633
634pub async fn guard_middleware(
659 axum::extract::State(guard): axum::extract::State<Arc<dyn Guard>>,
660 req: Request,
661 next: Next,
662) -> Response {
663 match guard.check(&req) {
664 Ok(()) => next.run(req).await,
665 Err(err) => err.into_response(),
666 }
667}
668
669pub fn check_guards(req: &Request, guards: &[Arc<dyn Guard>]) -> Result<(), GuardError> {
683 for guard in guards {
684 guard.check(req)?;
685 }
686 Ok(())
687}
688
689#[cfg(test)]
694mod tests {
695 use super::*;
696 use axum::body::Body;
697 use axum::http::StatusCode;
698 use axum::Router;
699 use http_body_util::BodyExt;
700 use tower::ServiceExt;
701
702 fn make_request() -> Request {
708 Request::builder()
709 .method("GET")
710 .uri("/test")
711 .body(Body::empty())
712 .unwrap()
713 }
714
715 fn make_request_with_user(user_id: i64) -> Request {
717 let mut req = make_request();
718 req.extensions_mut().insert(AuthenticatedUser { user_id });
719 req
720 }
721
722 fn make_request_with_context(user_ctx: UserContext) -> Request {
724 let mut req = make_request_with_user(user_ctx.user_id);
725 req.extensions_mut().insert(user_ctx);
726 req
727 }
728
729 async fn read_body(resp: Response) -> String {
731 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
732 String::from_utf8(bytes.to_vec()).unwrap()
733 }
734
735 fn build_app(guard: Arc<dyn Guard>) -> Router {
737 Router::new()
738 .route(
739 "/protected",
740 axum::routing::get(|| async { axum::http::StatusCode::OK }),
741 )
742 .layer(axum::middleware::from_fn_with_state(
743 guard,
744 guard_middleware,
745 ))
746 }
747
748 #[test]
753 fn test_guard_error_new() {
754 let err = GuardError::new(ErrorCode::Forbidden, "无权限");
755 assert_eq!(err.code, ErrorCode::Forbidden);
756 assert_eq!(err.msg, "无权限");
757 }
758
759 #[test]
760 fn test_guard_error_not_login() {
761 let err = GuardError::not_login("not_login");
762 assert_eq!(err.code, ErrorCode::NotLogin);
763 assert_eq!(err.msg, "not_login");
764 assert_eq!(err.code.as_i32(), -1);
766 }
767
768 #[test]
769 fn test_guard_error_forbidden() {
770 let err = GuardError::forbidden("无权限访问");
771 assert_eq!(err.code, ErrorCode::Forbidden);
772 assert_eq!(err.msg, "无权限访问");
773 assert_eq!(err.code.as_i32(), 403);
774 }
775
776 #[test]
777 fn test_guard_error_user_disabled() {
778 let err = GuardError::user_disabled("您已离职");
779 assert_eq!(err.code, ErrorCode::UserDisabled);
780 assert_eq!(err.msg, "您已离职");
781 assert_eq!(err.code.as_i32(), -3);
783 }
784
785 #[test]
786 fn test_guard_error_display() {
787 let err = GuardError::not_login("not_login");
788 assert_eq!(format!("{}", err), "[-1] not_login");
789 }
790
791 #[test]
792 fn test_guard_error_clone() {
793 let err = GuardError::forbidden("无权限");
794 let cloned = err.clone();
795 assert_eq!(err.code, cloned.code);
796 assert_eq!(err.msg, cloned.msg);
797 }
798
799 #[test]
800 fn test_guard_error_into_response_not_login() {
801 let err = GuardError::not_login("not_login");
802 let resp = err.into_response();
803 assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
805 }
806
807 #[test]
808 fn test_guard_error_into_response_forbidden() {
809 let err = GuardError::forbidden("无权限访问");
810 let resp = err.into_response();
811 assert_eq!(resp.status(), StatusCode::FORBIDDEN);
813 }
814
815 #[test]
816 fn test_guard_error_into_response_user_disabled() {
817 let err = GuardError::user_disabled("您已离职");
818 let resp = err.into_response();
819 assert_eq!(resp.status(), StatusCode::FORBIDDEN);
821 }
822
823 #[test]
824 fn test_guard_error_into_base_exception() {
825 let err = GuardError::not_login("not_login");
826 let exc: BaseException = err.into();
827 assert_eq!(exc.code, -1);
828 assert_eq!(exc.msg, "not_login");
829 }
830
831 #[tokio::test]
832 async fn test_guard_error_response_body_format() {
833 let err = GuardError::not_login("not_login");
835 let resp = err.into_response();
836 let body = read_body(resp).await;
837 let json: serde_json::Value = serde_json::from_str(&body).unwrap();
838 assert_eq!(json["code"], -1);
839 assert_eq!(json["msg"], "not_login");
840 assert_eq!(json["data"], serde_json::json!({}));
841 }
842
843 #[test]
848 fn test_user_context_new() {
849 let ctx = UserContext::new(100);
850 assert_eq!(ctx.user_id, 100);
851 assert!(!ctx.is_super);
852 assert!(ctx.roles.is_empty());
853 assert!(ctx.permissions.is_empty());
854 }
855
856 #[test]
857 fn test_user_context_with_super() {
858 let ctx = UserContext::new(1).with_super(true);
859 assert!(ctx.is_super);
860 }
861
862 #[test]
863 fn test_user_context_with_roles() {
864 let ctx = UserContext::new(1).with_roles(vec!["admin".to_string(), "editor".to_string()]);
865 assert_eq!(ctx.roles, vec!["admin", "editor"]);
866 }
867
868 #[test]
869 fn test_user_context_with_permissions() {
870 let ctx = UserContext::new(1)
871 .with_permissions(vec!["user/list".to_string(), "user/save".to_string()]);
872 assert_eq!(ctx.permissions, vec!["user/list", "user/save"]);
873 }
874
875 #[test]
876 fn test_user_context_has_role() {
877 let ctx = UserContext::new(1).with_roles(vec!["admin".to_string(), "editor".to_string()]);
878 assert!(ctx.has_role("admin"));
879 assert!(ctx.has_role("editor"));
880 assert!(!ctx.has_role("guest"));
881 }
882
883 #[test]
884 fn test_user_context_has_role_empty() {
885 let ctx = UserContext::new(1);
886 assert!(!ctx.has_role("admin"));
887 }
888
889 #[test]
890 fn test_user_context_has_permission_exact() {
891 let ctx = UserContext::new(1).with_permissions(vec!["user/list".to_string()]);
892 assert!(ctx.has_permission("user/list"));
893 assert!(!ctx.has_permission("user/save"));
894 }
895
896 #[test]
897 fn test_user_context_has_permission_wildcard() {
898 let ctx = UserContext::new(1).with_permissions(vec!["user/*".to_string()]);
900 assert!(ctx.has_permission("user/list"));
901 assert!(ctx.has_permission("user/save"));
902 assert!(ctx.has_permission("user/delete"));
903 assert!(!ctx.has_permission("order/list"));
905 }
906
907 #[test]
908 fn test_user_context_has_permission_empty() {
909 let ctx = UserContext::new(1);
910 assert!(!ctx.has_permission("user/list"));
911 }
912
913 #[test]
914 fn test_user_context_has_permission_multiple() {
915 let ctx = UserContext::new(1).with_permissions(vec![
916 "user/list".to_string(),
917 "order/*".to_string(),
918 "system/config".to_string(),
919 ]);
920 assert!(ctx.has_permission("user/list"));
922 assert!(ctx.has_permission("system/config"));
923 assert!(ctx.has_permission("order/list"));
925 assert!(ctx.has_permission("order/save"));
926 assert!(!ctx.has_permission("user/save"));
928 assert!(!ctx.has_permission("product/list"));
929 }
930
931 #[test]
932 fn test_user_context_from_authenticated_user() {
933 let user = AuthenticatedUser { user_id: 42 };
934 let ctx = UserContext::from(user);
935 assert_eq!(ctx.user_id, 42);
936 assert!(!ctx.is_super);
937 assert!(ctx.roles.is_empty());
938 assert!(ctx.permissions.is_empty());
939 }
940
941 #[test]
942 fn test_user_context_default() {
943 let ctx = UserContext::default();
944 assert_eq!(ctx.user_id, 0);
945 assert!(!ctx.is_super);
946 }
947
948 #[test]
949 fn test_user_context_clone() {
950 let ctx = UserContext::new(1)
951 .with_super(true)
952 .with_roles(vec!["admin".to_string()])
953 .with_permissions(vec!["user/list".to_string()]);
954 let cloned = ctx.clone();
955 assert_eq!(ctx.user_id, cloned.user_id);
956 assert_eq!(ctx.is_super, cloned.is_super);
957 assert_eq!(ctx.roles, cloned.roles);
958 assert_eq!(ctx.permissions, cloned.permissions);
959 }
960
961 #[test]
962 fn test_user_context_debug() {
963 let ctx = UserContext::new(1).with_super(true);
964 let debug_str = format!("{:?}", ctx);
965 assert!(debug_str.contains("UserContext"));
966 assert!(debug_str.contains("user_id"));
967 assert!(debug_str.contains("is_super"));
968 }
969
970 #[test]
971 fn test_user_context_builder_chain() {
972 let ctx = UserContext::new(1)
973 .with_super(false)
974 .with_roles(vec!["editor".to_string()])
975 .with_permissions(vec!["post/list".to_string(), "post/save".to_string()]);
976 assert_eq!(ctx.user_id, 1);
977 assert!(!ctx.is_super);
978 assert_eq!(ctx.roles, vec!["editor"]);
979 assert_eq!(ctx.permissions.len(), 2);
980 assert!(ctx.has_role("editor"));
981 assert!(ctx.has_permission("post/list"));
982 }
983
984 #[test]
989 fn test_auth_guard_new() {
990 let guard = AuthGuard::new();
991 let _ = format!("{:?}", guard);
993 }
994
995 #[test]
996 fn test_auth_guard_passes_when_authenticated() {
997 let guard = AuthGuard::new();
998 let req = make_request_with_user(1);
999 assert!(guard.check(&req).is_ok());
1000 }
1001
1002 #[test]
1003 fn test_auth_guard_fails_when_not_authenticated() {
1004 let guard = AuthGuard::new();
1005 let req = make_request();
1006 let result = guard.check(&req);
1007 assert!(result.is_err());
1008 let err = result.unwrap_err();
1009 assert_eq!(err.code, ErrorCode::NotLogin);
1010 assert_eq!(err.msg, "not_login");
1011 }
1012
1013 #[test]
1018 fn test_admin_guard_new() {
1019 let _guard = AdminGuard::new();
1020 }
1021
1022 #[test]
1023 fn test_admin_guard_fails_when_not_logged_in() {
1024 let guard = AdminGuard::new();
1025 let req = make_request();
1026 let result = guard.check(&req);
1027 assert!(result.is_err());
1028 let err = result.unwrap_err();
1029 assert_eq!(err.code, ErrorCode::NotLogin);
1031 }
1032
1033 #[test]
1034 fn test_admin_guard_fails_when_logged_in_but_no_user_context() {
1035 let guard = AdminGuard::new();
1036 let req = make_request_with_user(1);
1037 let result = guard.check(&req);
1038 assert!(result.is_err());
1039 let err = result.unwrap_err();
1040 assert_eq!(err.code, ErrorCode::Forbidden);
1042 }
1043
1044 #[test]
1045 fn test_admin_guard_fails_when_not_super() {
1046 let guard = AdminGuard::new();
1047 let user_ctx = UserContext::new(1).with_super(false);
1048 let req = make_request_with_context(user_ctx);
1049 let result = guard.check(&req);
1050 assert!(result.is_err());
1051 let err = result.unwrap_err();
1052 assert_eq!(err.code, ErrorCode::Forbidden);
1053 }
1054
1055 #[test]
1056 fn test_admin_guard_passes_when_super() {
1057 let guard = AdminGuard::new();
1058 let user_ctx = UserContext::new(1).with_super(true);
1059 let req = make_request_with_context(user_ctx);
1060 assert!(guard.check(&req).is_ok());
1061 }
1062
1063 #[test]
1068 fn test_permission_guard_new() {
1069 let guard = PermissionGuard::new("user/list");
1070 assert_eq!(guard.permission, "user/list");
1071 }
1072
1073 #[test]
1074 fn test_permission_guard_fails_when_not_logged_in() {
1075 let guard = PermissionGuard::new("user/list");
1076 let req = make_request();
1077 let result = guard.check(&req);
1078 assert!(result.is_err());
1079 let err = result.unwrap_err();
1080 assert_eq!(err.code, ErrorCode::NotLogin);
1081 }
1082
1083 #[test]
1084 fn test_permission_guard_fails_when_no_user_context() {
1085 let guard = PermissionGuard::new("user/list");
1086 let req = make_request_with_user(1);
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_fails_when_no_permission() {
1095 let guard = PermissionGuard::new("user/delete");
1096 let user_ctx = UserContext::new(1).with_permissions(vec!["user/list".to_string()]);
1097 let req = make_request_with_context(user_ctx);
1098 let result = guard.check(&req);
1099 assert!(result.is_err());
1100 let err = result.unwrap_err();
1101 assert_eq!(err.code, ErrorCode::Forbidden);
1102 }
1103
1104 #[test]
1105 fn test_permission_guard_passes_when_has_exact_permission() {
1106 let guard = PermissionGuard::new("user/list");
1107 let user_ctx = UserContext::new(1).with_permissions(vec!["user/list".to_string()]);
1108 let req = make_request_with_context(user_ctx);
1109 assert!(guard.check(&req).is_ok());
1110 }
1111
1112 #[test]
1113 fn test_permission_guard_passes_when_has_wildcard_permission() {
1114 let guard = PermissionGuard::new("user/list");
1115 let user_ctx = UserContext::new(1).with_permissions(vec!["user/*".to_string()]);
1116 let req = make_request_with_context(user_ctx);
1117 assert!(guard.check(&req).is_ok());
1118 }
1119
1120 #[test]
1121 fn test_permission_guard_passes_when_super() {
1122 let guard = PermissionGuard::new("user/delete");
1124 let user_ctx = UserContext::new(1).with_super(true);
1125 let req = make_request_with_context(user_ctx);
1126 assert!(guard.check(&req).is_ok());
1127 }
1128
1129 #[test]
1130 fn test_permission_guard_passes_when_super_without_permissions() {
1131 let guard = PermissionGuard::new("system/config");
1133 let user_ctx = UserContext::new(1).with_super(true);
1134 let req = make_request_with_context(user_ctx);
1135 assert!(guard.check(&req).is_ok());
1136 }
1137
1138 #[test]
1143 fn test_role_guard_new() {
1144 let guard = RoleGuard::new("admin");
1145 assert_eq!(guard.role, "admin");
1146 }
1147
1148 #[test]
1149 fn test_role_guard_fails_when_not_logged_in() {
1150 let guard = RoleGuard::new("admin");
1151 let req = make_request();
1152 let result = guard.check(&req);
1153 assert!(result.is_err());
1154 let err = result.unwrap_err();
1155 assert_eq!(err.code, ErrorCode::NotLogin);
1156 }
1157
1158 #[test]
1159 fn test_role_guard_fails_when_no_user_context() {
1160 let guard = RoleGuard::new("admin");
1161 let req = make_request_with_user(1);
1162 let result = guard.check(&req);
1163 assert!(result.is_err());
1164 let err = result.unwrap_err();
1165 assert_eq!(err.code, ErrorCode::Forbidden);
1166 }
1167
1168 #[test]
1169 fn test_role_guard_fails_when_no_role() {
1170 let guard = RoleGuard::new("admin");
1171 let user_ctx = UserContext::new(1).with_roles(vec!["editor".to_string()]);
1172 let req = make_request_with_context(user_ctx);
1173 let result = guard.check(&req);
1174 assert!(result.is_err());
1175 }
1176
1177 #[test]
1178 fn test_role_guard_passes_when_has_role() {
1179 let guard = RoleGuard::new("admin");
1180 let user_ctx = UserContext::new(1).with_roles(vec!["admin".to_string()]);
1181 let req = make_request_with_context(user_ctx);
1182 assert!(guard.check(&req).is_ok());
1183 }
1184
1185 #[test]
1186 fn test_role_guard_passes_when_super() {
1187 let guard = RoleGuard::new("admin");
1189 let user_ctx = UserContext::new(1).with_super(true);
1190 let req = make_request_with_context(user_ctx);
1191 assert!(guard.check(&req).is_ok());
1192 }
1193
1194 #[test]
1199 fn test_guard_chain_new() {
1200 let chain = GuardChain::new();
1201 assert!(chain.guards.is_empty());
1202 }
1203
1204 #[test]
1205 fn test_guard_chain_default() {
1206 let chain = GuardChain::default();
1207 assert!(chain.guards.is_empty());
1208 }
1209
1210 #[test]
1211 fn test_guard_chain_with_guard() {
1212 let chain = GuardChain::new()
1213 .with_guard(Arc::new(AuthGuard))
1214 .with_guard(Arc::new(AdminGuard));
1215 assert_eq!(chain.guards.len(), 2);
1216 }
1217
1218 #[test]
1219 fn test_guard_chain_from_guards() {
1220 let guards: Vec<Arc<dyn Guard>> = vec![Arc::new(AuthGuard), Arc::new(AdminGuard)];
1221 let chain = GuardChain::from_guards(guards);
1222 assert_eq!(chain.guards.len(), 2);
1223 }
1224
1225 #[test]
1226 fn test_guard_chain_empty_passes() {
1227 let chain = GuardChain::new();
1229 let req = make_request();
1230 assert!(chain.check(&req).is_ok());
1231 }
1232
1233 #[test]
1234 fn test_guard_chain_single_guard_passes() {
1235 let chain = GuardChain::new().with_guard(Arc::new(AuthGuard));
1236 let req = make_request_with_user(1);
1237 assert!(chain.check(&req).is_ok());
1238 }
1239
1240 #[test]
1241 fn test_guard_chain_single_guard_fails() {
1242 let chain = GuardChain::new().with_guard(Arc::new(AuthGuard));
1243 let req = make_request();
1244 assert!(chain.check(&req).is_err());
1245 }
1246
1247 #[test]
1248 fn test_guard_chain_and_semantics_all_pass() {
1249 let chain = GuardChain::new()
1250 .with_guard(Arc::new(AuthGuard))
1251 .with_guard(Arc::new(AdminGuard));
1252 let user_ctx = UserContext::new(1).with_super(true);
1253 let req = make_request_with_context(user_ctx);
1254 assert!(chain.check(&req).is_ok());
1255 }
1256
1257 #[test]
1258 fn test_guard_chain_and_semantics_first_fails() {
1259 let chain = GuardChain::new()
1261 .with_guard(Arc::new(AuthGuard))
1262 .with_guard(Arc::new(AdminGuard));
1263 let req = make_request();
1264 let result = chain.check(&req);
1265 assert!(result.is_err());
1266 let err = result.unwrap_err();
1267 assert_eq!(err.code, ErrorCode::NotLogin);
1269 }
1270
1271 #[test]
1272 fn test_guard_chain_and_semantics_second_fails() {
1273 let chain = GuardChain::new()
1275 .with_guard(Arc::new(AuthGuard))
1276 .with_guard(Arc::new(AdminGuard));
1277 let user_ctx = UserContext::new(1).with_super(false);
1279 let req = make_request_with_context(user_ctx);
1280 let result = chain.check(&req);
1281 assert!(result.is_err());
1282 let err = result.unwrap_err();
1283 assert_eq!(err.code, ErrorCode::Forbidden);
1285 }
1286
1287 #[test]
1288 fn test_guard_chain_and_semantics_short_circuit() {
1289 struct FailGuard;
1291 impl Guard for FailGuard {
1292 fn check(&self, _req: &Request) -> Result<(), GuardError> {
1293 Err(GuardError::forbidden("fail_guard_called"))
1294 }
1295 }
1296 struct PanicGuard;
1297 impl Guard for PanicGuard {
1298 fn check(&self, _req: &Request) -> Result<(), GuardError> {
1299 panic!("PanicGuard should not be called due to short-circuit");
1300 }
1301 }
1302 let chain = GuardChain::new()
1303 .with_guard(Arc::new(FailGuard))
1304 .with_guard(Arc::new(PanicGuard));
1305 let req = make_request();
1306 let result = chain.check(&req);
1307 assert!(result.is_err());
1308 assert_eq!(result.unwrap_err().msg, "fail_guard_called");
1309 }
1310
1311 #[test]
1312 fn test_guard_chain_order_matters() {
1313 let chain = GuardChain::new()
1315 .with_guard(Arc::new(AuthGuard))
1316 .with_guard(Arc::new(PermissionGuard::new("user/list")));
1317 let req = make_request();
1319 let result = chain.check(&req);
1320 assert!(result.is_err());
1321 let err = result.unwrap_err();
1322 assert_eq!(err.code, ErrorCode::NotLogin);
1323 }
1324
1325 #[test]
1326 fn test_guard_chain_multiple_permissions() {
1327 let chain = GuardChain::new()
1328 .with_guard(Arc::new(AuthGuard))
1329 .with_guard(Arc::new(PermissionGuard::new("user/list")))
1330 .with_guard(Arc::new(PermissionGuard::new("user/save")));
1331 let user_ctx = UserContext::new(1)
1332 .with_permissions(vec!["user/list".to_string(), "user/save".to_string()]);
1333 let req = make_request_with_context(user_ctx);
1334 assert!(chain.check(&req).is_ok());
1335 }
1336
1337 #[test]
1338 fn test_guard_chain_mixed_guard_types() {
1339 let chain = GuardChain::new()
1340 .with_guard(Arc::new(AuthGuard))
1341 .with_guard(Arc::new(RoleGuard::new("editor")))
1342 .with_guard(Arc::new(PermissionGuard::new("post/list")));
1343 let user_ctx = UserContext::new(1)
1344 .with_roles(vec!["editor".to_string()])
1345 .with_permissions(vec!["post/list".to_string()]);
1346 let req = make_request_with_context(user_ctx);
1347 assert!(chain.check(&req).is_ok());
1348 }
1349
1350 #[test]
1355 fn test_check_guards_empty() {
1356 let req = make_request();
1357 assert!(check_guards(&req, &[]).is_ok());
1358 }
1359
1360 #[test]
1361 fn test_check_guards_all_pass() {
1362 let guards: Vec<Arc<dyn Guard>> = vec![Arc::new(AuthGuard)];
1363 let req = make_request_with_user(1);
1364 assert!(check_guards(&req, &guards).is_ok());
1365 }
1366
1367 #[test]
1368 fn test_check_guards_fails() {
1369 let guards: Vec<Arc<dyn Guard>> = vec![Arc::new(AuthGuard)];
1370 let req = make_request();
1371 assert!(check_guards(&req, &guards).is_err());
1372 }
1373
1374 #[tokio::test]
1379 async fn test_guard_middleware_passes() {
1380 let app = build_app(Arc::new(AuthGuard));
1381 let req = Request::builder()
1383 .method("GET")
1384 .uri("/protected")
1385 .extension(AuthenticatedUser { user_id: 1 })
1386 .body(Body::empty())
1387 .unwrap();
1388 let resp = app.oneshot(req).await.unwrap();
1389 assert_eq!(resp.status(), StatusCode::OK);
1390 }
1391
1392 #[tokio::test]
1393 async fn test_guard_middleware_fails_not_login() {
1394 let app = build_app(Arc::new(AuthGuard));
1395 let req = Request::builder()
1396 .method("GET")
1397 .uri("/protected")
1398 .body(Body::empty())
1399 .unwrap();
1400 let resp = app.oneshot(req).await.unwrap();
1401 assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
1403 let body = read_body(resp).await;
1404 let json: serde_json::Value = serde_json::from_str(&body).unwrap();
1405 assert_eq!(json["code"], -1);
1406 assert_eq!(json["msg"], "not_login");
1407 }
1408
1409 #[tokio::test]
1410 async fn test_guard_middleware_fails_forbidden() {
1411 let app = build_app(Arc::new(AdminGuard));
1412 let req = Request::builder()
1414 .method("GET")
1415 .uri("/protected")
1416 .extension(AuthenticatedUser { user_id: 1 })
1417 .extension(UserContext::new(1).with_super(false))
1418 .body(Body::empty())
1419 .unwrap();
1420 let resp = app.oneshot(req).await.unwrap();
1421 assert_eq!(resp.status(), StatusCode::FORBIDDEN);
1423 let body = read_body(resp).await;
1424 let json: serde_json::Value = serde_json::from_str(&body).unwrap();
1425 assert_eq!(json["code"], 403);
1426 }
1427
1428 #[tokio::test]
1429 async fn test_guard_middleware_with_chain() {
1430 let chain = GuardChain::new()
1431 .with_guard(Arc::new(AuthGuard))
1432 .with_guard(Arc::new(AdminGuard));
1433 let app = build_app(Arc::new(chain));
1434
1435 let req = Request::builder()
1437 .method("GET")
1438 .uri("/protected")
1439 .extension(AuthenticatedUser { user_id: 1 })
1440 .extension(UserContext::new(1).with_super(true))
1441 .body(Body::empty())
1442 .unwrap();
1443 let resp = app.clone().oneshot(req).await.unwrap();
1444 assert_eq!(resp.status(), StatusCode::OK);
1445
1446 let req = Request::builder()
1448 .method("GET")
1449 .uri("/protected")
1450 .extension(AuthenticatedUser { user_id: 2 })
1451 .extension(UserContext::new(2).with_super(false))
1452 .body(Body::empty())
1453 .unwrap();
1454 let resp = app.oneshot(req).await.unwrap();
1455 assert_eq!(resp.status(), StatusCode::FORBIDDEN);
1456 }
1457
1458 #[tokio::test]
1459 async fn test_guard_middleware_permission_guard() {
1460 let chain = GuardChain::new()
1461 .with_guard(Arc::new(AuthGuard))
1462 .with_guard(Arc::new(PermissionGuard::new("user/list")));
1463 let app = build_app(Arc::new(chain));
1464
1465 let req = Request::builder()
1467 .method("GET")
1468 .uri("/protected")
1469 .extension(AuthenticatedUser { user_id: 1 })
1470 .extension(UserContext::new(1).with_permissions(vec!["user/list".to_string()]))
1471 .body(Body::empty())
1472 .unwrap();
1473 let resp = app.clone().oneshot(req).await.unwrap();
1474 assert_eq!(resp.status(), StatusCode::OK);
1475
1476 let req = Request::builder()
1478 .method("GET")
1479 .uri("/protected")
1480 .extension(AuthenticatedUser { user_id: 2 })
1481 .extension(UserContext::new(2).with_permissions(vec!["order/list".to_string()]))
1482 .body(Body::empty())
1483 .unwrap();
1484 let resp = app.oneshot(req).await.unwrap();
1485 assert_eq!(resp.status(), StatusCode::FORBIDDEN);
1486 }
1487
1488 #[test]
1495 fn test_php_alignment_is_super_bypass() {
1496 let permission_guard = PermissionGuard::new("system/config");
1497 let role_guard = RoleGuard::new("admin");
1498 let admin_guard = AdminGuard::new();
1499
1500 let user_ctx = UserContext::new(1).with_super(true);
1502 let req = make_request_with_context(user_ctx);
1503
1504 assert!(permission_guard.check(&req).is_ok());
1505 assert!(role_guard.check(&req).is_ok());
1506 assert!(admin_guard.check(&req).is_ok());
1507 }
1508
1509 #[test]
1514 fn test_php_alignment_error_codes() {
1515 let err = GuardError::not_login("not_login");
1517 assert_eq!(err.code.as_i32(), -1);
1518
1519 let err = GuardError::user_disabled("您已离职");
1521 assert_eq!(err.code.as_i32(), -3);
1522
1523 let err = GuardError::forbidden("无权限访问");
1526 assert_eq!(err.code.as_i32(), 403);
1527 }
1528
1529 #[test]
1534 fn test_php_alignment_check_login() {
1535 let guard = AuthGuard::new();
1536
1537 let req = make_request();
1539 let result = guard.check(&req);
1540 assert!(matches!(
1541 result,
1542 Err(GuardError {
1543 code: ErrorCode::NotLogin,
1544 ..
1545 })
1546 ));
1547
1548 let req = make_request_with_user(1);
1550 assert!(guard.check(&req).is_ok());
1551 }
1552
1553 #[test]
1556 fn test_php_alignment_wildcard_permission() {
1557 let ctx = UserContext::new(1).with_permissions(vec!["user/*".to_string()]);
1558 assert!(ctx.has_permission("user/list"));
1559 assert!(ctx.has_permission("user/save"));
1560 assert!(ctx.has_permission("user/delete"));
1561 assert!(!ctx.has_permission("order/list"));
1562 }
1563
1564 #[test]
1567 fn test_php_alignment_multiple_roles() {
1568 let ctx = UserContext::new(1).with_roles(vec!["editor".to_string(), "viewer".to_string()]);
1569 assert!(ctx.has_role("editor"));
1570 assert!(ctx.has_role("viewer"));
1571 assert!(!ctx.has_role("admin"));
1572 }
1573
1574 #[tokio::test]
1577 async fn test_php_alignment_response_format() {
1578 let err = GuardError::user_disabled("您已离职,无权使用本系统!");
1579 let resp = err.into_response();
1580 let body = read_body(resp).await;
1581 let json: serde_json::Value = serde_json::from_str(&body).unwrap();
1582 assert_eq!(json["code"], -3);
1584 assert_eq!(json["msg"], "您已离职,无权使用本系统!");
1585 assert_eq!(json["data"], serde_json::json!({}));
1586 }
1587}