From 4a3077fa3a2cc5f5cf7c32835680b652f512a81d Mon Sep 17 00:00:00 2001 From: cduvray Date: Thu, 7 Sep 2023 07:23:35 +0200 Subject: [PATCH] refactor: extract JwtAuthorizer in builder.rs - JwtAuthorizer is in fact a builder, it may be renamed in future --- jwt-authorizer/src/builder.rs | 190 ++++++++++++++++++++++++++++++++++ jwt-authorizer/src/layer.rs | 188 +-------------------------------- jwt-authorizer/src/lib.rs | 3 +- 3 files changed, 194 insertions(+), 187 deletions(-) create mode 100644 jwt-authorizer/src/builder.rs diff --git a/jwt-authorizer/src/builder.rs b/jwt-authorizer/src/builder.rs new file mode 100644 index 0000000..a67ab3e --- /dev/null +++ b/jwt-authorizer/src/builder.rs @@ -0,0 +1,190 @@ +use std::sync::Arc; + +use serde::de::DeserializeOwned; + +use crate::{ + authorizer::{FnClaimsChecker, KeySourceType}, + error::InitError, + layer::{AuthorizationLayer, JwtSource}, + Authorizer, Refresh, RefreshStrategy, RegisteredClaims, Validation, +}; + +/// Authorizer Layer builder +/// +/// - initialisation of the Authorizer from jwks, rsa, ed, ec or secret +/// - can define a checker (jwt claims check) +pub struct JwtAuthorizer +where + C: Clone + DeserializeOwned, +{ + key_source_type: KeySourceType, + refresh: Option, + claims_checker: Option>, + validation: Option, + jwt_source: JwtSource, +} + +/// authorization layer builder +impl JwtAuthorizer +where + C: Clone + DeserializeOwned + Send + Sync, +{ + /// Builds Authorizer Layer from a OpenId Connect discover metadata + pub fn from_oidc(issuer: &str) -> JwtAuthorizer { + JwtAuthorizer { + key_source_type: KeySourceType::Discovery(issuer.to_string()), + refresh: Default::default(), + claims_checker: None, + validation: None, + jwt_source: JwtSource::AuthorizationHeader, + } + } + + /// Builds Authorizer Layer from a JWKS endpoint + pub fn from_jwks_url(url: &str) -> JwtAuthorizer { + JwtAuthorizer { + key_source_type: KeySourceType::Jwks(url.to_owned()), + refresh: Default::default(), + claims_checker: None, + validation: None, + jwt_source: JwtSource::AuthorizationHeader, + } + } + + /// Builds Authorizer Layer from a RSA PEM file + pub fn from_rsa_pem(path: &str) -> JwtAuthorizer { + JwtAuthorizer { + key_source_type: KeySourceType::RSA(path.to_owned()), + refresh: Default::default(), + claims_checker: None, + validation: None, + jwt_source: JwtSource::AuthorizationHeader, + } + } + + /// Builds Authorizer Layer from an RSA PEM raw text + pub fn from_rsa_pem_text(text: &str) -> JwtAuthorizer { + JwtAuthorizer { + key_source_type: KeySourceType::RSAString(text.to_owned()), + refresh: Default::default(), + claims_checker: None, + validation: None, + jwt_source: JwtSource::AuthorizationHeader, + } + } + + /// Builds Authorizer Layer from a EC PEM file + pub fn from_ec_pem(path: &str) -> JwtAuthorizer { + JwtAuthorizer { + key_source_type: KeySourceType::EC(path.to_owned()), + refresh: Default::default(), + claims_checker: None, + validation: None, + jwt_source: JwtSource::AuthorizationHeader, + } + } + + /// Builds Authorizer Layer from a EC PEM raw text + pub fn from_ec_pem_text(text: &str) -> JwtAuthorizer { + JwtAuthorizer { + key_source_type: KeySourceType::ECString(text.to_owned()), + refresh: Default::default(), + claims_checker: None, + validation: None, + jwt_source: JwtSource::AuthorizationHeader, + } + } + + /// Builds Authorizer Layer from a EC PEM file + pub fn from_ed_pem(path: &str) -> JwtAuthorizer { + JwtAuthorizer { + key_source_type: KeySourceType::ED(path.to_owned()), + refresh: Default::default(), + claims_checker: None, + validation: None, + jwt_source: JwtSource::AuthorizationHeader, + } + } + + /// Builds Authorizer Layer from a EC PEM raw text + pub fn from_ed_pem_text(text: &str) -> JwtAuthorizer { + JwtAuthorizer { + key_source_type: KeySourceType::EDString(text.to_owned()), + refresh: Default::default(), + claims_checker: None, + validation: None, + jwt_source: JwtSource::AuthorizationHeader, + } + } + + /// Builds Authorizer Layer from a secret phrase + pub fn from_secret(secret: &str) -> JwtAuthorizer { + JwtAuthorizer { + key_source_type: KeySourceType::Secret(secret.to_owned()), + refresh: Default::default(), + claims_checker: None, + validation: None, + jwt_source: JwtSource::AuthorizationHeader, + } + } + + /// Refreshes configuration for jwk store + pub fn refresh(mut self, refresh: Refresh) -> JwtAuthorizer { + if self.refresh.is_some() { + tracing::warn!("More than one refresh configuration found!"); + } + self.refresh = Some(refresh); + self + } + + /// no refresh, jwks will be loaded juste once + pub fn no_refresh(mut self) -> JwtAuthorizer { + if self.refresh.is_some() { + tracing::warn!("More than one refresh configuration found!"); + } + self.refresh = Some(Refresh { + strategy: RefreshStrategy::NoRefresh, + ..Default::default() + }); + self + } + + /// configures token content check (custom function), if false a 403 will be sent. + /// (AuthError::InvalidClaims()) + pub fn check(mut self, checker_fn: fn(&C) -> bool) -> JwtAuthorizer { + self.claims_checker = Some(FnClaimsChecker { checker_fn }); + + self + } + + pub fn validation(mut self, validation: Validation) -> JwtAuthorizer { + self.validation = Some(validation); + + self + } + + /// configures the source of the bearer token + /// + /// (default: AuthorizationHeader) + pub fn jwt_source(mut self, src: JwtSource) -> JwtAuthorizer { + self.jwt_source = src; + + self + } + + /// Build axum layer + #[deprecated(since = "0.10.0", note = "please use `IntoLayer::into_layer()` instead")] + pub async fn layer(self) -> Result, InitError> { + let val = self.validation.unwrap_or_default(); + let auth = Arc::new( + Authorizer::build(self.key_source_type, self.claims_checker, self.refresh, val, self.jwt_source).await?, + ); + Ok(AuthorizationLayer::new(vec![auth])) + } + + pub async fn build(self) -> Result, InitError> { + let val = self.validation.unwrap_or_default(); + + Authorizer::build(self.key_source_type, self.claims_checker, self.refresh, val, self.jwt_source).await + } +} diff --git a/jwt-authorizer/src/layer.rs b/jwt-authorizer/src/layer.rs index 1ea2864..83297a3 100644 --- a/jwt-authorizer/src/layer.rs +++ b/jwt-authorizer/src/layer.rs @@ -11,192 +11,8 @@ use std::task::{Context, Poll}; use tower_layer::Layer; use tower_service::Service; -use crate::authorizer::{Authorizer, FnClaimsChecker, KeySourceType}; -use crate::claims::RegisteredClaims; -use crate::error::InitError; -use crate::jwks::key_store_manager::Refresh; -use crate::validation::Validation; -use crate::{AuthError, RefreshStrategy}; - -/// Authorizer Layer builder -/// -/// - initialisation of the Authorizer from jwks, rsa, ed, ec or secret -/// - can define a checker (jwt claims check) -pub struct JwtAuthorizer -where - C: Clone + DeserializeOwned, -{ - key_source_type: KeySourceType, - refresh: Option, - claims_checker: Option>, - validation: Option, - jwt_source: JwtSource, -} - -/// authorization layer builder -impl JwtAuthorizer -where - C: Clone + DeserializeOwned + Send + Sync, -{ - /// Builds Authorizer Layer from a OpenId Connect discover metadata - pub fn from_oidc(issuer: &str) -> JwtAuthorizer { - JwtAuthorizer { - key_source_type: KeySourceType::Discovery(issuer.to_string()), - refresh: Default::default(), - claims_checker: None, - validation: None, - jwt_source: JwtSource::AuthorizationHeader, - } - } - - /// Builds Authorizer Layer from a JWKS endpoint - pub fn from_jwks_url(url: &str) -> JwtAuthorizer { - JwtAuthorizer { - key_source_type: KeySourceType::Jwks(url.to_owned()), - refresh: Default::default(), - claims_checker: None, - validation: None, - jwt_source: JwtSource::AuthorizationHeader, - } - } - - /// Builds Authorizer Layer from a RSA PEM file - pub fn from_rsa_pem(path: &str) -> JwtAuthorizer { - JwtAuthorizer { - key_source_type: KeySourceType::RSA(path.to_owned()), - refresh: Default::default(), - claims_checker: None, - validation: None, - jwt_source: JwtSource::AuthorizationHeader, - } - } - - /// Builds Authorizer Layer from an RSA PEM raw text - pub fn from_rsa_pem_text(text: &str) -> JwtAuthorizer { - JwtAuthorizer { - key_source_type: KeySourceType::RSAString(text.to_owned()), - refresh: Default::default(), - claims_checker: None, - validation: None, - jwt_source: JwtSource::AuthorizationHeader, - } - } - - /// Builds Authorizer Layer from a EC PEM file - pub fn from_ec_pem(path: &str) -> JwtAuthorizer { - JwtAuthorizer { - key_source_type: KeySourceType::EC(path.to_owned()), - refresh: Default::default(), - claims_checker: None, - validation: None, - jwt_source: JwtSource::AuthorizationHeader, - } - } - - /// Builds Authorizer Layer from a EC PEM raw text - pub fn from_ec_pem_text(text: &str) -> JwtAuthorizer { - JwtAuthorizer { - key_source_type: KeySourceType::ECString(text.to_owned()), - refresh: Default::default(), - claims_checker: None, - validation: None, - jwt_source: JwtSource::AuthorizationHeader, - } - } - - /// Builds Authorizer Layer from a EC PEM file - pub fn from_ed_pem(path: &str) -> JwtAuthorizer { - JwtAuthorizer { - key_source_type: KeySourceType::ED(path.to_owned()), - refresh: Default::default(), - claims_checker: None, - validation: None, - jwt_source: JwtSource::AuthorizationHeader, - } - } - - /// Builds Authorizer Layer from a EC PEM raw text - pub fn from_ed_pem_text(text: &str) -> JwtAuthorizer { - JwtAuthorizer { - key_source_type: KeySourceType::EDString(text.to_owned()), - refresh: Default::default(), - claims_checker: None, - validation: None, - jwt_source: JwtSource::AuthorizationHeader, - } - } - - /// Builds Authorizer Layer from a secret phrase - pub fn from_secret(secret: &str) -> JwtAuthorizer { - JwtAuthorizer { - key_source_type: KeySourceType::Secret(secret.to_owned()), - refresh: Default::default(), - claims_checker: None, - validation: None, - jwt_source: JwtSource::AuthorizationHeader, - } - } - - /// Refreshes configuration for jwk store - pub fn refresh(mut self, refresh: Refresh) -> JwtAuthorizer { - if self.refresh.is_some() { - tracing::warn!("More than one refresh configuration found!"); - } - self.refresh = Some(refresh); - self - } - - /// no refresh, jwks will be loaded juste once - pub fn no_refresh(mut self) -> JwtAuthorizer { - if self.refresh.is_some() { - tracing::warn!("More than one refresh configuration found!"); - } - self.refresh = Some(Refresh { - strategy: RefreshStrategy::NoRefresh, - ..Default::default() - }); - self - } - - /// configures token content check (custom function), if false a 403 will be sent. - /// (AuthError::InvalidClaims()) - pub fn check(mut self, checker_fn: fn(&C) -> bool) -> JwtAuthorizer { - self.claims_checker = Some(FnClaimsChecker { checker_fn }); - - self - } - - pub fn validation(mut self, validation: Validation) -> JwtAuthorizer { - self.validation = Some(validation); - - self - } - - /// configures the source of the bearer token - /// - /// (default: AuthorizationHeader) - pub fn jwt_source(mut self, src: JwtSource) -> JwtAuthorizer { - self.jwt_source = src; - - self - } - - /// Build axum layer - #[deprecated(since = "0.10.0", note = "please use `IntoLayer::into_layer()` instead")] - pub async fn layer(self) -> Result, InitError> { - let val = self.validation.unwrap_or_default(); - let auth = Arc::new( - Authorizer::build(self.key_source_type, self.claims_checker, self.refresh, val, self.jwt_source).await?, - ); - Ok(AuthorizationLayer::new(vec![auth])) - } - - pub async fn build(self) -> Result, InitError> { - let val = self.validation.unwrap_or_default(); - - Authorizer::build(self.key_source_type, self.claims_checker, self.refresh, val, self.jwt_source).await - } -} +use crate::authorizer::Authorizer; +use crate::AuthError; /// Trait for authorizing requests. pub trait Authorize { diff --git a/jwt-authorizer/src/lib.rs b/jwt-authorizer/src/lib.rs index 1940777..b5cd5ca 100644 --- a/jwt-authorizer/src/lib.rs +++ b/jwt-authorizer/src/lib.rs @@ -6,12 +6,13 @@ use serde::de::DeserializeOwned; pub use self::error::AuthError; pub use authorizer::{Authorizer, IntoLayer}; +pub use builder::JwtAuthorizer; pub use claims::{NumericDate, OneOrArray, RegisteredClaims}; pub use jwks::key_store_manager::{Refresh, RefreshStrategy}; -pub use layer::JwtAuthorizer; pub use validation::Validation; pub mod authorizer; +pub mod builder; pub mod claims; pub mod error; pub mod jwks;