#![allow(dead_code)]
use axum::{
extract::FromRequestParts,
http::{HeaderMap, Method, Uri, request::Parts},
response::{IntoResponse, Redirect, Response},
};
use ocre::{Ctx, Error, Result, Session, security::url_from};
use crate::models::user::{self, User};
pub const USER_ID: &str = "user_id";
const RETURN_TO: &str = "return_to";
pub const SESSION_SECONDS: i64 = 14 * 24 * 3600;
pub const OAUTH_PROVIDERS: &[&str] = &[];
pub struct CurrentUser(pub User);
pub struct OptionalUser(pub Option<User>);
pub struct ConfirmedUser(pub User);
impl FromRequestParts<Ctx> for OptionalUser {
type Rejection = Error;
async fn from_request_parts(parts: &mut Parts, ctx: &Ctx) -> Result<Self> {
let session = Session::from_request_parts(parts, ctx).await?;
match session.get::<i64>(USER_ID)? {
Some(id) => Ok(Self(user::find(ctx, id).await?)),
None => Ok(Self(None)),
}
}
}
impl FromRequestParts<Ctx> for CurrentUser {
type Rejection = Response;
async fn from_request_parts(parts: &mut Parts, ctx: &Ctx) -> std::result::Result<Self, Response> {
match OptionalUser::from_request_parts(parts, ctx).await {
Ok(OptionalUser(Some(user))) => Ok(Self(user)),
Ok(OptionalUser(None)) => Err(to_login(parts, ctx).await.unwrap_or_else(IntoResponse::into_response)),
Err(err) => Err(err.into_response()),
}
}
}
impl FromRequestParts<Ctx> for ConfirmedUser {
type Rejection = Response;
async fn from_request_parts(parts: &mut Parts, ctx: &Ctx) -> std::result::Result<Self, Response> {
let CurrentUser(user) = CurrentUser::from_request_parts(parts, ctx).await?;
if user.confirmed() {
return Ok(Self(user));
}
let session = Session::from_request_parts(parts, ctx).await.map_err(IntoResponse::into_response)?;
session.flash("alert", "Please confirm your email address first.").map_err(IntoResponse::into_response)?;
Err(Redirect::to("/account").into_response())
}
}
async fn to_login(parts: &mut Parts, ctx: &Ctx) -> Result<Response> {
let session = Session::from_request_parts(parts, ctx).await?;
if parts.method == Method::GET {
session.insert(RETURN_TO, parts.uri.path_and_query().map_or("/", |path| path.as_str()))?;
}
session.flash("alert", "Please log in to continue.")?;
Ok(Redirect::to("/login").into_response())
}
pub async fn sign_in(_ctx: &Ctx, session: &Session, _headers: &HeaderMap, user: &User, remember: bool) -> Result<String> {
let return_to = session.get::<String>(RETURN_TO)?.and_then(|path| url_from(&Uri::default(), &path));
session.clear()?;
session.insert(USER_ID, user.id)?;
if remember { session.remember_for(SESSION_SECONDS)? } else { session.expire_in(SESSION_SECONDS)? }
Ok(return_to.unwrap_or_else(|| "/".to_owned()))
}
pub async fn sign_out(_ctx: &Ctx, session: &Session) -> Result<()> {
session.clear()
}
pub fn origin(uri: &Uri) -> String {
let scheme = uri.scheme_str().unwrap_or("https");
let host = uri.authority().map_or("localhost", |authority| authority.as_str());
format!("{scheme}://{host}")
}