chore: remove tonic temporarily (waiting hyperium/tonic/#1595)

This commit is contained in:
cduvray 2024-01-20 14:28:07 +01:00
parent 12eeed5240
commit 5f71827bf2
5 changed files with 11 additions and 473 deletions

216
Cargo.lock generated
View file

@ -68,28 +68,6 @@ dependencies = [
"futures-core",
]
[[package]]
name = "async-stream"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
dependencies = [
"async-stream-impl",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-stream-impl"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "async-trait"
version = "0.1.73"
@ -107,34 +85,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "axum"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
dependencies = [
"async-trait",
"axum-core 0.3.4",
"bitflags 1.3.2",
"bytes",
"futures-util",
"http 0.2.9",
"http-body 0.4.5",
"hyper 0.14.27",
"itoa",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"sync_wrapper",
"tower",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum"
version = "0.7.1"
@ -142,7 +92,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "810a80b128d70e6ed2bdf3fe8ed72c0ae56f5f5948d01c2753282dd92a84fce8"
dependencies = [
"async-trait",
"axum-core 0.4.0",
"axum-core",
"bytes",
"futures-util",
"http 1.0.0",
@ -168,23 +118,6 @@ dependencies = [
"tower-service",
]
[[package]]
name = "axum-core"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
dependencies = [
"async-trait",
"bytes",
"futures-util",
"http 0.2.9",
"http-body 0.4.5",
"mime",
"rustversion",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum-core"
version = "0.4.0"
@ -381,7 +314,7 @@ name = "demo-server"
version = "0.1.0"
dependencies = [
"anyhow",
"axum 0.7.1",
"axum",
"headers",
"josekit",
"jsonwebtoken",
@ -413,12 +346,6 @@ dependencies = [
"crypto-common",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "encoding_rs"
version = "0.8.33"
@ -813,7 +740,7 @@ dependencies = [
"http 0.2.9",
"infer",
"pin-project-lite",
"rand 0.7.3",
"rand",
"serde",
"serde_json",
"serde_qs",
@ -891,18 +818,6 @@ dependencies = [
"tokio-rustls",
]
[[package]]
name = "hyper-timeout"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
dependencies = [
"hyper 0.14.27",
"pin-project-lite",
"tokio",
"tokio-io-timeout",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
@ -1010,15 +925,6 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.9"
@ -1070,7 +976,7 @@ dependencies = [
name = "jwt-authorizer"
version = "0.13.0"
dependencies = [
"axum 0.7.1",
"axum",
"chrono",
"futures-core",
"futures-util",
@ -1081,14 +987,12 @@ dependencies = [
"jsonwebtoken",
"lazy_static",
"pin-project",
"prost",
"reqwest",
"serde",
"serde_json",
"thiserror",
"time 0.3.28",
"tokio",
"tonic",
"tower",
"tower-http",
"tower-layer",
@ -1420,29 +1324,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "prost"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa8473a65b88506c106c28ae905ca4a2b83a2993640467a41bb3080627ddfd2c"
dependencies = [
"bytes",
"prost-derive",
]
[[package]]
name = "prost-derive"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56075c27b20ae524d00f247b8a4dc333e5784f889fe63099f8e626bc8d73486c"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "quote"
version = "1.0.33"
@ -1460,22 +1341,11 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
@ -1483,17 +1353,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core 0.5.1",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
"rand_core",
]
[[package]]
@ -1505,22 +1365,13 @@ dependencies = [
"getrandom 0.1.16",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.10",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core 0.5.1",
"rand_core",
]
[[package]]
@ -2062,16 +1913,6 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "tokio-io-timeout"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
dependencies = [
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-macros"
version = "2.1.0"
@ -2103,17 +1944,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-stream"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.8"
@ -2128,33 +1958,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "tonic"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5469afaf78a11265c343a88969045c1568aa8ecc6c787dbf756e92e70f199861"
dependencies = [
"async-stream",
"async-trait",
"axum 0.6.20",
"base64 0.21.3",
"bytes",
"h2 0.3.21",
"http 0.2.9",
"http-body 0.4.5",
"hyper 0.14.27",
"hyper-timeout",
"percent-encoding",
"pin-project",
"prost",
"tokio",
"tokio-stream",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower"
version = "0.4.13"
@ -2163,11 +1966,8 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"indexmap 1.9.3",
"pin-project",
"pin-project-lite",
"rand 0.8.5",
"slab",
"tokio",
"tokio-util",
"tower-layer",

View file

@ -6,6 +6,8 @@ JWT authorizer Layer for Axum.
[![Crates.io](https://img.shields.io/crates/v/jwt-authorizer)](https://crates.io/crates/jwt-authorizer)
[![Documentation](https://docs.rs/jwt-authorizer/badge.svg)](https://docs.rs/jwt-authorizer)
> **Tonic support is temporarily removed** (waiting upgrade to hyper 1 and axum 0.7 hyperium/tonic/#1584)
## Features
- JWT token verification (Bearer)
@ -19,7 +21,7 @@ JWT authorizer Layer for Axum.
- into custom deserializable structs or into `RegisteredClaims` (default)
- Claims checker
- Tracing support (error logging)
- *tonic* support
- ~~*tonic* support~~
## Usage

View file

@ -28,14 +28,12 @@ tower-layer = "0.3"
tower-service = "0.3"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tonic = { version = "0.10", optional = true }
time = { version = "0.3", optional = true }
http-body-util = "0.1.0"
[dev-dependencies]
hyper = { version = "1.0.1", features = ["full"] }
lazy_static = "1.4.0"
prost = "0.12"
tower = { version = "0.4", features = ["util", "buffer"] }
wiremock = "0.5.19"
@ -51,7 +49,3 @@ rustls-tls-webpki-roots = ["reqwest/rustls-tls-webpki-roots"]
rustls-tls-native-roots = ["reqwest/rustls-tls-native-roots"]
time = ["dep:time"]
chrono = ["dep:chrono"]
[[test]]
name = "tonic"
required-features = ["tonic"]

View file

@ -84,55 +84,6 @@ fn response_500() -> Response<Body> {
res
}
#[cfg(feature = "tonic")]
impl From<AuthError> for Response<tonic::body::BoxBody> {
fn from(e: AuthError) -> Self {
match e {
AuthError::JwksRefreshError(err) => {
tracing::error!("AuthErrors::JwksRefreshError: {}", err);
tonic::Status::internal("")
}
AuthError::InvalidKey(err) => {
tracing::error!("AuthErrors::InvalidKey: {}", err);
tonic::Status::internal("")
}
AuthError::JwksSerialisationError(err) => {
tracing::error!("AuthErrors::JwksSerialisationError: {}", err);
tonic::Status::internal("")
}
AuthError::InvalidKeyAlg(err) => {
debug!("AuthErrors::InvalidKeyAlg: {:?}", err);
tonic::Status::unauthenticated("error=\"invalid_token\", error_description=\"invalid key algorithm\"")
}
AuthError::InvalidKid(err) => {
debug!("AuthErrors::InvalidKid: {}", err);
tonic::Status::unauthenticated("error=\"invalid_token\", error_description=\"invalid kid\"")
}
AuthError::InvalidToken(err) => {
debug!("AuthErrors::InvalidToken: {}", err);
tonic::Status::unauthenticated("error=\"invalid_token\"")
}
AuthError::MissingToken() => {
debug!("AuthErrors::MissingToken");
tonic::Status::unauthenticated("")
}
AuthError::InvalidClaims() => {
debug!("AuthErrors::InvalidClaims");
tonic::Status::unauthenticated("error=\"insufficient_scope\"")
}
AuthError::NoAuthorizer() => {
debug!("AuthErrors::NoAuthorizer");
tonic::Status::unauthenticated("error=\"invalid_token\"")
}
AuthError::NoAuthorizerLayer() => {
debug!("AuthErrors::NoAuthorizerLayer");
tonic::Status::unauthenticated("error=\"no_authorizer_layer\"")
}
}
.to_http()
}
}
impl From<AuthError> for Response {
fn from(e: AuthError) -> Self {
e.into_response()

View file

@ -1,209 +0,0 @@
use std::{sync::Once, task::Poll};
use axum::body::HttpBody;
use futures_core::future::BoxFuture;
use http::header::AUTHORIZATION;
use jwt_authorizer::{layer::AuthorizationService, IntoLayer, JwtAuthorizer, Validation};
use serde::{Deserialize, Serialize};
use tonic::{server::UnaryService, transport::NamedService, IntoRequest, Status};
use tower::{buffer::Buffer, Service};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use crate::common::{JWT_RSA1_OK, JWT_RSA2_OK};
mod common;
/// Static variable to ensure that logging is only initialized once.
pub static INITIALIZED: Once = Once::new();
#[derive(Debug, Deserialize, Serialize, Clone)]
struct User {
sub: String,
}
#[derive(prost::Message)]
struct HelloMessage {
#[prost(string, tag = "1")]
message: String,
}
#[derive(Debug, Default, Clone)]
struct SayHelloMethod {}
impl UnaryService<HelloMessage> for SayHelloMethod {
type Response = HelloMessage;
type Future = BoxFuture<'static, Result<tonic::Response<Self::Response>, Status>>;
fn call(&mut self, request: tonic::Request<HelloMessage>) -> Self::Future {
Box::pin(async move {
let hi = request.into_inner();
let reply = HelloMessage {
message: format!("Hello, {}", hi.message),
};
Ok(tonic::Response::new(reply))
})
}
}
#[derive(Debug, Default, Clone)]
struct GreeterServer {
expected_sub: String,
}
impl Service<http::Request<tonic::transport::Body>> for GreeterServer {
type Response = http::Response<tonic::body::BoxBody>;
type Error = std::convert::Infallible;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: http::Request<tonic::transport::Body>) -> Self::Future {
let token = req.extensions().get::<jsonwebtoken::TokenData<User>>().unwrap();
assert_eq!(token.claims.sub, self.expected_sub);
match req.uri().path() {
"/hello/SayHello" => Box::pin(async move {
let mut grpc = tonic::server::Grpc::new(tonic::codec::ProstCodec::default());
Ok(grpc.unary(SayHelloMethod::default(), req).await)
}),
p => {
let p = p.to_string();
Box::pin(async move { Ok(Status::unimplemented(p).to_http()) })
}
}
}
}
impl NamedService for GreeterServer {
const NAME: &'static str = "hello";
}
async fn app(
jwt_auth: JwtAuthorizer<User>,
expected_sub: String,
) -> AuthorizationService<Buffer<tonic::transport::server::Routes, http::Request<tonic::transport::Body>>, User> {
let layer = jwt_auth.build().await.unwrap().into_layer();
tonic::transport::Server::builder()
.layer(layer)
.layer(tower::buffer::BufferLayer::new(1))
.add_service(GreeterServer { expected_sub })
.into_service()
}
fn init_test() {
INITIALIZED.call_once(|| {
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(
std::env::var("RUST_LOG").unwrap_or_else(|_| "info,jwt-authorizer=debug,tower_http=debug".into()),
))
.with(tracing_subscriber::fmt::layer())
.init();
});
}
// The grpc client produces a http request with a tonic boxbody that the transport is meant to sent out, while the server side
// expects to receive a http request with a hyper body.. This simple wrapper converts from one to
// the other.
struct GrpcWrapper<S>
where
S: Service<http::Request<axum::body::Body>> + Clone,
{
inner: S,
}
impl<S> Service<http::Request<tonic::body::BoxBody>> for GrpcWrapper<S>
where
S: Service<http::Request<axum::body::Body>> + Clone + Send + 'static,
S::Future: Send,
{
type Response = S::Response;
type Error = S::Error;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: http::Request<tonic::body::BoxBody>) -> Self::Future {
let inner = self.inner.clone();
// take the service that was ready
let mut inner = std::mem::replace(&mut self.inner, inner);
Box::pin(async move {
let (parts, mut body) = req.into_parts();
let mut data = Vec::new();
while let Some(d) = body.data().await {
let d = d.unwrap();
data.extend_from_slice(&d)
}
inner
.call(http::Request::from_parts(parts, axum::body::Body::from(data)))
.await
})
}
}
async fn make_protected_request<S: Clone>(
app: AuthorizationService<S, User>,
bearer: Option<&str>,
message: &str,
) -> Result<tonic::Response<HelloMessage>, Status>
where
S: Service<
http::Request<tonic::transport::Body>,
Response = http::Response<tonic::body::BoxBody>,
Error = tower::BoxError,
> + Send
+ 'static,
S::Future: Send,
{
let mut grpc = tonic::client::Grpc::new(GrpcWrapper { inner: app });
let mut request = HelloMessage {
message: message.to_string(),
}
.into_request();
if let Some(bearer) = bearer {
let headers = request.metadata_mut();
headers.insert(AUTHORIZATION.as_str(), format!("Bearer {bearer}").parse().unwrap());
}
grpc.ready().await.unwrap();
grpc.unary(
request,
http::uri::PathAndQuery::from_static("/hello/SayHello"),
tonic::codec::ProstCodec::default(),
)
.await
}
#[tokio::test]
async fn successfull_auth() {
init_test();
let auth: JwtAuthorizer<User> =
JwtAuthorizer::from_rsa_pem("../config/rsa-public1.pem").validation(Validation::new().aud(&["aud1"]));
let app = app(auth, "b@b.com".to_string()).await;
let r = make_protected_request(app.clone(), Some(JWT_RSA1_OK), "world").await.unwrap();
assert_eq!(r.get_ref().message, "Hello, world");
}
#[tokio::test]
async fn wrong_token() {
init_test();
let auth: JwtAuthorizer<User> = JwtAuthorizer::from_rsa_pem("../config/rsa-public1.pem");
let app = app(auth, "b@b.com".to_string()).await;
let status = make_protected_request(app.clone(), Some(JWT_RSA2_OK), "world")
.await
.unwrap_err();
assert_eq!(status.code(), tonic::Code::Unauthenticated);
}
#[tokio::test]
async fn no_token() {
init_test();
let auth: JwtAuthorizer<User> = JwtAuthorizer::from_rsa_pem("../config/rsa-public1.pem");
let app = app(auth, "b@b.com".to_string()).await;
let status = make_protected_request(app.clone(), None, "world").await.unwrap_err();
assert_eq!(status.code(), tonic::Code::Unauthenticated);
}