use std::convert::Infallible;
use actix_web::{
dev::Payload,
FromRequest,
HttpMessage,
HttpRequest,
};
use crate::{
app::App,
errors::Error,
UserID,
UserState,
};
#[derive(Clone)]
pub struct Auth<A: App> {
pub user: A::User,
pub user_state: UserState,
pub session_id: A::ID,
pub(crate) _deny_public_constructor: (),
}
#[derive(Clone)]
pub enum MaybeAuth<A: App> {
Authenticated(Auth<A>),
Unauthenticated,
}
impl<A: App> MaybeAuth<A> {
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 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 maybe_auth_from_request<A: App>(request: &impl HttpMessage) -> MaybeAuth<A> {
let exts = request.extensions();
let auth = exts.get::<Auth<A>>();
match auth {
Some(auth) => MaybeAuth::Authenticated(Auth {
user: auth.user.clone(),
user_state: auth.user_state,
session_id: auth.session_id,
_deny_public_constructor: (),
}),
None => MaybeAuth::Unauthenticated,
}
}
pub fn require_auth_from_request<A: App>(request: &impl HttpMessage) -> Result<Auth<A>, A::Error> {
maybe_auth_from_request::<A>(request)
.require()
}
pub fn require_user_from_request<A: App>(request: &impl HttpMessage) -> Result<A::User, A::Error> {
require_auth_from_request::<A>(request)
.map(|auth| auth.user)
}
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 = maybe_auth_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 = require_auth_from_request(request);
std::future::ready(result)
}
}