Compare commits
2 Commits
main
...
feat/sa-pu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db92d47d5e | ||
|
|
91b0baa92d |
@ -33,6 +33,7 @@
|
||||
- Groups: include required label filters in transitive group searches so `groups list` doesn’t 400 on Cloud Identity. (#315) — thanks @salmonumbrella.
|
||||
- Gmail: fall back to `MimeType` charset hints when `Content-Type` headers are missing so GBK/GB2312 message bodies decode correctly. (#428) — thanks @WinnCook.
|
||||
- Auth: preserve scope-shaping flags in the remote step-2 replay guidance for `auth add --remote`. (#427) — thanks @doodaaatimmy-creator.
|
||||
- Auth: allow pure service-account mode when the configured subject matches the service account itself, instead of forcing domain-wide delegation impersonation. (#399) — thanks @carrotRakko.
|
||||
- Calendar: preserve full RRULE values and recurring-event timezones during updates so recurrence edits don’t lose BYDAY lists or hit missing-timezone API errors. (#392) — thanks @salmonumbrella.
|
||||
- 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.
|
||||
|
||||
@ -12,12 +12,23 @@ import (
|
||||
"github.com/steipete/gogcli/internal/config"
|
||||
)
|
||||
|
||||
func serviceAccountSubject(subject string, serviceAccountEmail string) string {
|
||||
if subject == "" || subject == serviceAccountEmail {
|
||||
return ""
|
||||
}
|
||||
|
||||
return subject
|
||||
}
|
||||
|
||||
var newServiceAccountTokenSource = func(ctx context.Context, keyJSON []byte, subject string, scopes []string) (oauth2.TokenSource, error) {
|
||||
cfg, err := google.JWTConfigFromJSON(keyJSON, scopes...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse service account: %w", err)
|
||||
}
|
||||
cfg.Subject = subject
|
||||
// Only set Subject (impersonation) when the caller requests a different
|
||||
// identity than the service account itself. When subject matches the
|
||||
// SA's client_email we run in pure SA mode: no Domain-Wide Delegation.
|
||||
cfg.Subject = serviceAccountSubject(subject, cfg.Email)
|
||||
|
||||
// Ensure token exchanges don't hang forever.
|
||||
ctx = context.WithValue(ctx, oauth2.HTTPClient, &http.Client{Timeout: tokenExchangeTimeout})
|
||||
|
||||
@ -11,6 +11,47 @@ import (
|
||||
"github.com/steipete/gogcli/internal/config"
|
||||
)
|
||||
|
||||
func TestServiceAccountSubject(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
subject string
|
||||
serviceAccountEmail string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "empty subject stays empty",
|
||||
subject: "",
|
||||
serviceAccountEmail: "sa@test-project.iam.gserviceaccount.com",
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "same subject becomes pure service account mode",
|
||||
subject: "sa@test-project.iam.gserviceaccount.com",
|
||||
serviceAccountEmail: "sa@test-project.iam.gserviceaccount.com",
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "different subject keeps impersonation target",
|
||||
subject: "user@example.com",
|
||||
serviceAccountEmail: "sa@test-project.iam.gserviceaccount.com",
|
||||
want: "user@example.com",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := serviceAccountSubject(tt.subject, tt.serviceAccountEmail)
|
||||
if got != tt.want {
|
||||
t.Fatalf("serviceAccountSubject(%q, %q) = %q, want %q", tt.subject, tt.serviceAccountEmail, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenSourceForServiceAccountScopes_NonKeepIgnoresKeepFallback(t *testing.T) {
|
||||
home := t.TempDir()
|
||||
t.Setenv("HOME", home)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user