use std::sync::Arc;
use crate::container::App;
use crate::session::{
auth_user_id, clear_auth_user, generate_csrf_token, regenerate_session_id, session,
session_mut, set_auth_user, DatabaseSessionDriver, SessionStore,
};
use super::authenticatable::Authenticatable;
use super::provider::UserProvider;
pub struct Auth;
impl Auth {
pub fn id() -> Option<i64> {
auth_user_id()
}
pub fn id_as<T>() -> Option<T>
where
T: TryFrom<i64>,
{
Self::id().and_then(|id| T::try_from(id).ok())
}
pub fn check() -> bool {
Self::id().is_some()
}
pub fn guest() -> bool {
!Self::check()
}
pub fn login(user_id: i64) {
regenerate_session_id();
set_auth_user(user_id);
session_mut(|session| {
session.csrf_token = generate_csrf_token();
});
}
pub fn login_remember(user_id: i64, _remember_token: &str) {
Self::login(user_id);
}
pub fn logout() {
clear_auth_user();
session_mut(|session| {
session.csrf_token = generate_csrf_token();
});
}
pub fn logout_and_invalidate() {
session_mut(|session| {
session.flush();
session.csrf_token = generate_csrf_token();
});
}
pub async fn logout_other_devices() -> Option<Result<u64, crate::error::FrameworkError>> {
let user_id = Self::id()?;
let current_session_id = session().map(|s| s.id);
let store = DatabaseSessionDriver::new(
std::time::Duration::from_secs(0),
std::time::Duration::from_secs(0),
);
Some(
store
.destroy_for_user(user_id, current_session_id.as_deref())
.await,
)
}
pub async fn attempt<F, Fut>(validator: F) -> Result<Option<i64>, crate::error::FrameworkError>
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = Result<Option<i64>, crate::error::FrameworkError>>,
{
let result = validator().await?;
if let Some(user_id) = result {
Self::login(user_id);
}
Ok(result)
}
pub async fn validate<F, Fut>(validator: F) -> Result<bool, crate::error::FrameworkError>
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = Result<bool, crate::error::FrameworkError>>,
{
validator().await
}
pub async fn user() -> Result<Option<Arc<dyn Authenticatable>>, crate::error::FrameworkError> {
let user_id = match Self::id() {
Some(id) => id,
None => return Ok(None),
};
let provider = App::make::<dyn UserProvider>().ok_or_else(|| {
crate::error::FrameworkError::internal(
"No UserProvider registered. Register one in bootstrap.rs with: \
bind!(dyn UserProvider, YourUserProvider)"
.to_string(),
)
})?;
provider.retrieve_by_id(user_id).await
}
pub async fn user_as<T: Authenticatable + Clone>(
) -> Result<Option<T>, crate::error::FrameworkError> {
let user = Self::user().await?;
Ok(user.and_then(|u| u.as_any().downcast_ref::<T>().cloned()))
}
}