feat: ctx resolver
This commit is contained in:
parent
a38438a700
commit
8843134aa6
5 changed files with 71 additions and 36 deletions
|
|
@ -3,13 +3,14 @@ use axum::response::{IntoResponse, Response};
|
|||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Error {
|
||||
LoginFail,
|
||||
|
||||
// -- Auth errors.
|
||||
AuthFailNoAuthTokenCookie,
|
||||
AuthFailTokenWrongFormat,
|
||||
AuthFailCtxNotInRequestExt,
|
||||
|
||||
// -- Model errors.
|
||||
PropertyDeleteFailIdNotFound { id: u64 },
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ async fn main() -> Result<()>{
|
|||
.merge(web::routes_login::routes())
|
||||
.nest("/api", routes_apis)
|
||||
.layer(middleware::map_response(main_response_mapper))
|
||||
.layer(middleware::from_fn_with_state(mc.clone(), web::mw_auth::mw_ctx_resolver))
|
||||
.layer(CookieManagerLayer::new()) // must be above? the auth routes
|
||||
// TODO: continue video at 22:15
|
||||
.fallback_service(routes_static());
|
||||
|
|
|
|||
11
src/model.rs
11
src/model.rs
|
|
@ -1,7 +1,7 @@
|
|||
//! Simplistic model layer
|
||||
//! (with mock-store layer)
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::{Error, Result, ctx::Ctx};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
|
|
@ -9,6 +9,7 @@ use std::sync::{Arc, Mutex};
|
|||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct Property {
|
||||
pub id: u64,
|
||||
pub creator_id: u64,
|
||||
pub address: String,
|
||||
pub contact: String,
|
||||
}
|
||||
|
|
@ -37,21 +38,21 @@ impl ModelController {
|
|||
|
||||
// CRUD implementation
|
||||
impl ModelController {
|
||||
pub async fn create_property(&self, property: PropertyForCreate) -> Result<Property> {
|
||||
pub async fn create_property(&self, ctx: Ctx, property: PropertyForCreate) -> Result<Property> {
|
||||
let mut store = self.property_store.lock().unwrap();
|
||||
let id = store.len() as u64;
|
||||
let property = Property { id, address: property.address, contact: property.contact };
|
||||
let property = Property { id, creator_id: ctx.user_id(), address: property.address, contact: property.contact };
|
||||
store.push(Some(property.clone()));
|
||||
Ok(property)
|
||||
}
|
||||
|
||||
pub async fn list_properties(&self) -> Result<Vec<Property>> {
|
||||
pub async fn list_properties(&self, ctx: Ctx) -> Result<Vec<Property>> {
|
||||
let store = self.property_store.lock().unwrap();
|
||||
let properties = store.iter().filter_map(|p| p.clone()).collect();
|
||||
Ok(properties)
|
||||
}
|
||||
|
||||
pub async fn delete_property(&self, id: u64) -> Result<Property> {
|
||||
pub async fn delete_property(&self, ctx: Ctx, id: u64) -> Result<Property> {
|
||||
let mut store = self.property_store.lock().unwrap();
|
||||
let property = store.get_mut(id as usize).and_then(|p| p.take());
|
||||
property.ok_or(Error::PropertyDeleteFailIdNotFound { id })
|
||||
|
|
|
|||
|
|
@ -6,33 +6,58 @@ use axum::http::request::Parts;
|
|||
use axum::middleware::Next;
|
||||
use axum::response::Response;
|
||||
use lazy_regex::regex_captures;
|
||||
use tower_cookies::Cookies;
|
||||
use tower_cookies::{Cookies, Cookie};
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
use crate::web::AUTH_TOKEN;
|
||||
use crate::{Error, Result};
|
||||
|
||||
pub async fn mw_require_auth<B>(
|
||||
pub async fn mw_ctx_resolver<B>(
|
||||
cookies: Cookies,
|
||||
mut req: Request<B>,
|
||||
next: Next<B>,
|
||||
) -> Result<Response> {
|
||||
println!("->> {:<12} - mw_ctx_resolver", "MIDDLEWARE");
|
||||
|
||||
let auth_token = cookies.get(AUTH_TOKEN).map(|c| c.value().to_string());
|
||||
|
||||
// Compute Result<Ctx>.
|
||||
let result_ctx = match auth_token.
|
||||
ok_or(Error::AuthFailNoAuthTokenCookie)
|
||||
.and_then(parse_token) {
|
||||
Ok((user_id, _exp, _sign)) => {
|
||||
// TODO: Token components validations.
|
||||
Ok(Ctx::new(user_id))
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
|
||||
// Remove the cookie if something went wrong other than NoAuthTokenCookie.
|
||||
if result_ctx.is_err() && !matches!(result_ctx, Err(Error::AuthFailNoAuthTokenCookie)) {
|
||||
cookies.remove(Cookie::named(AUTH_TOKEN))
|
||||
}
|
||||
|
||||
// Store the ctx_result in the request extension.
|
||||
req.extensions_mut().insert(result_ctx);
|
||||
|
||||
Ok(next.run(req).await)
|
||||
}
|
||||
|
||||
/// Middleware to require authentication.
|
||||
pub async fn mw_require_auth<B>(
|
||||
ctx: Result<Ctx>,
|
||||
req: Request<B>,
|
||||
next: Next<B>
|
||||
) -> Result<Response> {
|
||||
println!("->> {:<12} - mw_require_auth", "MIDDLEWARE");
|
||||
let auth_token = cookies.get(AUTH_TOKEN).map(|c| c.value().to_string());
|
||||
|
||||
// Parse token.
|
||||
let (user_id, exp, sign) = auth_token
|
||||
.ok_or(Error::AuthFailNoAuthTokenCookie)
|
||||
.and_then(parse_token)?;
|
||||
|
||||
// TODO: Token components validation.
|
||||
println!("->> {:<12} - mw_require_auth - {ctx:?}", "MIDDLEWARE");
|
||||
|
||||
ctx?;
|
||||
|
||||
Ok(next.run(req).await)
|
||||
}
|
||||
|
||||
// region: --- Ctx Extractor
|
||||
|
||||
// TODO: Find out what all this syntax means
|
||||
#[async_trait]
|
||||
impl<S: Send + Sync> FromRequestParts<S> for Ctx {
|
||||
type Rejection = Error;
|
||||
|
|
@ -40,19 +65,25 @@ impl<S: Send + Sync> FromRequestParts<S> for Ctx {
|
|||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self> {
|
||||
println!("->> {:<12} - Ctx", "EXTRACTOR");
|
||||
|
||||
// Use the cookies extractor.
|
||||
let cookies = parts.extract::<Cookies>().await.unwrap();
|
||||
parts
|
||||
.extensions
|
||||
.get::<Result<Ctx>>()
|
||||
.ok_or(Error::AuthFailCtxNotInRequestExt)?
|
||||
.clone()
|
||||
|
||||
let auth_token = cookies.get(AUTH_TOKEN).map(|c| c.value().to_string());
|
||||
|
||||
// Parse token.
|
||||
let (user_id, exp, sign) = auth_token
|
||||
.ok_or(Error::AuthFailNoAuthTokenCookie)
|
||||
.and_then(parse_token)?;
|
||||
|
||||
// TODO: Token components validation.
|
||||
|
||||
Ok(Ctx::new(user_id))
|
||||
// // Use the cookies extractor.
|
||||
// let cookies = parts.extract::<Cookies>().await.unwrap();
|
||||
//
|
||||
// let auth_token = cookies.get(AUTH_TOKEN).map(|c| c.value().to_string());
|
||||
//
|
||||
// // Parse token.
|
||||
// let (user_id, exp, sign) = auth_token
|
||||
// .ok_or(Error::AuthFailNoAuthTokenCookie)
|
||||
// .and_then(parse_token)?;
|
||||
//
|
||||
// // TODO: Token components validation.
|
||||
//
|
||||
// Ok(Ctx::new(user_id))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::ctx::Ctx;
|
||||
use crate::model::{ModelController, Property, PropertyForCreate};
|
||||
use crate::Result;
|
||||
use axum::extract::{Path, State};
|
||||
|
|
@ -13,21 +14,21 @@ pub fn routes(mc: ModelController) -> Router {
|
|||
|
||||
// region: --- REST Handlers
|
||||
|
||||
async fn create_property(State(mc): State<ModelController>, Json(property_fc): Json<PropertyForCreate>) -> Result<Json<Property>> {
|
||||
async fn create_property(State(mc): State<ModelController>, ctx: Ctx, Json(property_fc): Json<PropertyForCreate>) -> Result<Json<Property>> {
|
||||
println!("->> {:<12} - create_property", "HANDLER");
|
||||
let property = mc.create_property(property_fc).await?;
|
||||
let property = mc.create_property(ctx, property_fc).await?;
|
||||
Ok(Json(property))
|
||||
}
|
||||
|
||||
async fn list_properties(State(mc): State<ModelController>) -> Result<Json<Vec<Property>>> {
|
||||
async fn list_properties(State(mc): State<ModelController>, ctx: Ctx) -> Result<Json<Vec<Property>>> {
|
||||
println!("->> {:<12} - list_properties", "HANDLER");
|
||||
let properties = mc.list_properties().await?;
|
||||
let properties = mc.list_properties(ctx).await?;
|
||||
Ok(Json(properties))
|
||||
}
|
||||
|
||||
async fn delete_property(State(mc): State<ModelController>, Path(id): Path<u64>) -> Result<Json<Property>> {
|
||||
async fn delete_property(State(mc): State<ModelController>, ctx: Ctx, Path(id): Path<u64>) -> Result<Json<Property>> {
|
||||
println!("->> {:<12} - delete_property", "HANDLER");
|
||||
let property = mc.delete_property(id).await?;
|
||||
let property = mc.delete_property(ctx, id).await?;
|
||||
Ok(Json(property))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue