chore: merge 'upstream/main' into multiple-authorizers2

This commit is contained in:
cduvray 2023-08-22 07:51:43 +02:00
commit cc7969db08
3 changed files with 33 additions and 6 deletions

View file

@ -58,6 +58,10 @@ pub enum AuthError {
#[error("No Authorizer")] #[error("No Authorizer")]
NoAuthorizer(), NoAuthorizer(),
/// Used when a claim extractor is used and no authorization layer is in front the handler
#[error("No Authorizer Layer")]
NoAuthorizerLayer(),
} }
fn response_wwwauth(status: StatusCode, bearer: &str) -> Response<BoxBody> { fn response_wwwauth(status: StatusCode, bearer: &str) -> Response<BoxBody> {
@ -120,6 +124,10 @@ impl From<AuthError> for Response<tonic::body::BoxBody> {
debug!("AuthErrors::NoAuthorizer"); debug!("AuthErrors::NoAuthorizer");
tonic::Status::unauthenticated("error=\"invalid_token\"") tonic::Status::unauthenticated("error=\"invalid_token\"")
} }
AuthError::NoAuthorizerLayer() => {
debug!("AuthErrors::NoAuthorizerLayer");
tonic::Status::unauthenticated("error=\"no_authorizer_layer\"")
}
} }
.to_http() .to_http()
} }
@ -177,6 +185,11 @@ impl IntoResponse for AuthError {
debug!("AuthErrors::NoAuthorizer"); debug!("AuthErrors::NoAuthorizer");
response_wwwauth(StatusCode::FORBIDDEN, "error=\"invalid_token\"") response_wwwauth(StatusCode::FORBIDDEN, "error=\"invalid_token\"")
} }
AuthError::NoAuthorizerLayer() => {
debug!("AuthErrors::NoAuthorizerLayer");
// TODO: should it be a standard error?
response_wwwauth(StatusCode::UNAUTHORIZED, "error=\"no_authorizer_layer\"")
}
} }
} }
} }

View file

@ -1,7 +1,6 @@
#![doc = include_str!("../docs/README.md")] #![doc = include_str!("../docs/README.md")]
use axum::{async_trait, extract::FromRequestParts, http::request::Parts}; use axum::{async_trait, extract::FromRequestParts, http::request::Parts};
use http::StatusCode;
use jsonwebtoken::TokenData; use jsonwebtoken::TokenData;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
@ -30,14 +29,13 @@ where
T: DeserializeOwned + Send + Sync + Clone + 'static, T: DeserializeOwned + Send + Sync + Clone + 'static,
S: Send + Sync, S: Send + Sync,
{ {
type Rejection = StatusCode; type Rejection = AuthError;
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> { async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
if let Some(claims) = parts.extensions.get::<TokenData<T>>() { if let Some(claims) = parts.extensions.get::<TokenData<T>>() {
Ok(JwtClaims(claims.claims.clone())) Ok(JwtClaims(claims.claims.clone()))
} else { } else {
tracing::error!("JwtClaims extractor must be behind a jwt-authoriser layer!"); Err(AuthError::NoAuthorizerLayer())
Err(StatusCode::INTERNAL_SERVER_ERROR)
} }
} }
} }

View file

@ -162,7 +162,7 @@ mod tests {
} }
#[tokio::test] #[tokio::test]
async fn extract_from_public_500() { async fn extract_from_public_401() {
let app = Router::new().route( let app = Router::new().route(
"/public", "/public",
get(|JwtClaims(user): JwtClaims<User>| async move { format!("hello: {}", user.sub) }), get(|JwtClaims(user): JwtClaims<User>| async move { format!("hello: {}", user.sub) }),
@ -172,7 +172,23 @@ mod tests {
.await .await
.unwrap(); .unwrap();
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR); assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn extract_from_public_optional() {
let app = Router::new().route(
"/public",
get(|user: Option<JwtClaims<User>>| async move { format!("option: {}", user.is_none()) }),
);
let response = app
.oneshot(Request::builder().uri("/public").body(Body::empty()).unwrap())
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
assert_eq!(&body[..], b"option: true");
} }
// -------------------- // --------------------