1use std::sync::Arc;
7
8pub fn match_path(path: &str, pattern: &str) -> bool {
30 if pattern == "/**" {
31 return true;
32 }
33 if pattern.ends_with("/**") {
34 let prefix = &pattern[..pattern.len() - 3];
35 return path.starts_with(prefix);
36 }
37 if pattern.starts_with("*") {
38 let suffix = &pattern[1..];
39 return path.ends_with(suffix);
40 }
41 if pattern.ends_with("/*") {
42 let prefix = &pattern[..pattern.len() - 2];
43 if path.starts_with(prefix) {
44 let suffix = &path[prefix.len()..];
45 return !suffix.contains('/') || suffix == "/";
46 }
47 return false;
48 }
49 path == pattern
50}
51
52pub fn match_any(path: &str, patterns: &[&str]) -> bool {
55 patterns.iter().any(|p| match_path(path, p))
56}
57
58pub fn need_auth(path: &str, include: &[&str], exclude: &[&str]) -> bool {
64 match_any(path, include) && !match_any(path, exclude)
65}
66
67#[derive(Clone)]
73pub struct PathAuthConfig {
74 include: Vec<String>,
77 exclude: Vec<String>,
80 validator: Option<Arc<dyn Fn(&str) -> bool + Send + Sync>>,
83}
84
85impl PathAuthConfig {
86 pub fn new() -> Self {
89 Self {
90 include: Vec::new(),
91 exclude: Vec::new(),
92 validator: None,
93 }
94 }
95
96 pub fn include(mut self, patterns: Vec<String>) -> Self {
99 self.include = patterns;
100 self
101 }
102
103 pub fn exclude(mut self, patterns: Vec<String>) -> Self {
106 self.exclude = patterns;
107 self
108 }
109
110 pub fn validator<F>(mut self, f: F) -> Self
113 where
114 F: Fn(&str) -> bool + Send + Sync + 'static,
115 {
116 self.validator = Some(Arc::new(f));
117 self
118 }
119
120 pub fn check(&self, path: &str) -> bool {
123 let inc: Vec<&str> = self.include.iter().map(|s| s.as_str()).collect();
124 let exc: Vec<&str> = self.exclude.iter().map(|s| s.as_str()).collect();
125 need_auth(path, &inc, &exc)
126 }
127
128 pub fn validate_login_id(&self, login_id: &str) -> bool {
131 self.validator.as_ref().map_or(true, |v| v(login_id))
132 }
133}
134
135impl Default for PathAuthConfig {
136 fn default() -> Self {
137 Self::new()
138 }
139}
140
141use crate::{SaTokenManager, TokenValue, SaTokenContext, token::TokenInfo};
142
143pub struct AuthResult {
146 pub need_auth: bool,
149 pub token: Option<TokenValue>,
152 pub token_info: Option<TokenInfo>,
155 pub is_valid: bool,
158}
159
160impl AuthResult {
161 pub fn should_reject(&self) -> bool {
164 self.need_auth && (!self.is_valid || self.token.is_none())
165 }
166
167 pub fn login_id(&self) -> Option<&str> {
170 self.token_info.as_ref().map(|t| t.login_id.as_str())
171 }
172}
173
174pub async fn process_auth(
187 path: &str,
188 token_str: Option<String>,
189 config: &PathAuthConfig,
190 manager: &SaTokenManager,
191) -> AuthResult {
192 let need_auth = config.check(path);
193
194 let token = token_str.map(TokenValue::new);
195
196 let (is_valid, token_info) = if let Some(ref t) = token {
197 let valid = manager.is_valid(t).await;
198 let info = if valid {
199 manager.get_token_info(t).await.ok()
200 } else {
201 None
202 };
203 (valid, info)
204 } else {
205 (false, None)
206 };
207
208 let is_valid = is_valid && if need_auth {
209 token_info.as_ref().map_or(false, |info| config.validate_login_id(&info.login_id))
210 } else {
211 true
212 };
213
214 AuthResult {
215 need_auth,
216 token,
217 token_info,
218 is_valid,
219 }
220}
221
222pub fn create_context(result: &AuthResult) -> SaTokenContext {
225 let mut ctx = SaTokenContext::new();
226 if let (Some(token), Some(info)) = (&result.token, &result.token_info) {
227 ctx.token = Some(token.clone());
228 ctx.token_info = Some(Arc::new(info.clone()));
229 ctx.login_id = Some(info.login_id.clone());
230 }
231 ctx
232}
233