use std::sync::Arc;
use std::time::Duration;
use crate::dao::SaTokenDao;
use crate::error::{SaTokenError, SaTokenResult};
use crate::sso::SsoTicket;
pub struct SsoTicketStore {
dao: Arc<SaTokenDao>,
ticket_ttl_secs: i64,
}
impl std::fmt::Debug for SsoTicketStore {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("SsoTicketStore { .. }")
}
}
impl SsoTicketStore {
pub fn new(dao: Arc<SaTokenDao>, ticket_ttl_secs: i64) -> Self {
Self {
dao,
ticket_ttl_secs,
}
}
fn ttl(&self) -> Option<Duration> {
if self.ticket_ttl_secs > 0 {
Some(Duration::from_secs(self.ticket_ttl_secs as u64))
} else {
None
}
}
pub async fn save(&self, ticket: &SsoTicket) -> SaTokenResult<()> {
let key = self.dao.keys().sso_ticket(&ticket.ticket_id);
self.dao.set_object(&key, ticket, self.ttl()).await
}
pub async fn get(&self, ticket_id: &str) -> SaTokenResult<Option<SsoTicket>> {
let key = self.dao.keys().sso_ticket(ticket_id);
self.dao.get_object(&key).await
}
pub async fn consume(&self, ticket_id: &str, service: &str) -> SaTokenResult<String> {
let key = self.dao.keys().sso_ticket(ticket_id);
let raw = self
.dao
.take_string(&key)
.await?
.ok_or(SaTokenError::InvalidTicket)?;
let ticket: SsoTicket = self.dao.decode(&raw)?;
if !ticket.is_valid() {
return Err(SaTokenError::TicketExpired);
}
if ticket.service != service {
return Err(SaTokenError::ServiceMismatch);
}
Ok(ticket.login_id)
}
pub async fn check(&self, ticket_id: &str, service: &str) -> SaTokenResult<(String, i64)> {
let ticket = self
.get(ticket_id)
.await?
.ok_or(SaTokenError::InvalidTicket)?;
if !ticket.is_valid() {
return Err(SaTokenError::TicketExpired);
}
if ticket.service != service {
return Err(SaTokenError::ServiceMismatch);
}
let remain = ticket.expire_time.signed_duration_since(chrono::Utc::now());
Ok((ticket.login_id, remain.num_seconds().max(0)))
}
}