Compare commits
2 Commits
main
...
fix/keep-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3165da427d | ||
|
|
91b4822c60 |
@ -15,6 +15,7 @@
|
||||
- Contacts: fix grouped parameter types in CRUD helpers to restore builds on newer Go toolchains. (#355) — thanks @laihenyi.
|
||||
- Timezone: embed the IANA timezone database so Windows builds can resolve calendar timezones correctly. (#388) — thanks @visionik.
|
||||
- Google API: use transport-level response-header timeouts for API clients while keeping token exchanges bounded, so large downloads are not cut short by `http.Client.Timeout`. (#425) — thanks @laihenyi.
|
||||
- Auth: keep Keep-only service-account fallback isolated to Keep commands so other Google services do not accidentally pick it up. (#414) — thanks @jgwesterlund.
|
||||
- Gmail: add a fetch delay in `watch serve` so History API reads don't race message indexing. (#397) — thanks @salmonumbrella.
|
||||
- Gmail: allow Workspace-managed send-as aliases with empty verification status in `send` and `drafts create`. (#407) — thanks @salmonumbrella.
|
||||
- Gmail: preserve the selected `--client` during `watch serve` push handling instead of falling back to the default client. (#411) — thanks @chrysb.
|
||||
|
||||
@ -111,7 +111,7 @@ func optionsForAccountScopes(ctx context.Context, serviceLabel string, email str
|
||||
|
||||
var ts oauth2.TokenSource
|
||||
|
||||
if serviceAccountTS, saPath, ok, err := tokenSourceForServiceAccountScopes(ctx, email, scopes); err != nil {
|
||||
if serviceAccountTS, saPath, ok, err := tokenSourceForServiceAccountScopes(ctx, serviceLabel, email, scopes); err != nil {
|
||||
return nil, fmt.Errorf("service account token source: %w", err)
|
||||
} else if ok {
|
||||
slog.Debug("using service account credentials", "email", email, "path", saPath)
|
||||
|
||||
@ -25,7 +25,7 @@ var newServiceAccountTokenSource = func(ctx context.Context, keyJSON []byte, sub
|
||||
return cfg.TokenSource(ctx), nil
|
||||
}
|
||||
|
||||
func tokenSourceForServiceAccountScopes(ctx context.Context, email string, scopes []string) (oauth2.TokenSource, string, bool, error) {
|
||||
func tokenSourceForServiceAccountScopes(ctx context.Context, serviceLabel string, email string, scopes []string) (oauth2.TokenSource, string, bool, error) {
|
||||
saPath, err := config.ServiceAccountPath(email)
|
||||
if err != nil {
|
||||
return nil, "", false, fmt.Errorf("service account path: %w", err)
|
||||
@ -45,6 +45,11 @@ func tokenSourceForServiceAccountScopes(ctx context.Context, email string, scope
|
||||
return nil, "", false, fmt.Errorf("read service account key: %w", readErr)
|
||||
}
|
||||
|
||||
// Keep-specific service account files should only be used for Keep.
|
||||
if serviceLabel != "keep" {
|
||||
return nil, "", false, nil
|
||||
}
|
||||
|
||||
// Backwards compatibility: Keep used a dedicated stored service account file.
|
||||
keepSAPath, keepErr := config.KeepServiceAccountPath(email)
|
||||
if keepErr == nil {
|
||||
|
||||
113
internal/googleapi/service_account_test.go
Normal file
113
internal/googleapi/service_account_test.go
Normal file
@ -0,0 +1,113 @@
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/steipete/gogcli/internal/config"
|
||||
)
|
||||
|
||||
func TestTokenSourceForServiceAccountScopes_NonKeepIgnoresKeepFallback(t *testing.T) {
|
||||
home := t.TempDir()
|
||||
t.Setenv("HOME", home)
|
||||
t.Setenv("XDG_CONFIG_HOME", filepath.Join(home, "xdg-config"))
|
||||
|
||||
keepSAPath, err := config.KeepServiceAccountPath("a@b.com")
|
||||
if err != nil {
|
||||
t.Fatalf("KeepServiceAccountPath: %v", err)
|
||||
}
|
||||
|
||||
if _, ensureErr := config.EnsureDir(); ensureErr != nil {
|
||||
t.Fatalf("EnsureDir: %v", ensureErr)
|
||||
}
|
||||
|
||||
if writeErr := os.WriteFile(keepSAPath, []byte(`{"type":"service_account"}`), 0o600); writeErr != nil {
|
||||
t.Fatalf("write keep sa: %v", writeErr)
|
||||
}
|
||||
|
||||
origSA := newServiceAccountTokenSource
|
||||
|
||||
t.Cleanup(func() { newServiceAccountTokenSource = origSA })
|
||||
|
||||
called := false
|
||||
newServiceAccountTokenSource = func(context.Context, []byte, string, []string) (oauth2.TokenSource, error) {
|
||||
called = true
|
||||
return oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "t"}), nil
|
||||
}
|
||||
|
||||
ts, path, ok, err := tokenSourceForServiceAccountScopes(context.Background(), "gmail", "a@b.com", []string{"s1"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
|
||||
if ok {
|
||||
t.Fatalf("expected keep-only fallback to be ignored, got ok=true path=%q ts=%v", path, ts)
|
||||
}
|
||||
|
||||
if called {
|
||||
t.Fatalf("expected keep-only fallback not to initialize a token source")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenSourceForServiceAccountScopes_KeepUsesKeepFallback(t *testing.T) {
|
||||
home := t.TempDir()
|
||||
t.Setenv("HOME", home)
|
||||
t.Setenv("XDG_CONFIG_HOME", filepath.Join(home, "xdg-config"))
|
||||
|
||||
keepSAPath, err := config.KeepServiceAccountPath("a@b.com")
|
||||
if err != nil {
|
||||
t.Fatalf("KeepServiceAccountPath: %v", err)
|
||||
}
|
||||
|
||||
if _, ensureErr := config.EnsureDir(); ensureErr != nil {
|
||||
t.Fatalf("EnsureDir: %v", ensureErr)
|
||||
}
|
||||
|
||||
if writeErr := os.WriteFile(keepSAPath, []byte(`{"type":"service_account"}`), 0o600); writeErr != nil {
|
||||
t.Fatalf("write keep sa: %v", writeErr)
|
||||
}
|
||||
|
||||
origSA := newServiceAccountTokenSource
|
||||
|
||||
t.Cleanup(func() { newServiceAccountTokenSource = origSA })
|
||||
|
||||
called := false
|
||||
newServiceAccountTokenSource = func(_ context.Context, keyJSON []byte, subject string, scopes []string) (oauth2.TokenSource, error) {
|
||||
called = true
|
||||
|
||||
if subject != "a@b.com" {
|
||||
t.Fatalf("unexpected subject: %q", subject)
|
||||
}
|
||||
|
||||
if len(scopes) != 1 || scopes[0] != "s1" {
|
||||
t.Fatalf("unexpected scopes: %#v", scopes)
|
||||
}
|
||||
|
||||
if string(keyJSON) == "" {
|
||||
t.Fatalf("expected key JSON")
|
||||
}
|
||||
|
||||
return oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "t"}), nil
|
||||
}
|
||||
|
||||
ts, path, ok, err := tokenSourceForServiceAccountScopes(context.Background(), "keep", "a@b.com", []string{"s1"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
|
||||
if !ok || ts == nil {
|
||||
t.Fatalf("expected keep fallback token source, got ok=%v ts=%v", ok, ts)
|
||||
}
|
||||
|
||||
if path != keepSAPath {
|
||||
t.Fatalf("unexpected keep fallback path: %q", path)
|
||||
}
|
||||
|
||||
if !called {
|
||||
t.Fatalf("expected keep fallback token source initialization")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user