Skip to content

Commit 52cf7d7

Browse files
authored
feat(auth): add downscope.Options.UniverseDomain (#9634)
1 parent 16efbb5 commit 52cf7d7

2 files changed

Lines changed: 53 additions & 11 deletions

File tree

auth/credentials/downscope/downscope.go

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ import (
2020
"fmt"
2121
"net/http"
2222
"net/url"
23+
"strings"
2324
"time"
2425

2526
"cloud.google.com/go/auth"
2627
"cloud.google.com/go/auth/internal"
2728
)
2829

29-
var identityBindingEndpoint = "https://sts.googleapis.com/v1/token"
30+
const (
31+
universeDomainPlaceholder = "UNIVERSE_DOMAIN"
32+
identityBindingEndpointTemplate = "https://sts.UNIVERSE_DOMAIN/v1/token"
33+
)
3034

3135
// Options for configuring [NewCredentials].
3236
type Options struct {
@@ -42,15 +46,27 @@ type Options struct {
4246
// Client configures the underlying client used to make network requests
4347
// when fetching tokens. Optional.
4448
Client *http.Client
49+
// UniverseDomain is the default service domain for a given Cloud universe.
50+
// The default value is "googleapis.com". Optional.
51+
UniverseDomain string
4552
}
4653

47-
func (c Options) client() *http.Client {
48-
if c.Client != nil {
49-
return c.Client
54+
func (o *Options) client() *http.Client {
55+
if o.Client != nil {
56+
return o.Client
5057
}
5158
return internal.CloneDefaultClient()
5259
}
5360

61+
// identityBindingEndpoint returns the identity binding endpoint with the
62+
// configured universe domain.
63+
func (o *Options) identityBindingEndpoint() string {
64+
if o.UniverseDomain == "" {
65+
return strings.Replace(identityBindingEndpointTemplate, universeDomainPlaceholder, internal.DefaultUniverseDomain, 1)
66+
}
67+
return strings.Replace(identityBindingEndpointTemplate, universeDomainPlaceholder, o.UniverseDomain, 1)
68+
}
69+
5470
// An AccessBoundaryRule Sets the permissions (and optionally conditions) that
5571
// the new token has on given resource.
5672
type AccessBoundaryRule struct {
@@ -108,17 +124,24 @@ func NewCredentials(opts *Options) (*auth.Credentials, error) {
108124
}
109125
}
110126
return auth.NewCredentials(&auth.CredentialsOptions{
111-
TokenProvider: &downscopedTokenProvider{Options: opts, Client: opts.client()},
127+
TokenProvider: &downscopedTokenProvider{
128+
Options: opts,
129+
Client: opts.client(),
130+
identityBindingEndpoint: opts.identityBindingEndpoint(),
131+
},
112132
ProjectIDProvider: auth.CredentialsPropertyFunc(opts.Credentials.ProjectID),
113133
QuotaProjectIDProvider: auth.CredentialsPropertyFunc(opts.Credentials.QuotaProjectID),
114-
UniverseDomainProvider: auth.CredentialsPropertyFunc(opts.Credentials.UniverseDomain),
134+
UniverseDomainProvider: internal.StaticCredentialsProperty(opts.UniverseDomain),
115135
}), nil
116136
}
117137

118138
// downscopedTokenProvider is used to retrieve a downscoped tokens.
119139
type downscopedTokenProvider struct {
120140
Options *Options
121141
Client *http.Client
142+
// identityBindingEndpoint is the identity binding endpoint with the
143+
// configured universe domain.
144+
identityBindingEndpoint string
122145
}
123146

124147
type downscopedOptions struct {
@@ -159,7 +182,7 @@ func (dts *downscopedTokenProvider) Token(ctx context.Context) (*auth.Token, err
159182
form.Add("subject_token", tok.Value)
160183
form.Add("options", string(b))
161184

162-
resp, err := dts.Client.PostForm(identityBindingEndpoint, form)
185+
resp, err := dts.Client.PostForm(dts.identityBindingEndpoint, form)
163186
if err != nil {
164187
return nil, err
165188
}

auth/credentials/downscope/downscope_test.go

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,6 @@ func TestNewTokenProvider(t *testing.T) {
6161

6262
}))
6363
defer ts.Close()
64-
oldEndpoint := identityBindingEndpoint
65-
identityBindingEndpoint = ts.URL
66-
t.Cleanup(func() { identityBindingEndpoint = oldEndpoint })
6764
creds, err := NewCredentials(&Options{
6865
Credentials: staticCredentials("token_base"),
6966
Rules: []AccessBoundaryRule{
@@ -76,6 +73,9 @@ func TestNewTokenProvider(t *testing.T) {
7673
if err != nil {
7774
t.Fatalf("NewTokenProvider() = %v", err)
7875
}
76+
// Replace the default STS endpoint on the TokenProvider with the test server URL.
77+
creds.TokenProvider.(*downscopedTokenProvider).identityBindingEndpoint = ts.URL
78+
7979
tok, err := creds.Token(context.Background())
8080
if err != nil {
8181
t.Fatalf("Token failed with error: %v", err)
@@ -85,7 +85,7 @@ func TestNewTokenProvider(t *testing.T) {
8585
}
8686
}
8787

88-
func TestTestNewCredentials_Validations(t *testing.T) {
88+
func TestNewCredentials_Validations(t *testing.T) {
8989
tests := []struct {
9090
name string
9191
opts *Options
@@ -136,3 +136,22 @@ func TestTestNewCredentials_Validations(t *testing.T) {
136136
})
137137
}
138138
}
139+
140+
func TestOptions_UniverseDomain(t *testing.T) {
141+
tests := []struct {
142+
universeDomain string
143+
want string
144+
}{
145+
{"", "https://sts.googleapis.com/v1/token"},
146+
{"googleapis.com", "https://sts.googleapis.com/v1/token"},
147+
{"example.com", "https://sts.example.com/v1/token"},
148+
}
149+
for _, tt := range tests {
150+
c := Options{
151+
UniverseDomain: tt.universeDomain,
152+
}
153+
if got := c.identityBindingEndpoint(); got != tt.want {
154+
t.Errorf("got %q, want %q", got, tt.want)
155+
}
156+
}
157+
}

0 commit comments

Comments
 (0)