1use std::sync::Arc;
6use chrono::{DateTime, Duration, Utc};
7use sa_token_adapter::storage::SaStorage;
8use crate::config::{LogoutMode, ReplacedLoginExitMode, SaTokenConfig};
9use crate::error::{SaTokenError, SaTokenResult};
10use crate::token::{TokenInfo, TokenValue, TokenGenerator};
11use crate::token::map::{
12 TOKEN_MAP_BE_REPLACED, TOKEN_MAP_KICK_OUT, is_kick_out_marker, is_replaced_marker,
13};
14use crate::session::SaSession;
15use crate::event::{SaTokenEventBus, SaTokenEvent};
16use crate::online::OnlineManager;
17use crate::distributed::DistributedSessionManager;
18use crate::nonce::NonceManager;
19use crate::refresh::RefreshTokenManager;
20use crate::stp_interface::StpInterface;
21
22#[derive(Clone)]
24pub struct SaTokenManager {
25 pub(crate) storage: Arc<dyn SaStorage>,
27 pub config: SaTokenConfig,
29 pub(crate) event_bus: SaTokenEventBus,
31 online_manager: Option<Arc<OnlineManager>>,
33 distributed_manager: Option<Arc<DistributedSessionManager>>,
35 pub(crate) stp_interface: Option<Arc<dyn StpInterface>>,
37}
38
39impl SaTokenManager {
40 pub fn new(storage: Arc<dyn SaStorage>, config: SaTokenConfig) -> Self {
42 Self {
43 storage,
44 config,
45 event_bus: SaTokenEventBus::new(),
46 online_manager: None,
47 distributed_manager: None,
48 stp_interface: None,
49 }
50 }
51
52 pub fn with_stp_interface(mut self, iface: Arc<dyn StpInterface>) -> Self {
53 self.stp_interface = Some(iface);
54 self
55 }
56
57 pub fn with_online_manager(mut self, manager: Arc<OnlineManager>) -> Self {
58 self.online_manager = Some(manager);
59 self
60 }
61
62 pub fn with_distributed_manager(mut self, manager: Arc<DistributedSessionManager>) -> Self {
63 self.distributed_manager = Some(manager);
64 self
65 }
66
67 pub fn online_manager(&self) -> Option<&Arc<OnlineManager>> {
68 self.online_manager.as_ref()
69 }
70
71 pub fn distributed_manager(&self) -> Option<&Arc<DistributedSessionManager>> {
72 self.distributed_manager.as_ref()
73 }
74
75 pub fn event_bus(&self) -> &SaTokenEventBus {
77 &self.event_bus
78 }
79
80 pub async fn login(&self, login_id: impl Into<String>) -> SaTokenResult<TokenValue> {
82 self.login_with_options(login_id, None, None, None, None, None).await
83 }
84
85 pub async fn login_with_options(
107 &self,
108 login_id: impl Into<String>,
109 login_type: Option<String>,
110 device: Option<String>,
111 extra_data: Option<serde_json::Value>,
112 nonce: Option<String>,
113 expire_time: Option<DateTime<Utc>>,
114 ) -> SaTokenResult<TokenValue> {
115 let login_id = login_id.into();
116
117 let token = match &extra_data {
119 Some(extra) => TokenGenerator::generate_with_login_id_and_extra(&self.config, &login_id, extra),
120 None => TokenGenerator::generate_with_login_id(&self.config, &login_id),
121 };
122
123 let mut token_info = TokenInfo::new(token.clone(), login_id.clone());
125
126 token_info.login_type = login_type.unwrap_or_else(|| "default".to_string());
128
129 if let Some(device_str) = device {
131 token_info.device = Some(device_str);
132 }
133
134 if let Some(extra) = extra_data {
136 token_info.extra_data = Some(extra);
137 }
138
139 if let Some(nonce_str) = nonce {
141 token_info.nonce = Some(nonce_str);
142 }
143
144 if let Some(custom_expire_time) = expire_time {
146 token_info.expire_time = Some(custom_expire_time);
147 }
148 self.login_with_token_info(token_info).await
152 }
153
154 pub async fn login_with_token_info(&self, mut token_info: TokenInfo) -> SaTokenResult<TokenValue> {
183 let login_id = token_info.login_id.clone();
184
185 let token = if token_info.token.as_str().is_empty() {
187 TokenGenerator::generate_with_login_id(&self.config, &login_id)
188 } else {
189 token_info.token.clone()
190 };
191
192 token_info.token = token.clone();
194
195 token_info.update_active_time();
197
198 let now = Utc::now();
200 if token_info.expire_time.is_none()
201 && let Some(timeout) = self.config.timeout_duration() {
202 token_info.expire_time = Some(now + Duration::from_std(timeout).unwrap());
203 }
204
205 if token_info.login_type.is_empty() {
207 token_info.login_type = "default".to_string();
208 }
209
210 let login_token_key = self.login_token_mapping_key(&login_id, &token_info.login_type);
212
213 if self.config.is_share {
215 if let Ok(Some(existing)) = self.storage.get(&login_token_key).await {
216 let existing_token = TokenValue::new(existing);
217 if self.is_valid(&existing_token).await {
218 return Ok(existing_token);
219 }
220 }
221 }
222
223 if self.config.enable_nonce
225 && let Some(ref nonce_str) = token_info.nonce {
226 let nonce_timeout = if self.config.nonce_timeout > 0 {
227 self.config.nonce_timeout
228 } else {
229 self.config.timeout
230 };
231 let nonce_mgr = NonceManager::new(self.storage.clone(), nonce_timeout);
232 nonce_mgr.validate_and_consume(nonce_str, &login_id).await?;
233 }
234
235 if !self.config.is_concurrent
237 && let Ok(Some(old_token)) = self.storage.get(&login_token_key).await
238 && old_token != token.as_str() {
239 match self.config.replaced_login_exit_mode {
240 ReplacedLoginExitMode::OldDevice => {
241 self.replaced_by_token(&TokenValue::new(old_token)).await?;
242 }
243 ReplacedLoginExitMode::NewDevice => {
244 return Err(SaTokenError::AccountReplaced);
245 }
246 }
247 }
248
249 let refresh_mgr = if self.config.enable_refresh_token {
251 Some(RefreshTokenManager::new(
252 self.storage.clone(),
253 Arc::new(self.config.clone()),
254 ))
255 } else {
256 None
257 };
258 if let Some(ref mgr) = refresh_mgr {
259 let rt = mgr.generate(&login_id);
260 token_info.refresh_token = Some(rt);
261 if self.config.refresh_token_timeout > 0 {
262 token_info.refresh_token_expire_time = Some(
263 Utc::now() + Duration::seconds(self.config.refresh_token_timeout),
264 );
265 }
266 }
267
268 let key = self.config.make_key("token:", token.as_str());
270 let value = serde_json::to_string(&token_info)
271 .map_err(SaTokenError::SerializationError)?;
272
273 self.storage.set(&key, &value, self.config.timeout_duration()).await
274 .map_err(|e| SaTokenError::StorageError(e.to_string()))?;
275
276 self.storage.set(&login_token_key, token.as_str(), self.config.timeout_duration()).await
278 .map_err(|e| SaTokenError::StorageError(e.to_string()))?;
279
280 self.save_token_id_mapping(token.as_str(), &login_id).await?;
282
283 let account_ns = self.account_ns(&token_info.login_type, &login_id);
284
285 self.append_token_index(&account_ns, token.as_str()).await?;
287
288 {
290 let mut session = self.get_session(&account_ns).await?;
291 let mut terminal = crate::session::SaTerminalInfo::new(
292 token.as_str(),
293 token_info.device.as_deref().unwrap_or(""),
294 );
295 if let Some(extra) = token_info.extra_data.clone() {
296 terminal = terminal.with_extra_data(extra);
297 }
298 session.add_terminal(terminal);
299 self.save_session(&session).await?;
300 }
301
302 self.enforce_max_login_count(&account_ns).await?;
303
304 if self.config.right_now_create_token_session {
305 let session = SaSession::new(format!("token-session:{}", token.as_str()));
306 let _ = self.save_token_session(&token, &session).await;
307 }
308
309 if let Some(ref mgr) = refresh_mgr
311 && let Some(ref rt) = token_info.refresh_token {
312 mgr.store_with_extra(
313 rt,
314 token.as_str(),
315 &login_id,
316 token_info.extra_data.as_ref(),
317 )
318 .await?;
319 }
320
321 let event = SaTokenEvent::login(login_id.clone(), token.as_str())
323 .with_login_type(&token_info.login_type);
324 self.event_bus.publish(event).await;
325
326 Ok(token)
327 }
328
329 pub async fn logout(&self, token: &TokenValue) -> SaTokenResult<()> {
331 self.logout_internal(token, LogoutMode::Logout, self.config.is_logout_keep_token_session)
332 .await
333 }
334
335 pub async fn kick_out_by_token(&self, token: &TokenValue) -> SaTokenResult<()> {
337 self.logout_internal(token, LogoutMode::KickOut, self.config.is_logout_keep_token_session)
338 .await
339 }
340
341 pub async fn replaced_by_token(&self, token: &TokenValue) -> SaTokenResult<()> {
343 self.logout_internal(token, LogoutMode::Replaced, self.config.is_logout_keep_token_session)
344 .await
345 }
346
347 async fn logout_internal(
348 &self,
349 token: &TokenValue,
350 mode: LogoutMode,
351 keep_token_session: bool,
352 ) -> SaTokenResult<()> {
353 tracing::debug!("Manager: logout_internal mode={:?}, token={}", mode, token);
354
355 let key = self.config.make_key("token:", token.as_str());
356 let token_info_str = self
357 .storage
358 .get(&key)
359 .await
360 .map_err(|e| SaTokenError::StorageError(e.to_string()))?;
361
362 let token_info = token_info_str
363 .as_ref()
364 .and_then(|value| serde_json::from_str::<TokenInfo>(value).ok());
365
366 let login_id = if let Some(ref info) = token_info {
367 Some(info.login_id.clone())
368 } else if let Ok(Some(mapped)) = self.get_token_id_mapping(token.as_str()).await {
369 if is_kick_out_marker(&mapped) || is_replaced_marker(&mapped) {
370 None
371 } else {
372 Some(mapped)
373 }
374 } else {
375 None
376 };
377
378 if mode == LogoutMode::Logout {
379 self.storage
380 .delete(&key)
381 .await
382 .map_err(|e| SaTokenError::StorageError(e.to_string()))?;
383 self.delete_token_id_mapping(token.as_str()).await?;
384 } else if mode == LogoutMode::KickOut {
385 self.update_token_id_mapping(token.as_str(), TOKEN_MAP_KICK_OUT)
386 .await?;
387 } else {
388 self.update_token_id_mapping(token.as_str(), TOKEN_MAP_BE_REPLACED)
389 .await?;
390 }
391
392 if !keep_token_session {
393 let _ = self.delete_token_session(token).await;
394 }
395
396 if let Some(info) = token_info {
397 let account_ns = self.account_ns(&info.login_type, &info.login_id);
398
399 if let Ok(mut session) = self.get_session(&account_ns).await {
400 if session.remove_terminal(token.as_str()).is_some() {
401 if session.terminal_count() == 0 && mode != LogoutMode::Replaced {
402 let _ = self.delete_session(&account_ns).await;
403 } else {
404 let _ = self.save_session(&session).await;
405 }
406 }
407 }
408
409 let login_token_key =
410 self.login_token_mapping_key(&info.login_id, &info.login_type);
411 if mode == LogoutMode::Logout {
412 if let Ok(Some(mapped)) = self.storage.get(&login_token_key).await
413 && mapped == token.as_str() {
414 let _ = self.storage.delete(&login_token_key).await;
415 }
416 let _ = self.remove_token_index(&account_ns, token.as_str()).await;
417 }
418
419 if let Some(online_mgr) = &self.online_manager {
420 online_mgr.mark_offline(&info.login_id, token.as_str()).await;
421 }
422
423 let event = match mode {
424 LogoutMode::Logout => {
425 SaTokenEvent::logout(&info.login_id, token.as_str())
426 .with_login_type(&info.login_type)
427 }
428 LogoutMode::KickOut => {
429 SaTokenEvent::kick_out(&info.login_id, token.as_str())
430 .with_login_type(&info.login_type)
431 }
432 LogoutMode::Replaced => {
433 SaTokenEvent::replaced(&info.login_id, token.as_str())
434 .with_login_type(&info.login_type)
435 }
436 };
437 self.event_bus.publish(event).await;
438 } else if let Some(id) = login_id {
439 let event = match mode {
440 LogoutMode::Logout => SaTokenEvent::logout(&id, token.as_str()),
441 LogoutMode::KickOut => SaTokenEvent::kick_out(&id, token.as_str()),
442 LogoutMode::Replaced => SaTokenEvent::replaced(&id, token.as_str()),
443 };
444 self.event_bus.publish(event).await;
445 }
446
447 Ok(())
448 }
449
450 pub async fn logout_by_login_id(&self, login_id: &str) -> SaTokenResult<()> {
452 let idx_key = self.config.make_key("login:tokens:", login_id);
454 let tokens = self.load_string_list(&idx_key).await.unwrap_or_default();
455 if !tokens.is_empty() {
456 for t in tokens {
457 let _ = self.logout(&TokenValue::new(t)).await;
458 }
459 return Ok(());
460 }
461
462 let token_prefix = format!("{}token:", self.config.key_prefix());
464
465 if let Ok(keys) = self.storage.keys(&format!("{}*", token_prefix)).await {
466 for key in keys {
467 if let Ok(Some(token_info_str)) = self.storage.get(&key).await {
468 if let Ok(token_info) = serde_json::from_str::<TokenInfo>(&token_info_str) {
469 let ti_ns = self.account_ns(&token_info.login_type, &token_info.login_id);
470 if ti_ns == login_id {
471 let token_str = key[token_prefix.len()..].to_string();
472 let _ = self.logout(&TokenValue::new(token_str)).await;
473 }
474 }
475 }
476 }
477 }
478
479 Ok(())
480 }
481
482 pub async fn get_token_info(&self, token: &TokenValue) -> SaTokenResult<TokenInfo> {
484 if let Some(mapped) = self.get_token_id_mapping(token.as_str()).await? {
485 if is_kick_out_marker(&mapped) {
486 return Err(SaTokenError::AccountKickedOut);
487 }
488 if is_replaced_marker(&mapped) {
489 return Err(SaTokenError::AccountReplaced);
490 }
491 }
492
493 let key = self.config.make_key("token:", token.as_str());
494 let value = self.storage.get(&key).await
495 .map_err(|e| SaTokenError::StorageError(e.to_string()))?
496 .ok_or(SaTokenError::TokenNotFound)?;
497
498 let token_info: TokenInfo = serde_json::from_str(&value)
499 .map_err(SaTokenError::SerializationError)?;
500
501 if token_info.is_expired() {
503 self.logout(token).await?;
504 return Err(SaTokenError::TokenExpired);
505 }
506
507 if token_info.is_freeze(self.config.active_timeout) {
509 return Err(SaTokenError::TokenInactive);
510 }
511
512 if self.config.auto_renew {
514 let renew_timeout = if self.config.active_timeout > 0 {
515 self.config.active_timeout
516 } else {
517 self.config.timeout
518 };
519
520 let mut renewed = token_info.clone();
521 renewed.update_active_time();
522 if renew_timeout > 0 {
523 renewed.expire_time =
524 Some(Utc::now() + Duration::seconds(renew_timeout));
525 }
526
527 let key = self.config.make_key("token:", token.as_str());
528 if let Ok(value) = serde_json::to_string(&renewed) {
529 let storage_ttl = if renew_timeout > 0 {
530 Some(std::time::Duration::from_secs(renew_timeout as u64))
531 } else {
532 self.config.timeout_duration()
533 };
534 let _ = self.storage.set(&key, &value, storage_ttl).await;
535 }
536 return Ok(renewed);
537 }
538
539 Ok(token_info)
540 }
541
542 pub async fn is_valid(&self, token: &TokenValue) -> bool {
544 self.get_token_info(token).await.is_ok()
545 }
546
547 pub async fn get_session(&self, login_id: &str) -> SaTokenResult<SaSession> {
549 let key = self.config.make_key("session:", login_id);
550 let value = self.storage.get(&key).await
551 .map_err(|e| SaTokenError::StorageError(e.to_string()))?;
552
553 if let Some(value) = value {
554 let session: SaSession = serde_json::from_str(&value)
555 .map_err(SaTokenError::SerializationError)?;
556 Ok(session)
557 } else {
558 Ok(SaSession::new(login_id))
559 }
560 }
561
562 pub async fn save_session(&self, session: &SaSession) -> SaTokenResult<()> {
564 let key = self.config.make_key("session:", &session.id);
565 let value = serde_json::to_string(session)
566 .map_err(SaTokenError::SerializationError)?;
567
568 self.storage.set(&key, &value, None).await
569 .map_err(|e| SaTokenError::StorageError(e.to_string()))?;
570
571 Ok(())
572 }
573
574 pub async fn delete_session(&self, login_id: &str) -> SaTokenResult<()> {
576 let key = self.config.make_key("session:", login_id);
577 self.storage.delete(&key).await
578 .map_err(|e| SaTokenError::StorageError(e.to_string()))?;
579 Ok(())
580 }
581
582 pub async fn renew_timeout(
584 &self,
585 token: &TokenValue,
586 timeout_seconds: i64,
587 ) -> SaTokenResult<()> {
588 let token_info = self.get_token_info(token).await?;
589 self.renew_timeout_internal(token, timeout_seconds, &token_info).await
590 }
591
592 async fn renew_timeout_internal(
594 &self,
595 token: &TokenValue,
596 timeout_seconds: i64,
597 token_info: &TokenInfo,
598 ) -> SaTokenResult<()> {
599 let mut new_token_info = token_info.clone();
600
601 use chrono::{Utc, Duration};
603 let new_expire_time = Utc::now() + Duration::seconds(timeout_seconds);
604 new_token_info.expire_time = Some(new_expire_time);
605
606 let key = self.config.make_key("token:", token.as_str());
608 let value = serde_json::to_string(&new_token_info)
609 .map_err(SaTokenError::SerializationError)?;
610
611 let timeout = std::time::Duration::from_secs(timeout_seconds as u64);
612 self.storage.set(&key, &value, Some(timeout)).await
613 .map_err(|e| SaTokenError::StorageError(e.to_string()))?;
614
615 Ok(())
616 }
617
618 pub async fn kick_out(&self, login_id: &str) -> SaTokenResult<()> {
620 if let Some(online_mgr) = &self.online_manager {
621 let _ = online_mgr
622 .kick_out_notify(login_id, "Account kicked out".to_string())
623 .await;
624 }
625
626 let idx_key = self.config.make_key("login:tokens:", login_id);
627 let tokens = self.load_string_list(&idx_key).await.unwrap_or_default();
628 if !tokens.is_empty() {
629 for t in tokens {
630 self.kick_out_by_token(&TokenValue::new(t)).await?;
631 }
632 } else if let Ok(Some(token_str)) = self
633 .storage
634 .get(&self.config.make_key("login:token:", login_id))
635 .await
636 {
637 self.kick_out_by_token(&TokenValue::new(token_str)).await?;
638 }
639
640 self.delete_session(login_id).await?;
641 Ok(())
642 }
643
644 fn token_id_mapping_key(&self, token: &str) -> String {
645 self.config.make_key("token-id:", token)
646 }
647
648 async fn save_token_id_mapping(&self, token: &str, login_id: &str) -> SaTokenResult<()> {
649 self.storage
650 .set(
651 &self.token_id_mapping_key(token),
652 login_id,
653 self.config.timeout_duration(),
654 )
655 .await
656 .map_err(|e| SaTokenError::StorageError(e.to_string()))
657 }
658
659 async fn update_token_id_mapping(&self, token: &str, value: &str) -> SaTokenResult<()> {
660 self.storage
661 .set(&self.token_id_mapping_key(token), value, None)
662 .await
663 .map_err(|e| SaTokenError::StorageError(e.to_string()))
664 }
665
666 async fn delete_token_id_mapping(&self, token: &str) -> SaTokenResult<()> {
667 self.storage
668 .delete(&self.token_id_mapping_key(token))
669 .await
670 .map_err(|e| SaTokenError::StorageError(e.to_string()))
671 }
672
673 async fn get_token_id_mapping(&self, token: &str) -> SaTokenResult<Option<String>> {
674 self.storage
675 .get(&self.token_id_mapping_key(token))
676 .await
677 .map_err(|e| SaTokenError::StorageError(e.to_string()))
678 }
679
680 async fn enforce_max_login_count(&self, login_id: &str) -> SaTokenResult<()> {
681 if self.config.max_login_count <= 0 || !self.config.is_concurrent {
682 return Ok(());
683 }
684 let idx_key = self.config.make_key("login:tokens:", login_id);
685 loop {
686 let list = self.load_string_list(&idx_key).await?;
687 if list.len() as i64 <= self.config.max_login_count {
688 break;
689 }
690 let Some(oldest) = list.first().cloned() else {
691 break;
692 };
693 let mut trimmed = list;
694 trimmed.remove(0);
695 self.save_string_list(&idx_key, &trimmed).await?;
696 let token = TokenValue::new(oldest);
697 match self.config.overflow_logout_mode {
698 LogoutMode::Logout => self.logout(&token).await?,
699 LogoutMode::KickOut => self.kick_out_by_token(&token).await?,
700 LogoutMode::Replaced => self.replaced_by_token(&token).await?,
701 }
702 }
703 Ok(())
704 }
705
706 pub(crate) fn account_ns(&self, login_type: &str, login_id: &str) -> String {
711 if login_type.is_empty() || login_type == "default" || login_type == "login" {
712 login_id.to_string()
713 } else {
714 format!("{}:{}", login_type, login_id)
715 }
716 }
717
718 fn login_token_mapping_key(&self, login_id: &str, login_type: &str) -> String {
720 let ns = self.account_ns(login_type, login_id);
721 self.config.make_key("login:token:", &ns)
722 }
723
724 pub async fn get_terminal_list(
726 &self,
727 login_type: &str,
728 login_id: &str,
729 device_type: Option<&str>,
730 ) -> SaTokenResult<Vec<crate::session::SaTerminalInfo>> {
731 let ns = self.account_ns(login_type, login_id);
732 let session = self.get_session(&ns).await?;
733 Ok(session.get_terminal_list_by_device_type(device_type))
734 }
735
736 pub async fn get_token_value_list_by_login_id(
738 &self,
739 login_type: &str,
740 login_id: &str,
741 device_type: Option<&str>,
742 ) -> SaTokenResult<Vec<String>> {
743 let ns = self.account_ns(login_type, login_id);
744 let session = self.get_session(&ns).await?;
745 Ok(session.get_token_value_list_by_device_type(device_type))
746 }
747
748 pub async fn get_terminal_info_by_token(
750 &self,
751 token: &TokenValue,
752 ) -> SaTokenResult<Option<crate::session::SaTerminalInfo>> {
753 let info = match self.get_token_info(token).await {
754 Ok(i) => i,
755 Err(_) => return Ok(None),
756 };
757 let ns = self.account_ns(&info.login_type, &info.login_id);
758 let session = self.get_session(&ns).await?;
759 Ok(session.get_terminal(token.as_str()).cloned())
760 }
761
762 async fn append_token_index(&self, login_id: &str, token: &str) -> SaTokenResult<()> {
764 let key = self.config.make_key("login:tokens:", login_id);
765 let mut list = self.load_string_list(&key).await?;
766 if !list.iter().any(|t| t == token) {
767 list.push(token.to_string());
768 self.save_string_list(&key, &list).await?;
769 }
770 Ok(())
771 }
772
773 async fn remove_token_index(&self, login_id: &str, token: &str) -> SaTokenResult<()> {
775 let key = self.config.make_key("login:tokens:", login_id);
776 let mut list = self.load_string_list(&key).await?;
777 let before = list.len();
778 list.retain(|t| t != token);
779 if list.len() != before {
780 self.save_string_list(&key, &list).await?;
781 }
782 Ok(())
783 }
784}
785
786impl SaTokenManager {
789 fn permission_key(&self, login_id: &str) -> String {
791 self.config.make_key("permission:", login_id)
792 }
793
794 fn role_key(&self, login_id: &str) -> String {
796 self.config.make_key("role:", login_id)
797 }
798
799 fn permission_key_ns(&self, login_type: &str, login_id: &str) -> String {
800 let ns = self.account_ns(login_type, login_id);
801 self.config.make_key("permission:", &ns)
802 }
803
804 fn role_key_ns(&self, login_type: &str, login_id: &str) -> String {
805 let ns = self.account_ns(login_type, login_id);
806 self.config.make_key("role:", &ns)
807 }
808
809 pub async fn get_permissions_with_type(
810 &self,
811 login_type: &str,
812 login_id: &str,
813 ) -> SaTokenResult<Vec<String>> {
814 if let Some(iface) = &self.stp_interface {
815 return iface.get_permission_list(login_id, login_type).await;
816 }
817 self.load_string_list(&self.permission_key_ns(login_type, login_id))
818 .await
819 }
820
821 pub async fn set_permissions_with_type(
822 &self,
823 login_type: &str,
824 login_id: &str,
825 permissions: Vec<String>,
826 ) -> SaTokenResult<()> {
827 self.save_string_list(
828 &self.permission_key_ns(login_type, login_id),
829 &permissions,
830 )
831 .await
832 }
833
834 pub async fn get_roles_with_type(
835 &self,
836 login_type: &str,
837 login_id: &str,
838 ) -> SaTokenResult<Vec<String>> {
839 if let Some(iface) = &self.stp_interface {
840 return iface.get_role_list(login_id, login_type).await;
841 }
842 self.load_string_list(&self.role_key_ns(login_type, login_id))
843 .await
844 }
845
846 pub async fn set_roles_with_type(
847 &self,
848 login_type: &str,
849 login_id: &str,
850 roles: Vec<String>,
851 ) -> SaTokenResult<()> {
852 self.save_string_list(&self.role_key_ns(login_type, login_id), &roles)
853 .await
854 }
855
856 async fn save_string_list(&self, key: &str, list: &[String]) -> SaTokenResult<()> {
859 let value = serde_json::to_string(list).map_err(SaTokenError::SerializationError)?;
860 self.storage
861 .set(key, &value, None)
862 .await
863 .map_err(|e| SaTokenError::StorageError(e.to_string()))
864 }
865
866 async fn load_string_list(&self, key: &str) -> SaTokenResult<Vec<String>> {
869 match self
870 .storage
871 .get(key)
872 .await
873 .map_err(|e| SaTokenError::StorageError(e.to_string()))?
874 {
875 Some(value) => serde_json::from_str(&value).map_err(SaTokenError::SerializationError),
876 None => Ok(Vec::new()),
877 }
878 }
879
880 pub async fn set_permissions(&self, login_id: &str, permissions: Vec<String>) -> SaTokenResult<()> {
883 self.save_string_list(&self.permission_key(login_id), &permissions).await
884 }
885
886 pub async fn get_permissions(&self, login_id: &str) -> SaTokenResult<Vec<String>> {
889 if let Some(iface) = &self.stp_interface {
890 return iface.get_permission_list(login_id, "default").await;
891 }
892 self.load_string_list(&self.permission_key(login_id)).await
893 }
894
895 pub async fn add_permission(&self, login_id: &str, permission: String) -> SaTokenResult<()> {
898 let key = self.permission_key(login_id);
899 let mut list = self.load_string_list(&key).await?;
900 if !list.contains(&permission) {
901 list.push(permission);
902 self.save_string_list(&key, &list).await?;
903 }
904 Ok(())
905 }
906
907 pub async fn remove_permission(&self, login_id: &str, permission: &str) -> SaTokenResult<()> {
910 let key = self.permission_key(login_id);
911 let mut list = self.load_string_list(&key).await?;
912 let before = list.len();
913 list.retain(|p| p != permission);
914 if list.len() != before {
915 self.save_string_list(&key, &list).await?;
916 }
917 Ok(())
918 }
919
920 pub async fn clear_permissions(&self, login_id: &str) -> SaTokenResult<()> {
923 self.storage
924 .delete(&self.permission_key(login_id))
925 .await
926 .map_err(|e| SaTokenError::StorageError(e.to_string()))
927 }
928
929 pub async fn set_roles(&self, login_id: &str, roles: Vec<String>) -> SaTokenResult<()> {
932 self.save_string_list(&self.role_key(login_id), &roles).await
933 }
934
935 pub async fn get_roles(&self, login_id: &str) -> SaTokenResult<Vec<String>> {
938 if let Some(iface) = &self.stp_interface {
939 return iface.get_role_list(login_id, "default").await;
940 }
941 self.load_string_list(&self.role_key(login_id)).await
942 }
943
944 pub async fn add_role(&self, login_id: &str, role: String) -> SaTokenResult<()> {
947 let key = self.role_key(login_id);
948 let mut list = self.load_string_list(&key).await?;
949 if !list.contains(&role) {
950 list.push(role);
951 self.save_string_list(&key, &list).await?;
952 }
953 Ok(())
954 }
955
956 pub async fn remove_role(&self, login_id: &str, role: &str) -> SaTokenResult<()> {
959 let key = self.role_key(login_id);
960 let mut list = self.load_string_list(&key).await?;
961 let before = list.len();
962 list.retain(|r| r != role);
963 if list.len() != before {
964 self.save_string_list(&key, &list).await?;
965 }
966 Ok(())
967 }
968
969 pub async fn clear_roles(&self, login_id: &str) -> SaTokenResult<()> {
972 self.storage
973 .delete(&self.role_key(login_id))
974 .await
975 .map_err(|e| SaTokenError::StorageError(e.to_string()))
976 }
977}
978
979#[cfg(test)]
980mod tests {
981 use super::*;
982 use sa_token_storage_memory::MemoryStorage;
983 use crate::config::{LogoutMode, TokenStyle};
984
985 fn make_manager(is_concurrent: bool, auto_renew: bool, active_timeout: i64) -> SaTokenManager {
986 let config = SaTokenConfig {
987 timeout: 3600,
988 token_style: TokenStyle::Uuid,
989 is_concurrent,
990 auto_renew,
991 active_timeout,
992 ..Default::default()
993 };
994 SaTokenManager::new(Arc::new(MemoryStorage::new()), config)
995 }
996
997 #[tokio::test]
998 async fn test_non_concurrent_login_invalidates_previous_token() {
999 let mgr = make_manager(false, false, -1);
1000 let t1 = mgr.login("user_1").await.unwrap();
1001 assert!(mgr.is_valid(&t1).await);
1002 let t2 = mgr.login("user_1").await.unwrap();
1003 assert!(!mgr.is_valid(&t1).await);
1004 assert!(mgr.is_valid(&t2).await);
1005 }
1006
1007 #[tokio::test]
1008 async fn test_logout_clears_login_token_mapping() {
1009 let mgr = make_manager(true, false, -1);
1010 let token = mgr.login("user_1").await.unwrap();
1011 let map_key = mgr.config.make_key("login:token:", "user_1");
1012 assert!(mgr.storage.get(&map_key).await.unwrap().is_some());
1013 mgr.logout(&token).await.unwrap();
1014 assert!(mgr.storage.get(&map_key).await.unwrap().is_none());
1015 }
1016
1017 #[tokio::test]
1018 async fn test_concurrent_login_appends_token_index() {
1019 let mgr = make_manager(true, false, -1);
1020 let t1 = mgr.login("user_1").await.unwrap();
1021 let t2 = mgr.login("user_1").await.unwrap();
1022 let idx_key = mgr.config.make_key("login:tokens:", "user_1");
1023 let list: Vec<String> = serde_json::from_str(
1024 &mgr.storage.get(&idx_key).await.unwrap().unwrap(),
1025 )
1026 .unwrap();
1027 assert_eq!(list.len(), 2);
1028 assert!(list.contains(&t1.as_str().to_string()));
1029 assert!(list.contains(&t2.as_str().to_string()));
1030 }
1031
1032 #[tokio::test]
1033 async fn test_active_timeout_freeze_returns_inactive() {
1034 let mgr = make_manager(true, false, 1);
1035 let token = mgr.login("user_1").await.unwrap();
1036 let key = mgr.config.make_key("token:", token.as_str());
1037 let mut info = mgr.get_token_info(&token).await.unwrap();
1038 info.last_active_time = Utc::now() - Duration::seconds(10);
1039 mgr.storage
1040 .set(
1041 &key,
1042 &serde_json::to_string(&info).unwrap(),
1043 mgr.config.timeout_duration(),
1044 )
1045 .await
1046 .unwrap();
1047 let result = mgr.get_token_info(&token).await;
1048 assert!(matches!(result, Err(SaTokenError::TokenInactive)));
1049 }
1050
1051 #[tokio::test]
1052 async fn test_auto_renew_updates_last_active_time() {
1053 let mgr = make_manager(true, true, 3600);
1054 let token = mgr.login("user_1").await.unwrap();
1055 let before = mgr.get_token_info(&token).await.unwrap().last_active_time;
1056 tokio::time::sleep(std::time::Duration::from_millis(20)).await;
1057 let after = mgr.get_token_info(&token).await.unwrap().last_active_time;
1058 assert!(after >= before);
1059 }
1060
1061 #[tokio::test]
1062 async fn test_login_with_nonce_when_enabled() {
1063 let config = SaTokenConfig {
1064 enable_nonce: true,
1065 nonce_timeout: 60,
1066 auto_renew: false,
1067 ..Default::default()
1068 };
1069 let mgr = SaTokenManager::new(Arc::new(MemoryStorage::new()), config);
1070 let nonce_mgr = crate::nonce::NonceManager::new(mgr.storage.clone(), 60);
1071 let nonce = nonce_mgr.generate();
1072 let token = mgr
1073 .login_with_options("user_1", None, None, None, Some(nonce.clone()), None)
1074 .await
1075 .unwrap();
1076 assert!(mgr.is_valid(&token).await);
1077 let result = mgr
1078 .login_with_options("user_1", None, None, None, Some(nonce), None)
1079 .await;
1080 assert!(matches!(result, Err(SaTokenError::NonceAlreadyUsed)));
1081 }
1082
1083 #[tokio::test]
1084 async fn test_kickout_token_returns_kicked_out() {
1085 let mgr = make_manager(true, false, -1);
1086 let token = mgr.login("user_kick").await.unwrap();
1087 mgr.kick_out_by_token(&token).await.unwrap();
1088 let err = mgr.get_token_info(&token).await.unwrap_err();
1089 assert!(matches!(err, SaTokenError::AccountKickedOut));
1090 }
1091
1092 #[tokio::test]
1093 async fn test_replaced_token_returns_replaced() {
1094 let mgr = make_manager(false, false, -1);
1095 let t1 = mgr.login("user_rep").await.unwrap();
1096 let _t2 = mgr.login("user_rep").await.unwrap();
1097 let err = mgr.get_token_info(&t1).await.unwrap_err();
1098 assert!(matches!(err, SaTokenError::AccountReplaced));
1099 }
1100
1101 #[tokio::test]
1102 async fn test_is_share_reuses_token() {
1103 let config = SaTokenConfig {
1104 is_share: true,
1105 is_concurrent: true,
1106 ..Default::default()
1107 };
1108 let mgr = SaTokenManager::new(Arc::new(MemoryStorage::new()), config);
1109 let t1 = mgr.login("user_share").await.unwrap();
1110 let t2 = mgr.login("user_share").await.unwrap();
1111 assert_eq!(t1.as_str(), t2.as_str());
1112 }
1113
1114 #[tokio::test]
1115 async fn test_max_login_count_overflow_kickout() {
1116 let config = SaTokenConfig {
1117 is_concurrent: true,
1118 max_login_count: 2,
1119 overflow_logout_mode: LogoutMode::KickOut,
1120 ..Default::default()
1121 };
1122 let mgr = SaTokenManager::new(Arc::new(MemoryStorage::new()), config);
1123 let t1 = mgr.login("user_max").await.unwrap();
1124 let _t2 = mgr.login("user_max").await.unwrap();
1125 let t3 = mgr.login("user_max").await.unwrap();
1126 assert!(matches!(
1127 mgr.get_token_info(&t1).await,
1128 Err(SaTokenError::AccountKickedOut)
1129 ));
1130 assert!(mgr.is_valid(&t3).await);
1131 }
1132
1133 #[test]
1134 fn test_account_ns_default_unchanged() {
1135 let mgr = make_manager(true, false, -1);
1136 assert_eq!(mgr.account_ns("default", "u1"), "u1");
1137 assert_eq!(mgr.account_ns("login", "u1"), "u1");
1138 assert_eq!(mgr.account_ns("", "u1"), "u1");
1139 assert_eq!(mgr.account_ns("admin", "u1"), "admin:u1");
1140 }
1141
1142 #[tokio::test]
1143 async fn test_login_writes_terminal_and_logout_removes() {
1144 let mgr = make_manager(true, false, -1);
1145 let token = mgr
1146 .login_with_options("u1", None, Some("PC".to_string()), None, None, None)
1147 .await
1148 .unwrap();
1149 let terminals = mgr.get_terminal_list("default", "u1", None).await.unwrap();
1150 assert_eq!(terminals.len(), 1);
1151 assert_eq!(terminals[0].token_value, token.as_str());
1152 assert_eq!(terminals[0].device_type, "PC");
1153 assert_eq!(terminals[0].index, 1);
1154
1155 mgr.logout(&token).await.unwrap();
1156 let terminals = mgr.get_terminal_list("default", "u1", None).await.unwrap();
1157 assert!(terminals.is_empty());
1158 }
1159
1160 #[tokio::test]
1161 async fn test_terminal_filter_by_device_type() {
1162 let mgr = make_manager(true, false, -1);
1163 mgr.login_with_options("u1", None, Some("PC".to_string()), None, None, None)
1164 .await
1165 .unwrap();
1166 mgr.login_with_options("u1", None, Some("APP".to_string()), None, None, None)
1167 .await
1168 .unwrap();
1169 assert_eq!(
1170 mgr.get_terminal_list("default", "u1", Some("PC"))
1171 .await
1172 .unwrap()
1173 .len(),
1174 1
1175 );
1176 assert_eq!(
1177 mgr.get_token_value_list_by_login_id("default", "u1", None)
1178 .await
1179 .unwrap()
1180 .len(),
1181 2
1182 );
1183 }
1184
1185 #[tokio::test]
1186 async fn test_permissions_isolated_by_login_type() {
1187 let mgr = make_manager(true, false, -1);
1188 mgr.set_permissions_with_type("admin", "u1", vec!["a:read".to_string()])
1189 .await
1190 .unwrap();
1191 mgr.set_permissions_with_type("user", "u1", vec!["u:read".to_string()])
1192 .await
1193 .unwrap();
1194 let admin_perms = mgr.get_permissions_with_type("admin", "u1").await.unwrap();
1195 let user_perms = mgr.get_permissions_with_type("user", "u1").await.unwrap();
1196 assert_eq!(admin_perms, vec!["a:read".to_string()]);
1197 assert_eq!(user_perms, vec!["u:read".to_string()]);
1198 }
1199}