Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Environment Variable for Authentication Token Refactor #75

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

amanjeev
Copy link
Member

@amanjeev amanjeev commented Jan 14, 2025

For important environment variables like CRITICALUP_TOKEN we should use a separate struct as a technique so as to avoid having to call std::env::var at multiple places. This can help with localizing errors and testing.

This does not change any functionality.

@amanjeev amanjeev force-pushed the amanjeev/criticalup-token-env-var branch from 8dfd262 to 9620519 Compare January 14, 2025 18:24
@Hoverbear
Copy link
Member

@amanjeev is this ready for review? Can you give it a meaningful title and description if so?

@amanjeev amanjeev changed the title Add EnvVar as a parameter Environment Variable for Authentication Token Refactor Jan 22, 2025
@amanjeev amanjeev marked this pull request as ready for review January 22, 2025 23:54
@amanjeev amanjeev requested a review from Hoverbear January 22, 2025 23:54
@amanjeev
Copy link
Member Author

@Hoverbear It is ready for review now.

@@ -374,6 +376,34 @@ impl StateInstallation {
}
}

#[derive(Default)]
pub struct EnvVars {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Paths does a similar thing (configurs some values based on env vars) here:

pub struct Paths {
pub(crate) state_file: PathBuf,
pub proxies_dir: PathBuf,
pub installation_dir: PathBuf,
pub cache_dir: PathBuf,
#[cfg(test)]
pub(crate) root: PathBuf,
}

Specifically here:

fn find_root(whitelabel: &WhitelabelConfig) -> Option<PathBuf> {
match env::var_os("CRITICALUP_ROOT") {
Some(val) if val.is_empty() => platform_specific_root(whitelabel),
Some(val) => Some(PathBuf::from(val)),
None => platform_specific_root(whitelabel),
}
}

As well as:

fn find_cache_dir(whitelabel: &WhitelabelConfig) -> Option<PathBuf> {
match env::var_os("CRITICALUP_CACHE_DIR") {
Some(val) if val.is_empty() => platform_specific_cache_dir(whitelabel),
Some(val) => Some(PathBuf::from(val)),
None => platform_specific_cache_dir(whitelabel),
}
}

This makes me wonder if Paths should be configured with the instance of these EnvVars instead, or if the root/cache_dir passed to it should not be options, but instead PathBufs acquired from EnvVars somehow. After all, CRITICALUP_CACHE_DIR is indeed an env var, and if we ever want to test that feature having it pull from env::var will be very problematic.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it makes sense to me to include these in EnvVars.

@@ -374,6 +376,34 @@ impl StateInstallation {
}
}

#[derive(Default)]
Copy link
Member

@Hoverbear Hoverbear Jan 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure EnvVars::default() resulting in a struct purely made of None values is what a programmer expects. Looking at EnvVars as a type name I would assume that EnvVars::default() would pull from the relevant environment. So I'd kind of assume it'd do the same thing as EnvVars::read().

As for an actually empty struct, I'd assume there would be an EnvVars::empty() I could call if I really needed (for testing etc)... I also think EnvVars::empty() could be a const fn and thus called in the const ENV_VARS: EnvVars = EnvVars::empty() below?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one of my issues was that I need to return an error when the string is not Unicode.

@@ -60,20 +61,21 @@ impl State {
pub async fn authentication_token(
&self,
token_path: Option<&str>,
env_vars: &EnvVars,
Copy link
Member

@Hoverbear Hoverbear Jan 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kind of muddies the waters on what State is. (In fact, having token_path here also muddies it, maybe we should consider removing the sole use of token_path here and find more clear logic).

At least from my reading, State represents what is on disk, yet now state.authentication_token(...) might return me the value from an env var instead of my state file (... or some random file path I passed in!?), and it requires me to pass in EnvVars.

Seems to me the logic around getting what the canonical token is should be something like:

let env = EnvVars::read();
let state = State::load();

// Determine the canonical auth token
// Having the env var set is higher priority than the on-disk
let auth_token = if let Some(env_token) = env.authentication_token() {
  env_token
} else if let Some(state_token) = state.authentication_token() {
  state_token
}

println!("The token is {}", auth_token);

let header = self
.state
.authentication_token(path_to_token_file)
.authentication_token(path_to_token_file, &env_vars)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted elsewhere but this is the only usage of the token file path in our codebase, it creates some very confusing logic and perhaps we should refactor it out and have EnvVars intelligently detect if it's inside docker and look at /run/secrets when it's being built.

Co-authored-by: Ana Hobden <operator@hoverbear.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants