use std::time::Duration;
use crate::error::{SaTokenError, SaTokenResult};
use crate::util::StpUtil;
pub const DEFAULT_HEADER: &str = "SA-SAME-TOKEN";
fn ttl(timeout_secs: i64) -> Option<Duration> {
if timeout_secs > 0 {
Some(Duration::from_secs(timeout_secs as u64))
} else {
None
}
}
fn generate_token() -> SaTokenResult<String> {
crate::token::random_hex(32)
}
pub async fn get_token_nh() -> SaTokenResult<Option<String>> {
let manager = StpUtil::try_get_manager()?;
let key = manager.keys().same_token();
manager.dao().get_string(&key).await
}
pub async fn get_past_token_nh() -> SaTokenResult<Option<String>> {
let manager = StpUtil::try_get_manager()?;
let key = manager.keys().same_token_past();
manager.dao().get_string(&key).await
}
pub async fn get_token() -> SaTokenResult<String> {
if let Some(existing) = get_token_nh().await?
&& !existing.is_empty()
{
return Ok(existing);
}
refresh_token().await
}
pub async fn is_valid(token: &str) -> SaTokenResult<bool> {
if token.is_empty() {
return Ok(false);
}
let current = get_token_nh().await?;
if current
.as_deref()
.is_some_and(|c| crate::http_basic::ct_eq(c.as_bytes(), token.as_bytes()))
{
return Ok(true);
}
let past = get_past_token_nh().await?;
Ok(past
.as_deref()
.is_some_and(|p| crate::http_basic::ct_eq(p.as_bytes(), token.as_bytes())))
}
pub async fn refresh_token() -> SaTokenResult<String> {
let manager = StpUtil::try_get_manager()?;
let timeout = manager.config.same_token_timeout;
let ttl_opt = ttl(timeout);
let dao = manager.dao();
let cur_key = manager.keys().same_token();
let past_key = manager.keys().same_token_past();
let current = dao.get_string(&cur_key).await?;
if let Some(ref cur) = current {
if !cur.is_empty() {
dao.set_string(&past_key, cur, ttl_opt).await?;
}
}
let next = generate_token()?;
let expected = current.as_deref().filter(|s| !s.is_empty());
let won = dao.cas(&cur_key, expected, &next, ttl_opt).await?;
if won {
return Ok(next);
}
dao.get_string(&cur_key)
.await?
.filter(|s| !s.is_empty())
.ok_or(SaTokenError::SameTokenInvalid)
}
pub async fn check_token(token: &str) -> SaTokenResult<()> {
if is_valid(token).await? {
Ok(())
} else {
Err(SaTokenError::SameTokenInvalid)
}
}
pub async fn check_current_request() -> SaTokenResult<()> {
let value = crate::context::SaTokenContext::try_current()
.and_then(|ctx| ctx.auth_meta().same_token)
.unwrap_or_default();
check_token(&value).await
}