Merge pull request #22 from IMPHNEN/feat/split-middleware
feat: split middleware
This commit is contained in:
Generated
+48
@@ -1874,6 +1874,26 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "imphnen-cms-service"
|
name = "imphnen-cms-service"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"axum",
|
||||||
|
"axum-test",
|
||||||
|
"chrono",
|
||||||
|
"imphnen-entities",
|
||||||
|
"imphnen-libs",
|
||||||
|
"imphnen-utils",
|
||||||
|
"lazy_static",
|
||||||
|
"rand 0.9.0",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"surrealdb",
|
||||||
|
"tokio",
|
||||||
|
"tower-http",
|
||||||
|
"utoipa",
|
||||||
|
"utoipa-swagger-ui",
|
||||||
|
"validator",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "imphnen-dimentorin-service"
|
name = "imphnen-dimentorin-service"
|
||||||
@@ -1945,6 +1965,7 @@ dependencies = [
|
|||||||
"imphnen-entities",
|
"imphnen-entities",
|
||||||
"imphnen-iam-service",
|
"imphnen-iam-service",
|
||||||
"imphnen-libs",
|
"imphnen-libs",
|
||||||
|
"imphnen-middleware-service",
|
||||||
"imphnen-utils",
|
"imphnen-utils",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"rand 0.9.0",
|
"rand 0.9.0",
|
||||||
@@ -1998,6 +2019,33 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imphnen-middleware-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"axum",
|
||||||
|
"axum-test",
|
||||||
|
"chrono",
|
||||||
|
"futures",
|
||||||
|
"imphnen-entities",
|
||||||
|
"imphnen-iam-service",
|
||||||
|
"imphnen-libs",
|
||||||
|
"imphnen-utils",
|
||||||
|
"lazy_static",
|
||||||
|
"rand 0.9.0",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"surrealdb",
|
||||||
|
"tokio",
|
||||||
|
"tower",
|
||||||
|
"tower-http",
|
||||||
|
"utoipa",
|
||||||
|
"utoipa-swagger-ui",
|
||||||
|
"validator",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "imphnen-utils"
|
name = "imphnen-utils"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
pub fn add(left: u64, right: u64) -> u64 {
|
||||||
|
left + right
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
let result = add(2, 2);
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
println!("Hello, world!");
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
imphnen-iam-service = { version = "0.1.0", path = "../imphnen-iam-service" }
|
imphnen-iam-service = { version = "0.1.0", path = "../imphnen-iam-service" }
|
||||||
|
imphnen-middleware-service = { version = "0.1.0", path = "../imphnen-middleware-service" }
|
||||||
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
|
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
|
||||||
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
|
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
|
||||||
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
|
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
|
||||||
|
|||||||
@@ -5,10 +5,8 @@ use imphnen_entities::{AppState, SurrealMemClient, SurrealWsClient};
|
|||||||
use imphnen_iam_service::{iam_protected_routes, iam_public_routes};
|
use imphnen_iam_service::{iam_protected_routes, iam_public_routes};
|
||||||
|
|
||||||
pub mod docs;
|
pub mod docs;
|
||||||
pub mod middleware;
|
|
||||||
|
|
||||||
pub use docs::*;
|
pub use docs::*;
|
||||||
pub use middleware::*;
|
use imphnen_middleware_service::{auth_middleware, cors_middleware};
|
||||||
use utoipa_swagger_ui::SwaggerUi;
|
use utoipa_swagger_ui::SwaggerUi;
|
||||||
|
|
||||||
pub async fn gateway_service(
|
pub async fn gateway_service(
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[package]
|
||||||
|
name = "imphnen-middleware-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
imphnen-iam-service = { version = "0.1.0", path = "../imphnen-iam-service" }
|
||||||
|
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
|
||||||
|
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
|
||||||
|
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
|
||||||
|
axum.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
utoipa.workspace = true
|
||||||
|
lazy_static.workspace = true
|
||||||
|
regex.workspace = true
|
||||||
|
validator.workspace = true
|
||||||
|
axum-test.workspace = true
|
||||||
|
surrealdb.workspace = true
|
||||||
|
rand.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
|
chrono.workspace = true
|
||||||
|
anyhow.workspace = true
|
||||||
|
tower-http.workspace = true
|
||||||
|
utoipa-swagger-ui.workspace = true
|
||||||
|
futures = "0.3.31"
|
||||||
|
tower = "0.5.2"
|
||||||
+2
-36
@@ -1,15 +1,11 @@
|
|||||||
use axum::{
|
use axum::{
|
||||||
Extension,
|
Extension, extract::Request, http::StatusCode, middleware::Next,
|
||||||
extract::Request,
|
|
||||||
http::{HeaderValue, Method, StatusCode, header},
|
|
||||||
middleware::Next,
|
|
||||||
response::Response,
|
response::Response,
|
||||||
};
|
};
|
||||||
use imphnen_iam_service::{UsersItemDtoRaw, UsersRepository};
|
use imphnen_iam_service::{UsersItemDtoRaw, UsersRepository};
|
||||||
use imphnen_libs::{AppState, Env};
|
use imphnen_libs::AppState;
|
||||||
use imphnen_utils::{common_response, extract_email};
|
use imphnen_utils::{common_response, extract_email};
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use tower_http::cors::CorsLayer;
|
|
||||||
|
|
||||||
pub async fn auth_middleware(
|
pub async fn auth_middleware(
|
||||||
Extension(state): Extension<AppState>,
|
Extension(state): Extension<AppState>,
|
||||||
@@ -46,33 +42,3 @@ pub async fn auth_middleware(
|
|||||||
req.extensions_mut().insert(user.unwrap());
|
req.extensions_mut().insert(user.unwrap());
|
||||||
Ok(next.run(req).await)
|
Ok(next.run(req).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cors_middleware() -> CorsLayer {
|
|
||||||
let env = Env::new();
|
|
||||||
let cors_origins = match env.rust_env.as_str() {
|
|
||||||
"development" => vec!["http://localhost:3000"],
|
|
||||||
"production" => {
|
|
||||||
vec![
|
|
||||||
"https://gacha.imphnen.dev",
|
|
||||||
"https://imphnen.dev",
|
|
||||||
"https://dimentorin.imphnen.dev",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
_ => vec![
|
|
||||||
"http://localhost:3000",
|
|
||||||
"https://gacha.imphnen.dev",
|
|
||||||
"https://imphnen.dev",
|
|
||||||
"https://dimentorin.imphnen.dev",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
let allowed_origins: Vec<HeaderValue> = cors_origins
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|origin| origin.parse::<HeaderValue>().ok())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
CorsLayer::new()
|
|
||||||
.allow_origin(allowed_origins)
|
|
||||||
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
|
|
||||||
.allow_headers([header::AUTHORIZATION, header::CONTENT_TYPE])
|
|
||||||
.allow_credentials(true)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
use axum::http::{HeaderValue, Method, header};
|
||||||
|
use imphnen_libs::Env;
|
||||||
|
use tower_http::cors::CorsLayer;
|
||||||
|
|
||||||
|
pub fn cors_middleware() -> CorsLayer {
|
||||||
|
let env = Env::new();
|
||||||
|
let cors_origins = match env.rust_env.as_str() {
|
||||||
|
"development" => vec!["http://localhost:3000"],
|
||||||
|
"production" => {
|
||||||
|
vec![
|
||||||
|
"https://gacha.imphnen.dev",
|
||||||
|
"https://imphnen.dev",
|
||||||
|
"https://dimentorin.imphnen.dev",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
_ => vec![
|
||||||
|
"http://localhost:3000",
|
||||||
|
"https://gacha.imphnen.dev",
|
||||||
|
"https://imphnen.dev",
|
||||||
|
"https://dimentorin.imphnen.dev",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let allowed_origins: Vec<HeaderValue> = cors_origins
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|origin| origin.parse::<HeaderValue>().ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
CorsLayer::new()
|
||||||
|
.allow_origin(allowed_origins)
|
||||||
|
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
|
||||||
|
.allow_headers([header::AUTHORIZATION, header::CONTENT_TYPE])
|
||||||
|
.allow_credentials(true)
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
pub mod auth_middleware;
|
||||||
|
pub mod cors_middleware;
|
||||||
|
pub mod permissions_middleware;
|
||||||
|
|
||||||
|
pub use auth_middleware::*;
|
||||||
|
pub use cors_middleware::*;
|
||||||
|
pub use permissions_middleware::*;
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
use axum::{
|
||||||
|
body::Body,
|
||||||
|
http::{Request, Response, StatusCode},
|
||||||
|
};
|
||||||
|
use futures::future::BoxFuture;
|
||||||
|
use imphnen_iam_service::{AuthRepository, PermissionsEnum};
|
||||||
|
use imphnen_libs::AppState;
|
||||||
|
use imphnen_utils::{common_response, extract_email};
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use tower::{Layer, Service};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PermissionsMiddlewareLayer {
|
||||||
|
app_state: AppState,
|
||||||
|
permissions: Vec<PermissionsEnum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PermissionsMiddlewareLayer {
|
||||||
|
pub fn new(app_state: AppState, permissions: Vec<PermissionsEnum>) -> Self {
|
||||||
|
Self {
|
||||||
|
app_state,
|
||||||
|
permissions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Layer<S> for PermissionsMiddlewareLayer {
|
||||||
|
type Service = PermissionsMiddleware<S>;
|
||||||
|
fn layer(&self, inner: S) -> Self::Service {
|
||||||
|
PermissionsMiddleware {
|
||||||
|
inner,
|
||||||
|
app_state: self.app_state.clone(),
|
||||||
|
permissions: self.permissions.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PermissionsMiddleware<S> {
|
||||||
|
inner: S,
|
||||||
|
app_state: AppState,
|
||||||
|
permissions: Vec<PermissionsEnum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Service<Request<Body>> for PermissionsMiddleware<S>
|
||||||
|
where
|
||||||
|
S: Service<Request<Body>, Response = Response<Body>> + Clone + Send + 'static,
|
||||||
|
S::Future: Send + 'static,
|
||||||
|
{
|
||||||
|
type Response = S::Response;
|
||||||
|
type Error = S::Error;
|
||||||
|
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
self.inner.poll_ready(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||||
|
let mut inner = self.inner.clone();
|
||||||
|
let app_state = self.app_state.clone();
|
||||||
|
let permissions = self.permissions.clone();
|
||||||
|
|
||||||
|
Box::pin(async move {
|
||||||
|
let headers = req.headers();
|
||||||
|
let email = match extract_email(headers) {
|
||||||
|
Some(email) => email,
|
||||||
|
None => {
|
||||||
|
return Ok(common_response(
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
"Invalid or missing authorization token",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let auth_repo = AuthRepository::new(&app_state);
|
||||||
|
let user = match auth_repo.query_get_stored_user(email).await {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(_) => {
|
||||||
|
return Ok(common_response(
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
"User session expired or not found",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_permissions: Vec<String> =
|
||||||
|
user.role.permissions.into_iter().map(|p| p.name).collect();
|
||||||
|
let allowed = permissions
|
||||||
|
.iter()
|
||||||
|
.all(|p| user_permissions.contains(&p.to_string()));
|
||||||
|
|
||||||
|
if !allowed {
|
||||||
|
return Ok(common_response(
|
||||||
|
StatusCode::FORBIDDEN,
|
||||||
|
"You don't have the required permissions",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
inner.call(req).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user