use std::sync::Arc;
use crate::disable;
use crate::error::SaTokenResult;
use crate::keys::SaKeys;
use crate::manager::SaTokenManager;
use crate::session::{SaSession, SaTerminalInfo};
use crate::token::TokenValue;
#[derive(Clone)]
pub struct SaLogic {
login_type: Arc<str>,
manager: SaTokenManager,
}
impl std::fmt::Debug for SaLogic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("SaLogic { .. }")
}
}
impl SaLogic {
pub fn new(login_type: impl AsRef<str>, manager: SaTokenManager) -> Self {
Self {
login_type: Arc::from(login_type.as_ref()),
manager,
}
}
pub fn login_type(&self) -> &str {
&self.login_type
}
pub fn manager(&self) -> &SaTokenManager {
&self.manager
}
pub fn keys(&self) -> &SaKeys {
self.manager.keys()
}
pub async fn login(&self, login_id: impl Into<String>) -> SaTokenResult<TokenValue> {
self.manager
.login_with_options(
login_id,
Some(self.login_type.to_string()),
None,
None,
None,
None,
)
.await
}
pub async fn login_with_device(
&self,
login_id: impl Into<String>,
device: Option<String>,
extra: Option<serde_json::Value>,
) -> SaTokenResult<TokenValue> {
self.manager
.login_with_options(
login_id,
Some(self.login_type.to_string()),
device,
extra,
None,
None,
)
.await
}
pub async fn logout(&self, token: &TokenValue) -> SaTokenResult<()> {
self.manager.logout(token).await
}
pub async fn logout_by_login_id(&self, login_id: &str) -> SaTokenResult<()> {
self.manager
.logout_by_login_id(&self.login_type, login_id)
.await
}
pub async fn kick_out(&self, login_id: &str) -> SaTokenResult<()> {
self.manager.kick_out(&self.login_type, login_id).await
}
pub async fn get_login_id(&self, token: &TokenValue) -> SaTokenResult<String> {
Ok(self
.manager
.get_token_info(token)
.await?
.login_id
.to_string())
}
pub async fn is_valid(&self, token: &TokenValue) -> bool {
self.manager.is_valid(token).await
}
pub async fn get_session(&self, login_id: &str) -> SaTokenResult<SaSession> {
self.manager
.get_session_with_type(&self.login_type, login_id)
.await
}
pub async fn save_session(&self, login_id: &str, session: &SaSession) -> SaTokenResult<()> {
self.manager
.save_session_with_type(&self.login_type, login_id, session)
.await
}
pub async fn delete_session(&self, login_id: &str) -> SaTokenResult<()> {
self.manager
.delete_session_with_type(&self.login_type, login_id)
.await
}
pub async fn get_terminal_list(
&self,
login_id: &str,
device_type: Option<&str>,
) -> SaTokenResult<Vec<SaTerminalInfo>> {
self.manager
.get_terminal_list(&self.login_type, login_id, device_type)
.await
}
pub async fn get_terminal_info_by_token(
&self,
token: &TokenValue,
) -> SaTokenResult<Option<SaTerminalInfo>> {
self.manager.get_terminal_info_by_token(token).await
}
pub async fn get_permissions(&self, login_id: &str) -> SaTokenResult<Vec<String>> {
self.manager
.get_permissions_with_type(&self.login_type, login_id)
.await
}
pub async fn set_permissions(&self, login_id: &str, perms: Vec<String>) -> SaTokenResult<()> {
self.manager
.set_permissions_with_type(&self.login_type, login_id, perms)
.await
}
pub async fn add_permission(
&self,
login_id: &str,
permission: impl Into<String>,
) -> SaTokenResult<()> {
self.manager
.add_permission_with_type(&self.login_type, login_id, permission.into())
.await
}
pub async fn remove_permission(&self, login_id: &str, permission: &str) -> SaTokenResult<()> {
self.manager
.remove_permission_with_type(&self.login_type, login_id, permission)
.await
}
pub async fn clear_permissions(&self, login_id: &str) -> SaTokenResult<()> {
self.manager
.clear_permissions_with_type(&self.login_type, login_id)
.await
}
pub async fn has_permission(&self, login_id: &str, permission: &str) -> SaTokenResult<bool> {
self.manager
.authz_service()
.has_permission(&self.login_type, login_id, permission)
.await
}
pub async fn check_permission(&self, login_id: &str, permission: &str) -> SaTokenResult<()> {
self.manager
.authz_service()
.check_permission(&self.login_type, login_id, permission)
.await
}
pub async fn has_all_permissions(
&self,
login_id: &str,
permissions: &[&str],
) -> SaTokenResult<bool> {
self.manager
.authz_service()
.has_all_permissions(&self.login_type, login_id, permissions)
.await
}
pub async fn has_any_permission(
&self,
login_id: &str,
permissions: &[&str],
) -> SaTokenResult<bool> {
self.manager
.authz_service()
.has_any_permission(&self.login_type, login_id, permissions)
.await
}
pub async fn get_roles(&self, login_id: &str) -> SaTokenResult<Vec<String>> {
self.manager
.get_roles_with_type(&self.login_type, login_id)
.await
}
pub async fn set_roles(&self, login_id: &str, roles: Vec<String>) -> SaTokenResult<()> {
self.manager
.set_roles_with_type(&self.login_type, login_id, roles)
.await
}
pub async fn add_role(&self, login_id: &str, role: impl Into<String>) -> SaTokenResult<()> {
self.manager
.add_role_with_type(&self.login_type, login_id, role.into())
.await
}
pub async fn remove_role(&self, login_id: &str, role: &str) -> SaTokenResult<()> {
self.manager
.remove_role_with_type(&self.login_type, login_id, role)
.await
}
pub async fn clear_roles(&self, login_id: &str) -> SaTokenResult<()> {
self.manager
.clear_roles_with_type(&self.login_type, login_id)
.await
}
pub async fn has_role(&self, login_id: &str, role: &str) -> SaTokenResult<bool> {
self.manager
.authz_service()
.has_role(&self.login_type, login_id, role)
.await
}
pub async fn check_role(&self, login_id: &str, role: &str) -> SaTokenResult<()> {
self.manager
.authz_service()
.check_role(&self.login_type, login_id, role)
.await
}
pub async fn has_all_roles(&self, login_id: &str, roles: &[&str]) -> SaTokenResult<bool> {
self.manager
.authz_service()
.has_all_roles(&self.login_type, login_id, roles)
.await
}
pub async fn has_any_role(&self, login_id: &str, roles: &[&str]) -> SaTokenResult<bool> {
self.manager
.authz_service()
.has_any_role(&self.login_type, login_id, roles)
.await
}
pub async fn disable(&self, login_id: &str, time: i64) -> SaTokenResult<()> {
self.manager
.disable_with_type(&self.login_type, login_id, time)
.await
}
pub async fn disable_level(
&self,
login_id: &str,
service: &str,
level: i32,
time: i64,
) -> SaTokenResult<()> {
self.manager
.disable_level_with_type(&self.login_type, login_id, service, level, time)
.await
}
pub async fn check_disable(&self, login_id: &str) -> SaTokenResult<()> {
self.manager
.check_disable_level_with_type(
&self.login_type,
login_id,
disable::DEFAULT_DISABLE_SERVICE,
disable::MIN_DISABLE_LEVEL,
)
.await
}
pub async fn get_disable_level(&self, login_id: &str, service: &str) -> SaTokenResult<i32> {
self.manager
.get_disable_level_with_type(&self.login_type, login_id, service)
.await
}
pub async fn untie_disable(&self, login_id: &str, service: &str) -> SaTokenResult<()> {
self.manager
.untie_disable_with_type(&self.login_type, login_id, service)
.await
}
pub async fn open_safe(
&self,
token: &TokenValue,
service: &str,
safe_time: i64,
) -> SaTokenResult<()> {
self.manager.open_safe(token, service, safe_time).await
}
pub async fn check_safe(&self, token: &TokenValue, service: &str) -> SaTokenResult<()> {
self.manager.check_safe(token, service).await
}
pub async fn is_safe(&self, token: &TokenValue, service: &str) -> SaTokenResult<bool> {
self.manager.is_safe(token, service).await
}
pub async fn close_safe(&self, token: &TokenValue, service: &str) -> SaTokenResult<()> {
self.manager.close_safe(token, service).await
}
pub async fn get_token_session(&self, token: &TokenValue) -> SaTokenResult<SaSession> {
self.manager.get_token_session(token).await
}
pub async fn get_anon_token_session(&self, token: &TokenValue) -> SaTokenResult<SaSession> {
self.manager.get_anon_token_session(token).await
}
pub async fn save_token_session(
&self,
token: &TokenValue,
session: &SaSession,
) -> SaTokenResult<()> {
self.manager.save_token_session(token, session).await
}
pub async fn delete_token_session(&self, token: &TokenValue) -> SaTokenResult<()> {
self.manager.delete_token_session(token).await
}
pub fn switch_to(&self, login_id: impl Into<String>) {
let target = login_id.into();
crate::context::SaTokenContext::with_current_mut(|inner| {
inner.switch_login_id = Some(target);
});
}
pub fn end_switch(&self) {
crate::context::SaTokenContext::with_current_mut(|inner| {
inner.switch_login_id = None;
});
}
pub fn is_switch(&self) -> bool {
crate::context::SaTokenContext::get_current()
.and_then(|c| c.switch_login_id())
.is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
use sa_token_storage_memory::MemoryStorage;
fn make_manager() -> SaTokenManager {
SaTokenManager::new(
Arc::new(MemoryStorage::new()),
crate::SaTokenConfig::default(),
)
}
#[tokio::test]
async fn test_sa_logic_permission_isolation() {
let mgr = make_manager();
let admin = SaLogic::new("admin", mgr.clone());
let user = SaLogic::new("user", mgr);
admin
.set_permissions("10001", vec!["admin:read".to_string()])
.await
.unwrap();
user.set_permissions("10001", vec!["user:read".to_string()])
.await
.unwrap();
assert_eq!(
admin.get_permissions("10001").await.unwrap(),
vec!["admin:read".to_string()]
);
assert_eq!(
user.get_permissions("10001").await.unwrap(),
vec!["user:read".to_string()]
);
}
#[tokio::test]
async fn test_sa_logic_clone_is_independent_facade() {
let mgr = make_manager();
let a = SaLogic::new("shared", mgr.clone());
let b = SaLogic::new("shared", mgr);
assert_eq!(a.login_type(), b.login_type());
assert_eq!(a.login_type(), "shared");
}
}