Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/fix-tui-cloud-platform-scope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@googleworkspace/cli": patch
---

Stop unconditionally injecting cloud-platform scope in TUI picker
29 changes: 8 additions & 21 deletions src/auth_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -539,19 +539,11 @@ async fn resolve_scopes(
/// `https://www.googleapis.com/auth/`). A scope matches a service if its
/// short name equals the service or starts with `service.` (e.g. service
/// `drive` matches `drive`, `drive.readonly`, `drive.metadata.readonly`).
///
/// The `cloud-platform` scope always passes through since it's a
/// cross-service platform scope.
fn scope_matches_service(scope_url: &str, services: &HashSet<String>) -> bool {
let short = scope_url
.strip_prefix("https://www.googleapis.com/auth/")
.unwrap_or(scope_url);

// cloud-platform is a cross-service scope, always include
if short == "cloud-platform" {
return true;
}

let prefix = short.split('.').next().unwrap_or(short);

services.iter().any(|svc| {
Expand Down Expand Up @@ -660,7 +652,7 @@ fn run_discovery_scope_picker(
relevant_scopes: &[crate::setup::DiscoveredScope],
services_filter: Option<&HashSet<String>>,
) -> Option<Vec<String>> {
use crate::setup::{ScopeClassification, PLATFORM_SCOPE};
use crate::setup::ScopeClassification;
use crate::setup_tui::{PickerResult, SelectItem};

let mut recommended_scopes = vec![];
Expand Down Expand Up @@ -832,11 +824,6 @@ fn run_discovery_scope_picker(
}
}

// Always include cloud-platform scope
if !selected.contains(&PLATFORM_SCOPE.to_string()) {
selected.push(PLATFORM_SCOPE.to_string());
}

// Hierarchical dedup: if we have both a broad scope (e.g. `.../auth/drive`)
// and a narrower scope (e.g. `.../auth/drive.metadata`, `.../auth/drive.readonly`),
// drop the narrower one since the broad scope subsumes it.
Expand Down Expand Up @@ -1926,9 +1913,11 @@ mod tests {
}

#[test]
fn scope_matches_service_cloud_platform_always_matches() {
fn scope_matches_service_cloud_platform_not_injected() {
// cloud-platform should only appear when the user explicitly selects it,
// not be auto-included for unrelated services like "drive"
let services: HashSet<String> = ["drive"].iter().map(|s| s.to_string()).collect();
assert!(scope_matches_service(
assert!(!scope_matches_service(
"https://www.googleapis.com/auth/cloud-platform",
&services
));
Expand Down Expand Up @@ -1990,9 +1979,7 @@ mod tests {
.strip_prefix("https://www.googleapis.com/auth/")
.unwrap_or(scope);
assert!(
short.starts_with("drive")
|| short.starts_with("gmail")
|| short == "cloud-platform",
short.starts_with("drive") || short.starts_with("gmail"),
"Unexpected scope with service filter: {scope}"
);
}
Expand All @@ -2017,7 +2004,7 @@ mod tests {
.strip_prefix("https://www.googleapis.com/auth/")
.unwrap_or(scope);
assert!(
short.starts_with("drive") || short == "cloud-platform",
short.starts_with("drive"),
"Unexpected scope with service + readonly filter: {scope}"
);
}
Expand All @@ -2033,7 +2020,7 @@ mod tests {
.strip_prefix("https://www.googleapis.com/auth/")
.unwrap_or(scope);
assert!(
short.starts_with("gmail") || short == "cloud-platform",
short.starts_with("gmail"),
"Unexpected scope with service + full filter: {scope}"
);
}
Expand Down
2 changes: 0 additions & 2 deletions src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,6 @@ pub enum ScopeClassification {
Restricted,
}

pub const PLATFORM_SCOPE: &str = "https://www.googleapis.com/auth/cloud-platform";

/// A scope discovered from a Discovery Document.
#[derive(Clone)]
pub struct DiscoveredScope {
Expand Down
Loading