Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: cargo
directory: "/"
schedule:
interval: weekly
time: "13:00"
open-pull-requests-limit: 99
58 changes: 58 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: CI

on:
push:
branches: ['main']
pull_request:
schedule:
- cron: "25 6 * * 5"

jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
rust: [stable, beta]
exclude:
- os: macos-latest
rust: beta

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- uses: actions-rs/cargo@v1
with:
command: check
args: --all-features --all-targets

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- uses: actions-rs/cargo@v1
if: always()
with:
command: clippy
args: --workspace --all-targets --all-features -- -D warnings

audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: EmbarkStudios/cargo-deny-action@v1
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ url = "2"
async-trait = "0.1"
thiserror = "1.0"
dirs-next = "2.0"

[dev-dependencies]
env_logger = "0.9"
tokio = { version = "1.1", features = ["macros", "rt-multi-thread"] }
9 changes: 9 additions & 0 deletions deny.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[licenses]
allow-osi-fsf-free = "either"
copyleft = "deny"
allow = ["MPL-2.0"]

[[licenses.clarify]]
name = "ring"
expression = "ISC AND MIT AND OpenSSL"
license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }]
17 changes: 0 additions & 17 deletions examples/Cargo.toml

This file was deleted.

4 changes: 3 additions & 1 deletion examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
let authentication_manager = gcp_auth::init().await?;
let _token = authentication_manager.get_token(&["https://www.googleapis.com/auth/cloud-platform"]).await?;
let _token = authentication_manager
.get_token(&["https://www.googleapis.com/auth/cloud-platform"])
.await?;
Ok(())
}
7 changes: 5 additions & 2 deletions src/authentication_manager.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use crate::prelude::*;
use async_trait::async_trait;

use crate::error::Error;
use crate::types::{HyperClient, Token};

#[async_trait]
pub trait ServiceAccount: Send + Sync {
pub(crate) trait ServiceAccount: Send + Sync {
async fn project_id(&self, client: &HyperClient) -> Result<String, Error>;
fn get_token(&self, scopes: &[&str]) -> Option<Token>;
async fn refresh_token(&self, client: &HyperClient, scopes: &[&str]) -> Result<Token, Error>;
Expand Down
57 changes: 28 additions & 29 deletions src/custom_service_account.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
use crate::authentication_manager::ServiceAccount;
use crate::error::Error::AplicationProfileMissing;
use crate::prelude::*;
use std::collections::HashMap;
use std::path::Path;
use std::sync::RwLock;

use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use tokio::fs;

use crate::authentication_manager::ServiceAccount;
use crate::error::Error;
use crate::types::{HyperClient, Token};
use crate::util::HyperExt;

#[derive(Debug)]
pub struct CustomServiceAccount {
pub(crate) struct CustomServiceAccount {
tokens: RwLock<HashMap<Vec<String>, Token>>,
credentials: ApplicationCredentials,
}

impl CustomServiceAccount {
pub async fn from_file(path: &str) -> Result<Self, Error> {
let credentials = ApplicationCredentials::from_file(path).await?;
pub(crate) async fn from_file(path: &Path) -> Result<Self, Error> {
Ok(Self {
credentials,
credentials: ApplicationCredentials::from_file(path).await?,
tokens: RwLock::new(HashMap::new()),
})
}

pub async fn from_env() -> Result<Self, Error> {
const GOOGLE_APPLICATION_CREDENTIALS: &str = "GOOGLE_APPLICATION_CREDENTIALS";
let path =
std::env::var(GOOGLE_APPLICATION_CREDENTIALS).map_err(|_| AplicationProfileMissing)?;
CustomServiceAccount::from_file(&path).await
}
}

#[async_trait]
Expand All @@ -43,12 +42,12 @@ impl ServiceAccount for CustomServiceAccount {

async fn refresh_token(&self, client: &HyperClient, scopes: &[&str]) -> Result<Token, Error> {
use crate::jwt::Claims;
use crate::jwt::JWTSigner;
use crate::jwt::JwtSigner;
use crate::jwt::GRANT_TYPE;
use hyper::header;
use url::form_urlencoded;

let signer = JWTSigner::new(&self.credentials.private_key)?;
let signer = JwtSigner::new(&self.credentials.private_key)?;

let claims = Claims::new(&self.credentials, scopes, None);
let signed = signer.sign_claims(&claims).map_err(Error::TLSError)?;
Expand All @@ -73,33 +72,33 @@ impl ServiceAccount for CustomServiceAccount {
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ApplicationCredentials {
pub r#type: Option<String>,
pub(crate) struct ApplicationCredentials {
pub(crate) r#type: Option<String>,
/// project_id
pub project_id: Option<String>,
pub(crate) project_id: Option<String>,
/// private_key_id
pub private_key_id: Option<String>,
pub(crate) private_key_id: Option<String>,
/// private_key
pub private_key: String,
pub(crate) private_key: String,
/// client_email
pub client_email: String,
pub(crate) client_email: String,
/// client_id
pub client_id: Option<String>,
pub(crate) client_id: Option<String>,
/// auth_uri
pub auth_uri: Option<String>,
pub(crate) auth_uri: Option<String>,
/// token_uri
pub token_uri: String,
pub(crate) token_uri: String,
/// auth_provider_x509_cert_url
pub auth_provider_x509_cert_url: Option<String>,
pub(crate) auth_provider_x509_cert_url: Option<String>,
/// client_x509_cert_url
pub client_x509_cert_url: Option<String>,
pub(crate) client_x509_cert_url: Option<String>,
}

impl ApplicationCredentials {
async fn from_file<T: AsRef<Path>>(path: T) -> Result<ApplicationCredentials, Error> {
let content = fs::read_to_string(path)
.await
.map_err(Error::AplicationProfilePath)?;
Ok(serde_json::from_str(&content).map_err(Error::AplicationProfileFormat)?)
.map_err(Error::ApplicationProfilePath)?;
Ok(serde_json::from_str(&content).map_err(Error::ApplicationProfileFormat)?)
}
}
35 changes: 20 additions & 15 deletions src/default_authorized_user.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
use crate::authentication_manager::ServiceAccount;
use crate::prelude::*;
use hyper::body::Body;
use hyper::Method;
use std::path::Path;
use std::sync::RwLock;

use async_trait::async_trait;
use hyper::body::Body;
use hyper::{Method, Request};
use serde::{Deserialize, Serialize};
use tokio::fs;

use crate::authentication_manager::ServiceAccount;
use crate::error::Error;
use crate::types::{HyperClient, Token};
use crate::util::HyperExt;

#[derive(Debug)]
pub struct DefaultAuthorizedUser {
pub(crate) struct DefaultAuthorizedUser {
token: RwLock<Token>,
}

Expand All @@ -15,7 +22,7 @@ impl DefaultAuthorizedUser {
const USER_CREDENTIALS_PATH: &'static str =
".config/gcloud/application_default_credentials.json";

pub async fn new(client: &HyperClient) -> Result<Self, Error> {
pub(crate) async fn new(client: &HyperClient) -> Result<Self, Error> {
let token = RwLock::new(Self::get_token(client).await?);
Ok(Self { token })
}
Expand All @@ -33,10 +40,8 @@ impl DefaultAuthorizedUser {
log::debug!("Loading user credentials file");
let mut home = dirs_next::home_dir().ok_or(Error::NoHomeDir)?;
home.push(Self::USER_CREDENTIALS_PATH);
let cred =
UserCredentials::from_file(home.display().to_string())
.await?;
let req = Self::build_token_request(&RerfeshRequest {
let cred = UserCredentials::from_file(home.display().to_string()).await?;
let req = Self::build_token_request(&RefreshRequest {
client_id: cred.client_id,
client_secret: cred.client_secret,
grant_type: "refresh_token".to_string(),
Expand Down Expand Up @@ -70,7 +75,7 @@ impl ServiceAccount for DefaultAuthorizedUser {
}

#[derive(Serialize, Debug)]
struct RerfeshRequest {
struct RefreshRequest {
client_id: String,
client_secret: String,
grant_type: String,
Expand All @@ -80,13 +85,13 @@ struct RerfeshRequest {
#[derive(Serialize, Deserialize, Debug, Clone)]
struct UserCredentials {
/// Client id
pub client_id: String,
pub(crate) client_id: String,
/// Client secret
pub client_secret: String,
pub(crate) client_secret: String,
/// Refresh Token
pub refresh_token: String,
pub(crate) refresh_token: String,
/// Type
pub r#type: String,
pub(crate) r#type: String,
}

impl UserCredentials {
Expand Down
17 changes: 11 additions & 6 deletions src/default_service_account.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use crate::authentication_manager::ServiceAccount;
use crate::prelude::*;
use hyper::body::Body;
use hyper::Method;
use std::str;
use std::sync::RwLock;

use async_trait::async_trait;
use hyper::body::Body;
use hyper::{Method, Request};

use crate::authentication_manager::ServiceAccount;
use crate::error::Error;
use crate::types::{HyperClient, Token};
use crate::util::HyperExt;

#[derive(Debug)]
pub struct DefaultServiceAccount {
pub(crate) struct DefaultServiceAccount {
token: RwLock<Token>,
}

Expand All @@ -15,7 +20,7 @@ impl DefaultServiceAccount {
"http://metadata.google.internal/computeMetadata/v1/project/project-id";
const DEFAULT_TOKEN_GCP_URI: &'static str = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token";

pub async fn new(client: &HyperClient) -> Result<Self, Error> {
pub(crate) async fn new(client: &HyperClient) -> Result<Self, Error> {
let token = RwLock::new(Self::get_token(client).await?);
Ok(Self { token })
}
Expand Down
6 changes: 3 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,20 @@ pub enum Error {
/// GOOGLE_APPLICATION_CREDENTIALS is used for providing path to json file with applications credentials.
/// File can be donwoloaded in GCP Console when creating service account.
#[error("Path to custom auth credentials was not provided in `GOOGLE_APPLICATION_CREDENTIALS` env variable")]
AplicationProfileMissing,
ApplicationProfileMissing,

/// Wrong path to custom application profile credentials provided
///
/// Path has to be defined using `GOOGLE_APPLICATION_CREDENTIALS` environment variable
#[error("Environment variable `GOOGLE_APPLICATION_CREDENTIALS` contains invalid path to application profile file")]
AplicationProfilePath(std::io::Error),
ApplicationProfilePath(std::io::Error),

/// Wrong format of custom application profile
///
/// Application profile is downloaded from GCP console and is stored in filesystem on the server.
/// Full path is passed to library by seeting `GOOGLE_APPLICATION_CREDENTIALS` variable with path as a value.
#[error("Application profile provided in `GOOGLE_APPLICATION_CREDENTIALS` was not parsable")]
AplicationProfileFormat(serde_json::error::Error),
ApplicationProfileFormat(serde_json::error::Error),

/// Default user profile not found
///
Expand Down
Loading