Skip to content

Commit

Permalink
Auth refactor and token reuse fix
Browse files Browse the repository at this point in the history
Refactors a lot of the auth package and a bug in token reuse checking.
  • Loading branch information
Zertsov committed Oct 11, 2023
1 parent e0e444b commit d200204
Show file tree
Hide file tree
Showing 17 changed files with 1,022 additions and 479 deletions.
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,8 @@
},
"search.exclude": {
"crates/turbopack-tests/tests/snapshot/**": true
}
},
"rust-analyzer.linkedProjects": [
"./crates/turborepo-auth/Cargo.toml"
]
}
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/turborepo-api-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ turborepo-vercel-api-mock = { workspace = true }

[dependencies]
anyhow = { workspace = true }
async-trait.workspace = true
chrono = { workspace = true, features = ["serde"] }
lazy_static = { workspace = true }
regex = { workspace = true }
Expand Down
104 changes: 83 additions & 21 deletions crates/turborepo-api-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use std::{backtrace::Backtrace, env};

use async_trait::async_trait;
use lazy_static::lazy_static;
use regex::Regex;
pub use reqwest::Response;
Expand All @@ -26,6 +27,66 @@ lazy_static! {
Regex::new(r"(?i)(?:^|,) *authorization *(?:,|$)").unwrap();
}

#[async_trait]
pub trait Client {
async fn get_user(&self, token: &str) -> Result<UserResponse>;
async fn get_teams(&self, token: &str) -> Result<TeamsResponse>;
async fn get_team(&self, token: &str, team_id: &str) -> Result<Option<Team>>;
fn add_ci_header(request_builder: RequestBuilder) -> RequestBuilder;
fn add_team_params(
request_builder: RequestBuilder,
team_id: &str,
team_slug: Option<&str>,
) -> RequestBuilder;
async fn get_caching_status(
&self,
token: &str,
team_id: &str,
team_slug: Option<&str>,
) -> Result<CachingStatusResponse>;
async fn get_spaces(&self, token: &str, team_id: Option<&str>) -> Result<SpacesResponse>;
async fn verify_sso_token(&self, token: &str, token_name: &str) -> Result<VerifiedSsoUser>;
async fn put_artifact(
&self,
hash: &str,
artifact_body: &[u8],
duration: u64,
tag: Option<&str>,
token: &str,
) -> Result<()>;
async fn handle_403(response: Response) -> Error;
async fn fetch_artifact(
&self,
hash: &str,
token: &str,
team_id: &str,
team_slug: Option<&str>,
) -> Result<Response>;
async fn artifact_exists(
&self,
hash: &str,
token: &str,
team_id: &str,
team_slug: Option<&str>,
) -> Result<Response>;
async fn get_artifact(
&self,
hash: &str,
token: &str,
team_id: &str,
team_slug: Option<&str>,
method: Method,
) -> Result<Response>;
async fn do_preflight(
&self,
token: &str,
request_url: &str,
request_method: &str,
request_headers: &str,
) -> Result<PreflightResponse>;
fn make_url(&self, endpoint: &str) -> String;
}

pub struct APIClient {
client: reqwest::Client,
base_url: String,
Expand All @@ -39,8 +100,9 @@ pub struct APIAuth {
pub team_slug: Option<String>,
}

impl APIClient {
pub async fn get_user(&self, token: &str) -> Result<UserResponse> {
#[async_trait]
impl Client for APIClient {
async fn get_user(&self, token: &str) -> Result<UserResponse> {
let url = self.make_url("/v2/user");
let request_builder = self
.client
Expand All @@ -55,7 +117,7 @@ impl APIClient {
Ok(response.json().await?)
}

pub async fn get_teams(&self, token: &str) -> Result<TeamsResponse> {
async fn get_teams(&self, token: &str) -> Result<TeamsResponse> {
let request_builder = self
.client
.get(self.make_url("/v2/teams?limit=100"))
Expand All @@ -70,7 +132,7 @@ impl APIClient {
Ok(response.json().await?)
}

pub async fn get_team(&self, token: &str, team_id: &str) -> Result<Option<Team>> {
async fn get_team(&self, token: &str, team_id: &str) -> Result<Option<Team>> {
let response = self
.client
.get(self.make_url("/v2/team"))
Expand All @@ -84,7 +146,6 @@ impl APIClient {

Ok(response.json().await?)
}

fn add_ci_header(mut request_builder: RequestBuilder) -> RequestBuilder {
if is_ci() {
if let Some(vendor_constant) = Vendor::get_constant() {
Expand All @@ -110,7 +171,7 @@ impl APIClient {
request_builder
}

pub async fn get_caching_status(
async fn get_caching_status(
&self,
token: &str,
team_id: &str,
Expand All @@ -132,7 +193,7 @@ impl APIClient {
Ok(response.json().await?)
}

pub async fn get_spaces(&self, token: &str, team_id: Option<&str>) -> Result<SpacesResponse> {
async fn get_spaces(&self, token: &str, team_id: Option<&str>) -> Result<SpacesResponse> {
// create url with teamId if provided
let endpoint = match team_id {
Some(team_id) => format!("/v0/spaces?limit=100&teamId={}", team_id),
Expand All @@ -153,7 +214,7 @@ impl APIClient {
Ok(response.json().await?)
}

pub async fn verify_sso_token(&self, token: &str, token_name: &str) -> Result<VerifiedSsoUser> {
async fn verify_sso_token(&self, token: &str, token_name: &str) -> Result<VerifiedSsoUser> {
let request_builder = self
.client
.get(self.make_url("/registration/verify"))
Expand All @@ -172,7 +233,7 @@ impl APIClient {
})
}

pub async fn put_artifact(
async fn put_artifact(
&self,
hash: &str,
artifact_body: &[u8],
Expand Down Expand Up @@ -258,7 +319,7 @@ impl APIClient {
}
}

pub async fn fetch_artifact(
async fn fetch_artifact(
&self,
hash: &str,
token: &str,
Expand All @@ -269,7 +330,7 @@ impl APIClient {
.await
}

pub async fn artifact_exists(
async fn artifact_exists(
&self,
hash: &str,
token: &str,
Expand Down Expand Up @@ -320,7 +381,7 @@ impl APIClient {
}
}

pub async fn do_preflight(
async fn do_preflight(
&self,
token: &str,
request_url: &str,
Expand Down Expand Up @@ -364,20 +425,25 @@ impl APIClient {
})
}

fn make_url(&self, endpoint: &str) -> String {
format!("{}{}", self.base_url, endpoint)
}
}

impl APIClient {
pub fn new(
base_url: impl AsRef<str>,
timeout: u64,
version: &str,
use_preflight: bool,
) -> Result<Self> {
let builder_result = if timeout != 0 {
) -> Result<APIClient> {
let client = if timeout != 0 {
reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(timeout))
.build()
.build()?
} else {
reqwest::Client::builder().build()
reqwest::Client::builder().build()?
};
let client = builder_result.map_err(Error::TlsError)?;

let user_agent = format!(
"turbo {} {} {} {}",
Expand All @@ -393,10 +459,6 @@ impl APIClient {
use_preflight,
})
}

fn make_url(&self, endpoint: &str) -> String {
format!("{}{}", self.base_url, endpoint)
}
}

#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions crates/turborepo-auth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ turborepo-api-client = { workspace = true }
turborepo-ui.workspace = true
turborepo-vercel-api-mock = { workspace = true }
webbrowser = { workspace = true }
async-trait.workspace = true

[dev-dependencies]
port_scanner = { workspace = true }
Loading

0 comments on commit d200204

Please sign in to comment.