-
Notifications
You must be signed in to change notification settings - Fork 1.4k
fix(auth): show env credential overrides #811
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
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -717,8 +717,12 @@ async fn handle_export(unmasked: bool) -> Result<(), GwsError> { | |
| /// Resolve OAuth client credentials from env vars or saved config file. | ||
| fn resolve_client_credentials() -> Result<(String, String, Option<String>), GwsError> { | ||
| // 1. Try env vars first | ||
| let env_id = std::env::var("GOOGLE_WORKSPACE_CLI_CLIENT_ID").ok(); | ||
| let env_secret = std::env::var("GOOGLE_WORKSPACE_CLI_CLIENT_SECRET").ok(); | ||
| let env_id = std::env::var("GOOGLE_WORKSPACE_CLI_CLIENT_ID") | ||
| .ok() | ||
| .filter(|value| !value.is_empty()); | ||
| let env_secret = std::env::var("GOOGLE_WORKSPACE_CLI_CLIENT_SECRET") | ||
| .ok() | ||
| .filter(|value| !value.is_empty()); | ||
|
|
||
| if let (Some(id), Some(secret)) = (env_id, env_secret) { | ||
| // Still try to load project_id from config file for the scope picker | ||
|
|
@@ -1260,6 +1264,8 @@ async fn handle_status() -> Result<(), GwsError> { | |
| } | ||
| } | ||
|
|
||
| add_oauth_env_status_fields(&mut output, has_config); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The addition of |
||
|
|
||
| // Show credential source by attempting actual resolution | ||
| let has_token_env = std::env::var("GOOGLE_WORKSPACE_CLI_TOKEN") | ||
| .ok() | ||
|
|
@@ -1453,6 +1459,25 @@ async fn handle_status() -> Result<(), GwsError> { | |
| Ok(()) | ||
| } | ||
|
|
||
| fn add_oauth_env_status_fields(output: &mut serde_json::Value, has_config: bool) { | ||
| let env_client_id = std::env::var("GOOGLE_WORKSPACE_CLI_CLIENT_ID") | ||
| .ok() | ||
| .filter(|value| !value.is_empty()); | ||
| let env_client_secret_set = std::env::var("GOOGLE_WORKSPACE_CLI_CLIENT_SECRET") | ||
| .ok() | ||
| .filter(|value| !value.is_empty()) | ||
| .is_some(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The environment variable check here is inconsistent with the actual credential resolution logic in let env_client_id = std::env::var("GOOGLE_WORKSPACE_CLI_CLIENT_ID").ok();
let env_client_secret_set = std::env::var("GOOGLE_WORKSPACE_CLI_CLIENT_SECRET").is_ok(); |
||
|
|
||
| output["env_client_id_set"] = json!(env_client_id.is_some()); | ||
| output["env_client_secret_set"] = json!(env_client_secret_set); | ||
| output["env_overrides_client_config"] = | ||
| json!(has_config && env_client_id.is_some() && env_client_secret_set); | ||
|
|
||
| if let Some(client_id) = env_client_id { | ||
| output["env_client_id"] = json!(mask_secret(&client_id)); | ||
| } | ||
| } | ||
|
|
||
| fn handle_logout() -> Result<(), GwsError> { | ||
| let plain_path = plain_credentials_path(); | ||
| let enc_path = credential_store::encrypted_credentials_path(); | ||
|
|
@@ -1974,6 +1999,30 @@ mod tests { | |
| assert_eq!(secret, "test-secret"); | ||
| } | ||
|
|
||
| #[test] | ||
| #[serial_test::serial] | ||
| fn resolve_credentials_ignores_empty_env_vars() { | ||
| let dir = tempfile::tempdir().unwrap(); | ||
| unsafe { | ||
| std::env::set_var("GOOGLE_WORKSPACE_CLI_CONFIG_DIR", dir.path()); | ||
| std::env::set_var("GOOGLE_WORKSPACE_CLI_CLIENT_ID", ""); | ||
| std::env::set_var("GOOGLE_WORKSPACE_CLI_CLIENT_SECRET", "test-secret"); | ||
| } | ||
|
|
||
| let result = resolve_client_credentials(); | ||
|
|
||
| unsafe { | ||
| std::env::remove_var("GOOGLE_WORKSPACE_CLI_CONFIG_DIR"); | ||
| std::env::remove_var("GOOGLE_WORKSPACE_CLI_CLIENT_ID"); | ||
| std::env::remove_var("GOOGLE_WORKSPACE_CLI_CLIENT_SECRET"); | ||
| } | ||
|
|
||
| match result.unwrap_err() { | ||
| GwsError::Auth(msg) => assert!(msg.contains("No OAuth client configured")), | ||
| other => panic!("Expected Auth error, got: {other:?}"), | ||
| } | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn handle_status_succeeds_without_credentials() { | ||
| // status should always succeed and report "none" | ||
|
|
@@ -1982,6 +2031,53 @@ mod tests { | |
| assert!(result.is_ok()); | ||
| } | ||
|
|
||
| #[test] | ||
| #[serial_test::serial] | ||
| fn add_oauth_env_status_fields_marks_env_override() { | ||
| unsafe { | ||
| std::env::set_var( | ||
| "GOOGLE_WORKSPACE_CLI_CLIENT_ID", | ||
| "1234567890abcdef.apps.googleusercontent.com", | ||
| ); | ||
| std::env::set_var("GOOGLE_WORKSPACE_CLI_CLIENT_SECRET", "test-secret"); | ||
| } | ||
|
|
||
| let mut output = json!({}); | ||
| add_oauth_env_status_fields(&mut output, true); | ||
|
|
||
| unsafe { | ||
| std::env::remove_var("GOOGLE_WORKSPACE_CLI_CLIENT_ID"); | ||
| std::env::remove_var("GOOGLE_WORKSPACE_CLI_CLIENT_SECRET"); | ||
| } | ||
|
|
||
| assert_eq!(output["env_client_id_set"], json!(true)); | ||
| assert_eq!(output["env_client_secret_set"], json!(true)); | ||
| assert_eq!(output["env_overrides_client_config"], json!(true)); | ||
| assert_eq!(output["env_client_id"], json!("1234....com")); | ||
| } | ||
|
|
||
| #[test] | ||
| #[serial_test::serial] | ||
| fn add_oauth_env_status_fields_ignores_empty_env_vars() { | ||
| unsafe { | ||
| std::env::set_var("GOOGLE_WORKSPACE_CLI_CLIENT_ID", ""); | ||
| std::env::set_var("GOOGLE_WORKSPACE_CLI_CLIENT_SECRET", "test-secret"); | ||
| } | ||
|
|
||
| let mut output = json!({}); | ||
| add_oauth_env_status_fields(&mut output, true); | ||
|
|
||
| unsafe { | ||
| std::env::remove_var("GOOGLE_WORKSPACE_CLI_CLIENT_ID"); | ||
| std::env::remove_var("GOOGLE_WORKSPACE_CLI_CLIENT_SECRET"); | ||
| } | ||
|
|
||
| assert_eq!(output["env_client_id_set"], json!(false)); | ||
| assert_eq!(output["env_client_secret_set"], json!(true)); | ||
| assert_eq!(output["env_overrides_client_config"], json!(false)); | ||
| assert!(output["env_client_id"].is_null()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn credential_store_save_load_round_trip() { | ||
| // Use encrypt/decrypt directly to avoid writing to the real config dir | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for resolving credentials from environment variables is now duplicated in
add_oauth_env_status_fields. To ensure consistency and improve maintainability, consider refactoring this into a shared helper function. This is particularly important for security-sensitive environment variables where the definition of "set" (e.g., handling empty vs. whitespace-only strings) should be uniform across the application.