Skip to content

Commit

Permalink
add source to AuthFile, create Auth enum
Browse files Browse the repository at this point in the history
  • Loading branch information
Zertsov committed Jan 4, 2024
1 parent b17b85e commit 94a8e59
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 9 deletions.
1 change: 1 addition & 0 deletions crates/turborepo-auth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ tokio.workspace = true
tracing.workspace = true
turbopath = { workspace = true }
turborepo-api-client = { workspace = true }
turborepo-dirs = { workspace = true }
turborepo-ui.workspace = true
turborepo-vercel-api = { workspace = true }
turborepo-vercel-api-mock = { workspace = true }
Expand Down
14 changes: 11 additions & 3 deletions crates/turborepo-auth/src/auth_file.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use turbopath::AbsoluteSystemPath;
use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf};
use turborepo_api_client::Client;

use crate::Error;

#[derive(Serialize, Deserialize, Debug, Default, PartialEq)]
/// AuthFile contains a list of domains, each with a token.
pub struct AuthFile {
source: AbsoluteSystemPathBuf,
tokens: HashMap<String, String>,
}

impl AuthFile {
/// Create an empty auth file. Caller must invoke `write_to_disk` to
/// actually write it to disk.
pub fn new() -> Self {
pub fn new(source: AbsoluteSystemPathBuf) -> Self {
AuthFile {
source,
tokens: HashMap::new(),
}
}
Expand Down Expand Up @@ -68,7 +70,7 @@ impl AuthFile {
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
/// Contains the token itself and a list of teams the token is valid for.
/// Contains the token itself.
pub struct AuthToken {
/// The token itself.
pub token: String,
Expand All @@ -77,6 +79,12 @@ pub struct AuthToken {
}

impl AuthToken {
pub fn new(token: &str, api: &str) -> Self {
AuthToken {
token: token.to_string(),
api: api.to_string(),
}
}
pub fn friendly_api_display(&self) -> &str {
if self.api.contains("vercel.com") {
// We're Vercel, let's make it look nice ;)
Expand Down
78 changes: 74 additions & 4 deletions crates/turborepo-auth/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ mod sso;
mod sso_server;
mod ui;

use turbopath::AbsoluteSystemPath;
use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf};
use turborepo_api_client::Client;

pub use self::{
Expand All @@ -26,10 +26,80 @@ pub use self::{
pub const TURBOREPO_AUTH_FILE_NAME: &str = "auth.json";
pub const TURBOREPO_LEGACY_AUTH_FILE_NAME: &str = "config.json";
pub const TURBOREPO_CONFIG_DIR: &str = "turborepo";
pub const VERCEL_CONFIG_DIR: &str = "com.vercel.cli";
pub const VERCEL_AUTH_FILE_NAME: &str = "auth.json";

pub const DEFAULT_LOGIN_URL: &str = "https://vercel.com";
pub const DEFAULT_API_URL: &str = "https://vercel.com/api";

/// AuthSource determines where the auth file should be read from. Each of the
/// variants has different initialization and permissions.
pub enum AuthSource {
/// A token passed in via the CLI. This is the most ephemeral of the auth
/// sources. It will no-op on any and all fs write operations.
CLI(String),
/// Our custom auth file. This is allowed to read/write.
Turborepo(AbsoluteSystemPathBuf),
/// The Vercel auth file. This is a read-only source issued from the Vercel
/// CLI. Write operations will no-op.
Vercel(AbsoluteSystemPathBuf),
}

/// Auth is an enum that contains either a token or a file. This is used for
/// holding a/many token(s), depending on the variant.
pub enum Auth {
Token(AuthToken),
File(AuthFile),
}
impl Auth {
/// Creates a new Auth enum from an AuthSource.
/// ## Arguments
/// * `source`: The AuthSource to create the Auth enum from.
/// ## Returns
/// * `Auth`: The Auth enum.
/// ## Examples
/// ```
/// use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf};
/// use turborepo_auth::{Auth, AuthSource};
/// let auth_file_path = AbsoluteSystemPath::new("/path/to/auth/file").unwrap();
///
/// // Create an Auth enum from a file.
/// let auth_file = Auth::new(AuthSource::Turborepo(auth_file_path));
/// // Create an Auth enum from a token.
/// let auth_token = Auth::new(AuthSource::CLI("test-token".to_string()));
///
/// assert!(auth_file.is_file());
/// assert!(!auth_token.is_file()
/// ```
pub fn new(source: AuthSource) -> Self {
match source {
// Any token coming in from the CLI is a one-off, so we don't give it any file checks,
// api keys, or permissions. If we add functionality, like refreshing tokens
// or anything that might allow for a passed in token to be written to disk,
// we'll update this arm.
AuthSource::CLI(t) => Auth::Token(AuthToken {
token: t,
api: "".to_string(),
}),
AuthSource::Turborepo(source) => Auth::File(AuthFile::new(source)),
AuthSource::Vercel(source) => Auth::File(AuthFile::new(source)),
}
}
/// Determines if this enum is a file or a token.
pub fn is_file(&self) -> bool {
matches!(self, Auth::File(_))
}
/// Returns the underlying token. If the enum is a `Token`, it will return
/// the token used to construct it. Otherwise, the `api` argument is used to
/// look up the token in the file, if it exists.
pub fn get_token(&self, api: &str) -> Option<AuthToken> {
match self {
Auth::Token(t) => Some(t.clone()),
Auth::File(f) => f.get_token(api),
}
}
}

/// Checks the auth file path first, then the config file path, and does the
/// following:
/// 1) If the auth file exists, read it and return the contents from it, if
Expand Down Expand Up @@ -58,7 +128,7 @@ pub async fn read_or_create_auth_file(
})?;
let tokens: AuthFile = serde_json::from_str(&content)
.map_err(|e| Error::FailedToDeserializeAuthFile { source: e })?;
let mut auth_file = AuthFile::new();
let mut auth_file = AuthFile::new(auth_file_path.to_owned());
for (api, token) in tokens.tokens() {
auth_file.insert(api.to_owned(), token.to_owned());
}
Expand All @@ -76,7 +146,7 @@ pub async fn read_or_create_auth_file(

let auth_token = convert_to_auth_token(&config_token.token, client);

let mut auth_file = AuthFile::new();
let mut auth_file = AuthFile::new(auth_file_path.to_owned());
auth_file.insert(client.base_url().to_owned(), auth_token.token);
auth_file.write_to_disk(auth_file_path)?;
return Ok(auth_file);
Expand Down Expand Up @@ -106,7 +176,7 @@ mod tests {
.expect("Failed to create config file path");

// Create auth file
let mut mock_auth_file = AuthFile::new();
let mut mock_auth_file = AuthFile::new(auth_file_path.to_owned());
mock_auth_file.insert("mock-api".to_owned(), "mock-token".to_owned());
mock_auth_file.write_to_disk(auth_file_path).unwrap();

Expand Down
4 changes: 2 additions & 2 deletions crates/turborepo-paths/src/absolute_system_path_buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ use std::{
use camino::{Utf8Components, Utf8Path, Utf8PathBuf};
use fs_err as fs;
use path_clean::PathClean;
use serde::Serialize;
use serde::{Deserialize, Serialize};

use crate::{AbsoluteSystemPath, AnchoredSystemPathBuf, PathError};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
pub struct AbsoluteSystemPathBuf(pub(crate) Utf8PathBuf);

impl Borrow<AbsoluteSystemPath> for AbsoluteSystemPathBuf {
Expand Down

0 comments on commit 94a8e59

Please sign in to comment.