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>;
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
LoginFail,
|
LoginFail,
|
||||||
|
|
||||||
// -- Auth errors.
|
// -- Auth errors.
|
||||||
AuthFailNoAuthTokenCookie,
|
AuthFailNoAuthTokenCookie,
|
||||||
AuthFailTokenWrongFormat,
|
AuthFailTokenWrongFormat,
|
||||||
|
AuthFailCtxNotInRequestExt,
|
||||||
|
|
||||||
// -- Model errors.
|
// -- Model errors.
|
||||||
PropertyDeleteFailIdNotFound { id: u64 },
|
PropertyDeleteFailIdNotFound { id: u64 },
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ async fn main() -> Result<()>{
|
||||||
.merge(web::routes_login::routes())
|
.merge(web::routes_login::routes())
|
||||||
.nest("/api", routes_apis)
|
.nest("/api", routes_apis)
|
||||||
.layer(middleware::map_response(main_response_mapper))
|
.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
|
.layer(CookieManagerLayer::new()) // must be above? the auth routes
|
||||||
// TODO: continue video at 22:15
|
// TODO: continue video at 22:15
|
||||||
.fallback_service(routes_static());
|
.fallback_service(routes_static());
|
||||||
|
|
|
||||||
11
src/model.rs
11
src/model.rs
|
|
@ -1,7 +1,7 @@
|
||||||
//! Simplistic model layer
|
//! Simplistic model layer
|
||||||
//! (with mock-store layer)
|
//! (with mock-store layer)
|
||||||
|
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result, ctx::Ctx};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
|
@ -9,6 +9,7 @@ use std::sync::{Arc, Mutex};
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub struct Property {
|
pub struct Property {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
|
pub creator_id: u64,
|
||||||
pub address: String,
|
pub address: String,
|
||||||
pub contact: String,
|
pub contact: String,
|
||||||
}
|
}
|
||||||
|
|
@ -37,21 +38,21 @@ impl ModelController {
|
||||||
|
|
||||||
// CRUD implementation
|
// CRUD implementation
|
||||||
impl ModelController {
|
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 mut store = self.property_store.lock().unwrap();
|
||||||
let id = store.len() as u64;
|
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()));
|
store.push(Some(property.clone()));
|
||||||
Ok(property)
|
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 store = self.property_store.lock().unwrap();
|
||||||
let properties = store.iter().filter_map(|p| p.clone()).collect();
|
let properties = store.iter().filter_map(|p| p.clone()).collect();
|
||||||
Ok(properties)
|
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 mut store = self.property_store.lock().unwrap();
|
||||||
let property = store.get_mut(id as usize).and_then(|p| p.take());
|
let property = store.get_mut(id as usize).and_then(|p| p.take());
|
||||||
property.ok_or(Error::PropertyDeleteFailIdNotFound { id })
|
property.ok_or(Error::PropertyDeleteFailIdNotFound { id })
|
||||||
|
|
|
||||||
|
|
@ -6,33 +6,58 @@ use axum::http::request::Parts;
|
||||||
use axum::middleware::Next;
|
use axum::middleware::Next;
|
||||||
use axum::response::Response;
|
use axum::response::Response;
|
||||||
use lazy_regex::regex_captures;
|
use lazy_regex::regex_captures;
|
||||||
use tower_cookies::Cookies;
|
use tower_cookies::{Cookies, Cookie};
|
||||||
|
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
use crate::web::AUTH_TOKEN;
|
use crate::web::AUTH_TOKEN;
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result};
|
||||||
|
|
||||||
pub async fn mw_require_auth<B>(
|
pub async fn mw_ctx_resolver<B>(
|
||||||
cookies: Cookies,
|
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>,
|
req: Request<B>,
|
||||||
next: Next<B>
|
next: Next<B>
|
||||||
) -> Result<Response> {
|
) -> Result<Response> {
|
||||||
println!("->> {:<12} - mw_require_auth", "MIDDLEWARE");
|
println!("->> {:<12} - mw_require_auth - {ctx:?}", "MIDDLEWARE");
|
||||||
let auth_token = cookies.get(AUTH_TOKEN).map(|c| c.value().to_string());
|
|
||||||
|
ctx?;
|
||||||
// Parse token.
|
|
||||||
let (user_id, exp, sign) = auth_token
|
|
||||||
.ok_or(Error::AuthFailNoAuthTokenCookie)
|
|
||||||
.and_then(parse_token)?;
|
|
||||||
|
|
||||||
// TODO: Token components validation.
|
|
||||||
|
|
||||||
Ok(next.run(req).await)
|
Ok(next.run(req).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
// region: --- Ctx Extractor
|
// region: --- Ctx Extractor
|
||||||
|
|
||||||
// TODO: Find out what all this syntax means
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<S: Send + Sync> FromRequestParts<S> for Ctx {
|
impl<S: Send + Sync> FromRequestParts<S> for Ctx {
|
||||||
type Rejection = Error;
|
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> {
|
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self> {
|
||||||
println!("->> {:<12} - Ctx", "EXTRACTOR");
|
println!("->> {:<12} - Ctx", "EXTRACTOR");
|
||||||
|
|
||||||
// Use the cookies extractor.
|
parts
|
||||||
let cookies = parts.extract::<Cookies>().await.unwrap();
|
.extensions
|
||||||
|
.get::<Result<Ctx>>()
|
||||||
|
.ok_or(Error::AuthFailCtxNotInRequestExt)?
|
||||||
|
.clone()
|
||||||
|
|
||||||
let auth_token = cookies.get(AUTH_TOKEN).map(|c| c.value().to_string());
|
// // Use the cookies extractor.
|
||||||
|
// let cookies = parts.extract::<Cookies>().await.unwrap();
|
||||||
// Parse token.
|
//
|
||||||
let (user_id, exp, sign) = auth_token
|
// let auth_token = cookies.get(AUTH_TOKEN).map(|c| c.value().to_string());
|
||||||
.ok_or(Error::AuthFailNoAuthTokenCookie)
|
//
|
||||||
.and_then(parse_token)?;
|
// // Parse token.
|
||||||
|
// let (user_id, exp, sign) = auth_token
|
||||||
// TODO: Token components validation.
|
// .ok_or(Error::AuthFailNoAuthTokenCookie)
|
||||||
|
// .and_then(parse_token)?;
|
||||||
Ok(Ctx::new(user_id))
|
//
|
||||||
|
// // 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::model::{ModelController, Property, PropertyForCreate};
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use axum::extract::{Path, State};
|
use axum::extract::{Path, State};
|
||||||
|
|
@ -13,21 +14,21 @@ pub fn routes(mc: ModelController) -> Router {
|
||||||
|
|
||||||
// region: --- REST Handlers
|
// 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");
|
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))
|
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");
|
println!("->> {:<12} - list_properties", "HANDLER");
|
||||||
let properties = mc.list_properties().await?;
|
let properties = mc.list_properties(ctx).await?;
|
||||||
Ok(Json(properties))
|
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");
|
println!("->> {:<12} - delete_property", "HANDLER");
|
||||||
let property = mc.delete_property(id).await?;
|
let property = mc.delete_property(ctx, id).await?;
|
||||||
Ok(Json(property))
|
Ok(Json(property))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue