Skip to content

Commit

Permalink
Merge pull request #232 from threema-donat/feature-improvements
Browse files Browse the repository at this point in the history
Make crypto provider configurable, rename feature and make hyper-tls feature visible
  • Loading branch information
dermesser authored Jun 27, 2024
2 parents 0701d9d + 310ea83 commit 5806f96
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 48 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ jobs:
strategy:
fail-fast: false
matrix:
features: ["service_account,hyper-rustls", "service_account,hyper-tls", "service_account", "hyper-rustls"]
features: ["service_account,hyper-rustls,ring",
"service_account,hyper-rustls,aws-lc-rs",
"service_account,hyper-tls,ring", "service_account,hyper-tls,aws-lc-rs",
"hyper-rustls,ring"]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
Expand Down
24 changes: 15 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,30 @@ edition = "2018"

[[example]]
name = "custom_flow"
required-features = ["hyper-rustls"]
required-features = ["hyper-rustls", "ring"]

[[example]]
name = "custom_client"
required-features = ["hyper-rustls", "service_account"]
required-features = ["hyper-rustls", "service-account", "ring"]

[[example]]
name = "custom_storage"
required-features = ["hyper-rustls"]
required-features = ["hyper-rustls", "ring"]

[[test]]
name = "tests"
required-features = ["hyper-rustls", "service_account"]
required-features = ["hyper-rustls", "service-account"]

[features]
default = ["hyper-rustls", "rustls", "service_account"]
service_account = ["hyper-rustls", "rustls", "rustls-pemfile"]
hyper-rustls = ["dep:hyper-rustls", "rustls"]
default = ["hyper-rustls", "service-account", "ring"]
service_account = ["service-account"]
service-account = ["rustls-pemfile"]
hyper-rustls = ["dep:hyper-rustls", "__rustls"]
ring = ["rustls/ring", "hyper-rustls?/ring"]
aws-lc-rs = ["rustls/aws_lc_rs", "hyper-rustls?/aws-lc-rs"]
hyper-tls = ["dep:hyper-tls", "__rustls"]
# hidden features
__rustls = ["dep:rustls"]

[dependencies]
anyhow = "1.0.38"
Expand All @@ -40,11 +46,11 @@ http = "1"
http-body-util = "0.1"
hyper = "1"
hyper-util = { version = "0.1.5", features = ["client-legacy", "server-auto", "http1", "http2", "server-graceful"] }
hyper-rustls = { version = "0.27", optional = true, default-features = false, features = ["http1", "http2", "rustls-native-certs", "ring", "native-tokio"] }
hyper-rustls = { version = "0.27", optional = true, default-features = false, features = ["http1", "http2", "rustls-native-certs", "native-tokio"] }
hyper-tls = { version = "0.6.0", optional = true }
log = "0.4"
percent-encoding = "2"
rustls = { version = "^0.23", optional = true, default-features = false, features = ["ring", "std"] }
rustls = { version = "^0.23", optional = true, default-features = false, features = ["std"] }
rustls-pemfile = { version = "2.0.0", optional = true }
seahash = "4"
serde = { version = "1.0", features = ["derive"] }
Expand Down
34 changes: 19 additions & 15 deletions src/authenticator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ use crate::installed::{InstalledFlow, InstalledFlowReturnMethod};
use crate::refresh::RefreshFlow;
use crate::service_account_impersonator::ServiceAccountImpersonationFlow;

#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
use crate::service_account::{self, ServiceAccountFlow, ServiceAccountFlowOpts, ServiceAccountKey};
use crate::storage::{self, Storage, TokenStorage};
use crate::types::{AccessToken, ApplicationSecret, TokenInfo};
use private::AuthFlow;
#[cfg(all(feature = "aws-lc-rs", feature = "hyper-rustls", not(feature = "ring")))]
use rustls::crypto::aws_lc_rs::default_provider as default_crypto_provider;
#[cfg(all(feature = "ring", feature = "hyper-rustls"))]
use rustls::crypto::ring::default_provider as default_crypto_provider;

use crate::access_token::AccessTokenFlow;

Expand Down Expand Up @@ -259,10 +263,10 @@ impl DeviceFlowAuthenticator {
/// .expect("failed to create authenticator");
/// # }
/// ```
#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
pub struct ServiceAccountAuthenticator;

#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
impl ServiceAccountAuthenticator {
/// Use the builder pattern to create an Authenticator that uses a service account.
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
Expand Down Expand Up @@ -293,7 +297,7 @@ impl ServiceAccountAuthenticator {

/// Create an authenticator that uses a application default credentials.
/// ```
/// # #[cfg(all(any(feature = "hyper-rustls", feature = "hyper-tls"), feature = "service_account"))]
/// # #[cfg(all(any(feature = "hyper-rustls", feature = "hyper-tls"), feature = "service-account"))]
/// # async fn foo() {
/// # use yup_oauth2::ApplicationDefaultCredentialsAuthenticator;
/// # use yup_oauth2::ApplicationDefaultCredentialsFlowOpts;
Expand All @@ -315,7 +319,7 @@ impl ServiceAccountAuthenticator {
pub struct ApplicationDefaultCredentialsAuthenticator;
impl ApplicationDefaultCredentialsAuthenticator {
/// Try to build ServiceAccountFlowOpts from the environment
#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
pub async fn from_environment() -> Result<ServiceAccountFlowOpts, std::env::VarError> {
let key_path = std::env::var("GOOGLE_APPLICATION_CREDENTIALS")?;

Expand All @@ -327,7 +331,7 @@ impl ApplicationDefaultCredentialsAuthenticator {

/// Use the builder pattern to deduce which model of authenticator should be used:
/// Service account one or GCE instance metadata kind
#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
#[cfg_attr(
yup_oauth2_docsrs,
Expand All @@ -340,7 +344,7 @@ impl ApplicationDefaultCredentialsAuthenticator {
}

/// Use the builder pattern to deduce which model of authenticator should be used and allow providing a hyper client
#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
pub async fn with_client<C>(
opts: ApplicationDefaultCredentialsFlowOpts,
client: C,
Expand All @@ -366,7 +370,7 @@ where
C: HyperClientBuilder,
{
/// Service account based authenticator signature
#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
ServiceAccount(AuthenticatorBuilder<C, ServiceAccountFlowOpts>),
/// GCE Instance Metadata based authenticator signature
InstanceMetadata(AuthenticatorBuilder<C, ApplicationDefaultCredentialsFlowOpts>),
Expand Down Expand Up @@ -745,7 +749,7 @@ impl<C> AuthenticatorBuilder<C, InstalledFlow> {
/// .expect("failed to create authenticator");
/// # }
/// ```
#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
impl<C> AuthenticatorBuilder<C, ServiceAccountFlowOpts> {
/// Use the provided subject.
pub fn subject(self, subject: impl Into<String>) -> Self {
Expand Down Expand Up @@ -871,7 +875,7 @@ mod private {
use crate::error::Error;
use crate::external_account::ExternalAccountFlow;
use crate::installed::InstalledFlow;
#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
use crate::service_account::ServiceAccountFlow;
use crate::service_account_impersonator::ServiceAccountImpersonationFlow;
use crate::types::{ApplicationSecret, TokenInfo};
Expand All @@ -880,7 +884,7 @@ mod private {
pub enum AuthFlow {
DeviceFlow(DeviceFlow),
InstalledFlow(InstalledFlow),
#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
ServiceAccountFlow(ServiceAccountFlow),
ServiceAccountImpersonationFlow(ServiceAccountImpersonationFlow),
ApplicationDefaultCredentialsFlow(ApplicationDefaultCredentialsFlow),
Expand All @@ -894,7 +898,7 @@ mod private {
match self {
AuthFlow::DeviceFlow(device_flow) => Some(&device_flow.app_secret),
AuthFlow::InstalledFlow(installed_flow) => Some(&installed_flow.app_secret),
#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
AuthFlow::ServiceAccountFlow(_) => None,
AuthFlow::ServiceAccountImpersonationFlow(_) => None,
AuthFlow::ApplicationDefaultCredentialsFlow(_) => None,
Expand All @@ -918,7 +922,7 @@ mod private {
AuthFlow::InstalledFlow(installed_flow) => {
installed_flow.token(hyper_client, scopes).await
}
#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
AuthFlow::ServiceAccountFlow(service_account_flow) => {
service_account_flow.token(hyper_client, scopes).await
}
Expand Down Expand Up @@ -1003,7 +1007,7 @@ impl HyperClientBuilder for DefaultHyperClient {
) -> Result<hyper_util::client::legacy::Client<Self::Connector, String>, Error> {
#[cfg(feature = "hyper-rustls")]
let connector = hyper_rustls::HttpsConnectorBuilder::new()
.with_provider_and_native_roots(rustls::crypto::ring::default_provider())?
.with_provider_and_native_roots(default_crypto_provider())?
.https_or_http()
.enable_http1()
.enable_http2()
Expand All @@ -1023,7 +1027,7 @@ impl HyperClientBuilder for DefaultHyperClient {
) -> hyper_util::client::legacy::Client<Self::Connector, String> {
#[cfg(feature = "hyper-rustls")]
let connector = hyper_rustls::HttpsConnectorBuilder::new()
.with_provider_and_native_roots(rustls::crypto::ring::default_provider())
.with_provider_and_native_roots(default_crypto_provider())
.unwrap()
.https_or_http()
.enable_http1()
Expand Down
6 changes: 3 additions & 3 deletions src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::authorized_user::AuthorizedUserSecret;
use crate::external_account::ExternalAccountSecret;
use crate::types::{ApplicationSecret, ConsoleApplicationSecret};

#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
use crate::service_account::ServiceAccountKey;

use std::io;
Expand Down Expand Up @@ -43,13 +43,13 @@ pub fn parse_application_secret<S: AsRef<[u8]>>(secret: S) -> io::Result<Applica

/// Read a service account key from a JSON file. You can download the JSON keys from the Google
/// Cloud Console or the respective console of your service provider.
#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
pub async fn read_service_account_key<P: AsRef<Path>>(path: P) -> io::Result<ServiceAccountKey> {
let key = tokio::fs::read(path).await?;
parse_service_account_key(key)
}

#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
/// Read a service account key from a JSON string.
pub fn parse_service_account_key<S: AsRef<[u8]>>(key: S) -> io::Result<ServiceAccountKey> {
serde_json::from_slice(key.as_ref()).map_err(|e| {
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ mod installed;
mod refresh;
pub mod service_account_impersonator;

#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
mod service_account;

/// Interface for storing tokens so that they can be re-used. There are built-in memory and
Expand All @@ -99,7 +99,7 @@ pub use hyper;
#[cfg(feature = "hyper-rustls")]
pub use hyper_rustls;

#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
#[doc(inline)]
pub use crate::authenticator::ServiceAccountAuthenticator;

Expand All @@ -117,7 +117,7 @@ pub use crate::helper::*;
pub use crate::installed::InstalledFlowReturnMethod;

pub use crate::application_default_credentials::ApplicationDefaultCredentialsFlowOpts;
#[cfg(feature = "service_account")]
#[cfg(feature = "service-account")]
pub use crate::service_account::ServiceAccountKey;

#[doc(inline)]
Expand Down
13 changes: 9 additions & 4 deletions src/service_account.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![cfg(feature = "service_account")]
#![cfg(feature = "service-account")]

//! This module provides a flow that obtains tokens for service accounts.
//!
Expand All @@ -23,7 +23,11 @@ use base64::Engine as _;
use http::header;
use http_body_util::BodyExt;
use hyper_util::client::legacy::connect::Connect;
use rustls::{self, crypto::ring::sign, pki_types::PrivateKeyDer, sign::SigningKey};
#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
use rustls::crypto::aws_lc_rs as crypto_provider;
#[cfg(feature = "ring")]
use rustls::crypto::ring as crypto_provider;
use rustls::{self, pki_types::PrivateKeyDer, sign::SigningKey};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use url::form_urlencoded;
Expand Down Expand Up @@ -125,7 +129,7 @@ pub(crate) struct JWTSigner {
impl JWTSigner {
fn new(private_key: &str) -> Result<Self, io::Error> {
let key = decode_rsa_key(private_key)?;
let signing_key = sign::RsaSigningKey::new(&key)
let signing_key = crypto_provider::sign::RsaSigningKey::new(&key)
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Couldn't initialize signer"))?;
let signer = signing_key
.choose_scheme(&[rustls::SignatureScheme::RSA_PKCS1_SHA256])
Expand Down Expand Up @@ -230,6 +234,7 @@ mod tests {
const TEST_PRIVATE_KEY_PATH: &str = "examples/Sanguine-69411a0c0eea.json";

// Uncomment this test to verify that we can successfully obtain tokens.
#[cfg(feature = "hyper-rustls")]
// #[tokio::test]
#[allow(dead_code)]
async fn test_service_account_e2e() {
Expand All @@ -243,7 +248,7 @@ mod tests {
hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new())
.build(
hyper_rustls::HttpsConnectorBuilder::new()
.with_provider_and_native_roots(rustls::crypto::ring::default_provider())
.with_provider_and_native_roots(crypto_provider::default_provider())
.unwrap()
.https_only()
.enable_http1()
Expand Down
8 changes: 7 additions & 1 deletion src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,13 @@ mod tests {
id_token: None,
};
let scope_set = ScopeSet::from(&["myscope"]);
let tempdir = tempfile::tempdir().unwrap();

let tempdir = tempfile::Builder::new()
.prefix("yup-oauth2-tests_")
.rand_bytes(15)
.tempdir()
.unwrap();

{
let storage = DiskStorage::new(tempdir.path().join("tokenstorage.json"))
.await
Expand Down
Loading

0 comments on commit 5806f96

Please sign in to comment.