use std::convert::Infallible;
use actix_web::{
dev::Payload,
FromRequest,
HttpMessage,
HttpRequest,
};
use crate::{
app::App,
errors::Error,
HasUserID,
UserState,
};
pub struct Auth<A: App> {
pub user: A::User,
pub user_state: UserState,
pub session_id: A::SessionID,
pub(crate) _deny_public_constructor: (),
}
pub enum MaybeAuth<A: App> {
Authenticated(Auth<A>),
Unauthenticated,
}
impl<A: App> Auth<A> {
pub fn require_from_request(request: &impl HttpMessage) -> Result<Auth<A>, A::Error> {
MaybeAuth::<A>::from_request(request)
.require()
}
pub fn into_maybe_auth(self) -> MaybeAuth<A> {
MaybeAuth::Authenticated(self)
}
}
impl<A: App> MaybeAuth<A> {
pub fn from_request(request: &impl HttpMessage) -> MaybeAuth<A> {
let exts = request.extensions();
let auth = exts.get::<Auth<A>>();
match auth {
Some(auth) => MaybeAuth::Authenticated(auth.clone()),
None => MaybeAuth::Unauthenticated,
}
}
pub fn require(self) -> Result<Auth<A>, A::Error> {
match self {
Self::Authenticated(auth) => {
auth.user_state.require_ready(auth.user.id())?;
Ok(auth)
}
Self::Unauthenticated => {
log::info!("Not authenticated");
Error::NotAuthenticated.as_app_err()
}
}
}
pub fn require_even_if_not_ready(self) -> Result<Auth<A>, A::Error> {
match self {
Self::Authenticated(auth) => Ok(auth),
Self::Unauthenticated => {
log::info!("Not authenticated");
Error::NotAuthenticated.as_app_err()
},
}
}
pub fn user(&self) -> Option<&A::User> {
match self {
Self::Authenticated(auth) if auth.user_state.is_ready() => Some(&auth.user),
_ => None,
}
}
pub fn into_user(self) -> Option<A::User> {
match self {
Self::Authenticated(auth) if auth.user_state.is_ready() => Some(auth.user),
_ => None,
}
}
pub fn user_even_if_not_ready(&self) -> Option<&A::User> {
match self {
Self::Authenticated(auth) => Some(&auth.user),
Self::Unauthenticated => None,
}
}
pub fn into_user_even_if_not_ready(self) -> Option<A::User> {
match self {
Self::Authenticated(auth) => Some(auth.user),
Self::Unauthenticated => None,
}
}
pub(crate) fn insert_into_request(self, request: &impl HttpMessage) {
if let Self::Authenticated(auth) = self {
request.extensions_mut().insert(auth);
}
}
}
pub fn require_user_from_request<A: App>(request: &impl HttpMessage) -> Result<A::User, A::Error> {
Auth::<A>::require_from_request(request)
.map(|auth| auth.user)
}
impl <A: App> Clone for Auth<A> {
fn clone(&self) -> Self {
Self {
user: self.user.clone(),
user_state: self.user_state,
session_id: self.session_id,
_deny_public_constructor: (),
}
}
}
impl <A: App> Clone for MaybeAuth<A> {
fn clone(&self) -> Self {
match self {
Self::Authenticated(auth) => Self::Authenticated(auth.clone()),
Self::Unauthenticated => Self::Unauthenticated,
}
}
}
impl<A: App> From<MaybeAuth<A>> for Option<Auth<A>> {
fn from(value: MaybeAuth<A>) -> Self {
match value {
MaybeAuth::Authenticated(auth) => Some(auth),
_ => None,
}
}
}
impl<A: App> FromRequest for MaybeAuth<A> {
type Error = Infallible;
type Future = std::future::Ready<Result<Self, Infallible>>;
fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future {
let auth = Self::from_request(request);
std::future::ready(Ok(auth))
}
}
impl<A: App> FromRequest for Auth<A> {
type Error = A::Error;
type Future = std::future::Ready<Result<Self, A::Error>>;
fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future {
let result = Auth::<A>::require_from_request(request);
std::future::ready(result)
}
}