moved common axum middleware to common crate
This commit is contained in:
Generated
+6
@@ -4732,11 +4732,17 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"axum 0.7.5",
|
||||
"bytes",
|
||||
"colored",
|
||||
"futures",
|
||||
"mime",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tracing",
|
||||
"utoipa",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -12,9 +12,15 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
axum.workspace = true
|
||||
bytes = { workspace = true }
|
||||
mime = { workspace = true }
|
||||
bytes.workspace = true
|
||||
colored.workspace = true
|
||||
futures.workspace = true
|
||||
mime.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
serde_yaml = { workspace = true }
|
||||
utoipa = { workspace = true }
|
||||
serde_yaml.workspace = true
|
||||
tokio.workspace = true
|
||||
tower.workspace = true
|
||||
tracing.workspace = true
|
||||
utoipa.workspace = true
|
||||
zeroize.workspace = true
|
||||
|
||||
@@ -8,6 +8,8 @@ use bytes::{BufMut, BytesMut};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
pub mod middleware;
|
||||
|
||||
#[derive(Debug, Clone, ToSchema)]
|
||||
pub enum FormattedResponse<T> {
|
||||
Json(Json<T>),
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use axum::http::{header, HeaderValue, StatusCode};
|
||||
use axum::response::IntoResponse;
|
||||
use axum::{extract::Request, response::Response};
|
||||
use futures::future::BoxFuture;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use tower::{Layer, Service};
|
||||
use tracing::{debug, instrument, trace};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BearerAuthLayer {
|
||||
bearer_token: Arc<Zeroizing<String>>,
|
||||
}
|
||||
|
||||
impl BearerAuthLayer {
|
||||
pub fn new(bearer_token: Arc<Zeroizing<String>>) -> Self {
|
||||
BearerAuthLayer { bearer_token }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Layer<S> for BearerAuthLayer {
|
||||
type Service = RequireBearerAuth<S>;
|
||||
|
||||
fn layer(&self, inner: S) -> Self::Service {
|
||||
RequireBearerAuth::new(inner, self.bearer_token.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RequireBearerAuth<S> {
|
||||
inner: S,
|
||||
bearer_token: Arc<Zeroizing<String>>,
|
||||
}
|
||||
|
||||
impl<S> RequireBearerAuth<S> {
|
||||
pub fn new(inner: S, bearer_token: Arc<Zeroizing<String>>) -> Self {
|
||||
RequireBearerAuth {
|
||||
inner,
|
||||
bearer_token,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_auth_header(&self, header: Option<&HeaderValue>) -> Result<(), &'static str> {
|
||||
let Some(token) = header else {
|
||||
trace!("missing header");
|
||||
return Err("`Authorization` header is missing");
|
||||
};
|
||||
|
||||
let Ok(authorization) = token.to_str() else {
|
||||
trace!("invalid header");
|
||||
return Err("`Authorization` header contains invalid characters");
|
||||
};
|
||||
|
||||
debug!("header value: '{authorization}'");
|
||||
|
||||
let split = authorization.split_once(' ');
|
||||
let bearer_token = match split {
|
||||
// Found proper bearer
|
||||
Some(("Bearer", contents)) => contents,
|
||||
// Found empty bearer;
|
||||
_ if authorization == "Bearer" => "",
|
||||
// Found nothing
|
||||
_ => return Err("`Authorization` header must be a bearer token"),
|
||||
};
|
||||
|
||||
debug!("parsed token: '{bearer_token}'");
|
||||
|
||||
if self.bearer_token.is_empty() && bearer_token.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
if bearer_token.is_empty() {
|
||||
return Err("`Authorization` header must contain non-empty `Bearer` token");
|
||||
}
|
||||
|
||||
if self.bearer_token.as_str() != bearer_token {
|
||||
return Err("`Authorization` header does not contain the correct `Bearer` token");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Service<Request> for RequireBearerAuth<S>
|
||||
where
|
||||
S: Service<Request, Response = Response> + Send + 'static,
|
||||
S: Send + Sync + 'static,
|
||||
S::Future: Send + 'static,
|
||||
{
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
|
||||
#[inline]
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.inner.poll_ready(cx)
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(uri = %req.uri()))]
|
||||
fn call(&mut self, req: Request) -> Self::Future {
|
||||
debug!("checking the auth");
|
||||
|
||||
let auth_header = req.headers().get(header::AUTHORIZATION);
|
||||
|
||||
match self.check_auth_header(auth_header) {
|
||||
Ok(_authorised) => Box::pin(self.inner.call(req)),
|
||||
Err(err) => {
|
||||
Box::pin(async move { Ok((StatusCode::UNAUTHORIZED, err).into_response()) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod bearer;
|
||||
+15
-2
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use axum::{
|
||||
@@ -12,6 +12,7 @@ use axum::{
|
||||
};
|
||||
use colored::*;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::time::Instant;
|
||||
use tracing::info;
|
||||
|
||||
/// Simple logger for requests
|
||||
@@ -29,7 +30,9 @@ pub async fn logger(
|
||||
|
||||
let host = header_map(req.headers().get(HOST), "Unknown Host".to_string());
|
||||
|
||||
let start = Instant::now();
|
||||
let res = next.run(req).await;
|
||||
let time_taken = start.elapsed();
|
||||
let status = res.status();
|
||||
let print_status = if status.is_client_error() || status.is_server_error() {
|
||||
status.to_string().red()
|
||||
@@ -39,7 +42,17 @@ pub async fn logger(
|
||||
status.to_string().yellow()
|
||||
};
|
||||
|
||||
info!(target: "incoming request", "[{addr} -> {host}] {method} '{uri}': {print_status} / agent: {agent}");
|
||||
let taken = "time taken".bold();
|
||||
|
||||
let time_taken = match time_taken.as_millis() {
|
||||
ms if ms > 500 => format!("{taken}: {}", format!("{ms}ms").red()),
|
||||
ms if ms > 200 => format!("{taken}: {}", format!("{ms}ms").yellow()),
|
||||
ms if ms > 50 => format!("{taken}: {}", format!("{ms}ms").bright_yellow()),
|
||||
ms => format!("{taken}: {ms}ms"),
|
||||
};
|
||||
|
||||
let agent_str = "agent".bold();
|
||||
info!("[{addr} -> {host}] {method} '{uri}': {print_status} {time_taken} {agent_str}: {agent}");
|
||||
|
||||
res
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod auth;
|
||||
pub mod logging;
|
||||
|
||||
pub use auth::bearer::{BearerAuthLayer, RequireBearerAuth};
|
||||
pub use logging::logger;
|
||||
+5
-7
@@ -32,8 +32,6 @@ pin-project = { workspace = true }
|
||||
rand = "0.8.5"
|
||||
rand-07 = { package = "rand", version = "0.7.3" } # required for compatibility
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
rocket = { workspace = true, features = ["json"] }
|
||||
rocket_cors = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tap = { workspace = true }
|
||||
@@ -55,11 +53,14 @@ sqlx = { workspace = true, features = [
|
||||
"migrate",
|
||||
] }
|
||||
|
||||
okapi = { workspace = true, features = ["impl_json_schema"] }
|
||||
rocket_okapi = { workspace = true, features = ["swagger"] }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
rocket = { workspace = true, features = ["json"] }
|
||||
rocket_cors = { workspace = true }
|
||||
rocket_okapi = { workspace = true, features = ["swagger"] }
|
||||
okapi = { workspace = true, features = ["impl_json_schema"] }
|
||||
|
||||
## ephemera-specific
|
||||
#actix-web = "4"
|
||||
#array-bytes = "6.0.0"
|
||||
@@ -68,12 +69,9 @@ zeroize = { workspace = true }
|
||||
#serde_derive = "1.0.149"
|
||||
#uuid = { version = "1.3.0", features = ["serde", "v4"] }
|
||||
|
||||
## internal
|
||||
#ephemera = { path = "../ephemera" }
|
||||
nym-bandwidth-controller = { path = "../common/bandwidth-controller" }
|
||||
nym-coconut-bandwidth-contract-common = { path = "../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
||||
nym-coconut-dkg-common = { path = "../common/cosmwasm-smart-contracts/coconut-dkg" }
|
||||
#nym-ephemera-common = { path = "../common/cosmwasm-smart-contracts/ephemera" }
|
||||
nym-config = { path = "../common/config" }
|
||||
cosmwasm-std = { workspace = true }
|
||||
nym-credential-storage = { path = "../common/credential-storage" }
|
||||
|
||||
@@ -11,7 +11,6 @@ use std::net::SocketAddr;
|
||||
use tracing::{debug, error};
|
||||
|
||||
pub mod error;
|
||||
pub mod middleware;
|
||||
pub mod router;
|
||||
pub mod state;
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub mod logging;
|
||||
@@ -9,7 +9,6 @@ use nym_task::TaskClient;
|
||||
use std::net::SocketAddr;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
pub mod middleware;
|
||||
pub mod router;
|
||||
pub mod state;
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
pub use crate::api::v1::gateway::client_interfaces::wireguard::WireguardAppState;
|
||||
use crate::error::NymNodeHttpError;
|
||||
use crate::middleware::logging;
|
||||
use crate::state::AppState;
|
||||
use crate::NymNodeHTTPServer;
|
||||
use axum::response::Redirect;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use nym_http_api_common::middleware::logger;
|
||||
use nym_node_requests::api::v1::gateway::models::{Gateway, Wireguard};
|
||||
use nym_node_requests::api::v1::ip_packet_router::models::IpPacketRouter;
|
||||
use nym_node_requests::api::v1::mixnode::models::Mixnode;
|
||||
@@ -193,7 +193,7 @@ impl NymNodeRouter {
|
||||
routes::API,
|
||||
api::routes(config.api, initial_wg_state.unwrap_or_default()),
|
||||
)
|
||||
.layer(axum::middleware::from_fn(logging::logger))
|
||||
.layer(axum::middleware::from_fn(logger))
|
||||
.with_state(state),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user