Skip to content
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

feat(auth): refactor public sigs to use Credentials #9603

Merged
merged 8 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions auth/credentials/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@
//
// # Credentials
//
// The [Credentials] type represents Google credentials, including Application
// Default Credentials.
// The [cloud.google.com/go/auth.Credentials] type represents Google
// credentials, including Application Default Credentials.
//
// Use [DetectDefault] to obtain Application Default Credentials.
//
Expand Down
6 changes: 3 additions & 3 deletions auth/credentials/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func ExampleDetectDefault() {
log.Fatal(err)
}
client, err := httptransport.NewClient(&httptransport.Options{
TokenProvider: creds,
Credentials: creds,
})
if err != nil {
log.Fatal(err)
Expand All @@ -55,7 +55,7 @@ func ExampleDetectDefault_withFilepath() {
log.Fatal(err)
}
client, err := httptransport.NewClient(&httptransport.Options{
TokenProvider: creds,
Credentials: creds,
})
if err != nil {
log.Fatal(err)
Expand All @@ -76,7 +76,7 @@ func ExampleDetectDefault_withJSON() {
log.Fatal(err)
}
client, err := httptransport.NewClient(&httptransport.Options{
TokenProvider: creds,
Credentials: creds,
})
if err != nil {
log.Fatal(err)
Expand Down
2 changes: 1 addition & 1 deletion auth/downscope/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
// For example, a token broker can be set up on a server in a private network.
// Various workloads (token consumers) in the same network will send
// authenticated requests to that broker for downscoped tokens to access or
// modify specific google cloud storage buckets. See the NewTokenProvider example
// modify specific google cloud storage buckets. See the NewCredentials example
// for an example of how a token broker would use this package.
//
// The broker will use the functionality in this package to generate a
Expand Down
26 changes: 15 additions & 11 deletions auth/downscope/downscope.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,11 @@ import (

var identityBindingEndpoint = "https://sts.googleapis.com/v1/token"

// Options for configuring [NewTokenProvider].
// Options for configuring [NewCredentials].
type Options struct {
// BaseProvider is the [cloud.google.com/go/auth.TokenProvider] used to
// create the downscoped provider. The downscoped provider therefore has
// some subset of the accesses of the original BaseProvider. Required.
BaseProvider auth.TokenProvider
// Credentials is the [cloud.google.com/go/auth.Credentials] used to
// create the downscoped provider. Required.
codyoss marked this conversation as resolved.
Show resolved Hide resolved
Credentials *auth.Credentials
// Rules defines the accesses held by the new downscoped provider. One or
// more AccessBoundaryRules are required to define permissions for the new
// downscoped provider. Each one defines an access (or set of accesses) that
Expand Down Expand Up @@ -84,14 +83,14 @@ type AvailabilityCondition struct {
Description string `json:"description,omitempty"`
}

// NewTokenProvider returns a [cloud.google.com/go/auth.TokenProvider] that is
// NewCredentials returns a [cloud.google.com/go/auth.Credentials] that is
// more restrictive than [Options.BaseProvider] provided.
codyoss marked this conversation as resolved.
Show resolved Hide resolved
func NewTokenProvider(opts *Options) (auth.TokenProvider, error) {
func NewCredentials(opts *Options) (*auth.Credentials, error) {
if opts == nil {
return nil, fmt.Errorf("downscope: providing opts is required")
}
if opts.BaseProvider == nil {
return nil, fmt.Errorf("downscope: BaseProvider cannot be nil")
if opts.Credentials == nil {
return nil, fmt.Errorf("downscope: BaseCredentials cannot be nil")
codyoss marked this conversation as resolved.
Show resolved Hide resolved
}
if len(opts.Rules) == 0 {
return nil, fmt.Errorf("downscope: length of AccessBoundaryRules must be at least 1")
Expand All @@ -107,7 +106,12 @@ func NewTokenProvider(opts *Options) (auth.TokenProvider, error) {
return nil, fmt.Errorf("downscope: all rules must provide at least one permission")
}
}
return &downscopedTokenProvider{Options: opts, Client: opts.client()}, nil
return auth.NewCredentials(&auth.CredentialsOptions{
TokenProvider: &downscopedTokenProvider{Options: opts, Client: opts.client()},
ProjectIDProvider: auth.CredentialsPropertyFunc(opts.Credentials.ProjectID),
QuotaProjectIDProvider: auth.CredentialsPropertyFunc(opts.Credentials.QuotaProjectID),
UniverseDomainProvider: auth.CredentialsPropertyFunc(opts.Credentials.UniverseDomain),
}), nil
}

// downscopedTokenProvider is used to retrieve a downscoped tokens.
Expand Down Expand Up @@ -138,7 +142,7 @@ func (dts *downscopedTokenProvider) Token(ctx context.Context) (*auth.Token, err
},
}

tok, err := dts.Options.BaseProvider.Token(ctx)
tok, err := dts.Options.Credentials.Token(ctx)
if err != nil {
return nil, fmt.Errorf("downscope: unable to obtain root token: %w", err)
}
Expand Down
30 changes: 18 additions & 12 deletions auth/downscope/downscope_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ var (
standardRespBody = `{"access_token":"fake_token","expires_in":42,"token_type":"Bearer"}`
)

func staticCredentials(tok string) *auth.Credentials {
return auth.NewCredentials(&auth.CredentialsOptions{
TokenProvider: staticTokenProvider(tok),
})
}

type staticTokenProvider string

func (s staticTokenProvider) Token(context.Context) (*auth.Token, error) {
Expand Down Expand Up @@ -58,8 +64,8 @@ func TestNewTokenProvider(t *testing.T) {
oldEndpoint := identityBindingEndpoint
identityBindingEndpoint = ts.URL
t.Cleanup(func() { identityBindingEndpoint = oldEndpoint })
tp, err := NewTokenProvider(&Options{
BaseProvider: staticTokenProvider("token_base"),
creds, err := NewCredentials(&Options{
Credentials: staticCredentials("token_base"),
Rules: []AccessBoundaryRule{
{
AvailableResource: "test1",
Expand All @@ -70,16 +76,16 @@ func TestNewTokenProvider(t *testing.T) {
if err != nil {
t.Fatalf("NewTokenProvider() = %v", err)
}
tok, err := tp.Token(context.Background())
tok, err := creds.Token(context.Background())
if err != nil {
t.Fatalf("NewDownscopedTokenSource failed with error: %v", err)
t.Fatalf("Token failed with error: %v", err)
}
if want := "fake_token"; tok.Value != want {
t.Fatalf("got %v, want %v", tok.Value, want)
}
}

func TestTestNewTokenProvider_Validations(t *testing.T) {
func TestTestNewCredentials_Validations(t *testing.T) {
tests := []struct {
name string
opts *Options
Expand All @@ -95,27 +101,27 @@ func TestTestNewTokenProvider_Validations(t *testing.T) {
{
name: "no rules",
opts: &Options{
BaseProvider: staticTokenProvider("token_base"),
Credentials: staticCredentials("token_base"),
},
},
{
name: "too many rules",
opts: &Options{
BaseProvider: staticTokenProvider("token_base"),
Rules: []AccessBoundaryRule{{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}},
Credentials: staticCredentials("token_base"),
Rules: []AccessBoundaryRule{{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}},
},
},
{
name: "no resource",
opts: &Options{
BaseProvider: staticTokenProvider("token_base"),
Rules: []AccessBoundaryRule{{}},
Credentials: staticCredentials("token_base"),
Rules: []AccessBoundaryRule{{}},
},
},
{
name: "no perm",
opts: &Options{
BaseProvider: staticTokenProvider("token_base"),
Credentials: staticCredentials("token_base"),
Rules: []AccessBoundaryRule{{
AvailableResource: "resource",
}},
Expand All @@ -124,7 +130,7 @@ func TestTestNewTokenProvider_Validations(t *testing.T) {
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if _, err := NewTokenProvider(test.opts); err == nil {
if _, err := NewCredentials(test.opts); err == nil {
t.Fatal("want non-nil err")
}
})
Expand Down
6 changes: 3 additions & 3 deletions auth/downscope/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"cloud.google.com/go/auth/downscope"
)

func ExampleNewTokenProvider() {
func ExampleNewCredentials() {
// This shows how to generate a downscoped token. This code would be run on
// the token broker, which holds the root token used to generate the
// downscoped token.
Expand All @@ -43,13 +43,13 @@ func ExampleNewTokenProvider() {
baseProvider, err := credentials.DetectDefault(&credentials.DetectOptions{
Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"},
})
tp, err := downscope.NewTokenProvider(&downscope.Options{BaseProvider: baseProvider, Rules: accessBoundary})
creds, err := downscope.NewCredentials(&downscope.Options{Credentials: baseProvider, Rules: accessBoundary})
if err != nil {
fmt.Printf("failed to generate downscoped token provider: %v", err)
return
}

tok, err := tp.Token(ctx)
tok, err := creds.Token(ctx)
if err != nil {
fmt.Printf("failed to generate token: %v", err)
return
Expand Down
8 changes: 4 additions & 4 deletions auth/downscope/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,17 @@ func TestDownscopedToken(t *testing.T) {
}
}

func testDownscopedToken(t *testing.T, rule downscope.AccessBoundaryRule, objectName string, tp auth.TokenProvider) error {
func testDownscopedToken(t *testing.T, rule downscope.AccessBoundaryRule, objectName string, creds *auth.Credentials) error {
t.Helper()
ctx := context.Background()
tp, err := downscope.NewTokenProvider(&downscope.Options{BaseProvider: tp, Rules: []downscope.AccessBoundaryRule{rule}})
creds, err := downscope.NewCredentials(&downscope.Options{Credentials: creds, Rules: []downscope.AccessBoundaryRule{rule}})
if err != nil {
return fmt.Errorf("downscope.NewTokenProvider() = %v", err)
return fmt.Errorf("downscope.NewCredentials() = %v", err)
}

ctx, cancel := context.WithTimeout(ctx, time.Second*30)
defer cancel()
client := testgcs.NewClient(tp)
client := testgcs.NewClient(creds)
resp, err := client.DownloadObject(ctx, bucket, objectName)
if err != nil {
return err
Expand Down
4 changes: 3 additions & 1 deletion auth/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ func ExampleNew2LOTokenProvider() {
log.Fatal(err)
}
client, err := httptransport.NewClient(&httptransport.Options{
TokenProvider: tp,
Credentials: auth.NewCredentials(&auth.CredentialsOptions{
TokenProvider: tp,
}),
})
if err != nil {
log.Fatal(err)
Expand Down
9 changes: 6 additions & 3 deletions auth/grpctransport/dial_socketopt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"testing"
"time"

"cloud.google.com/go/auth"
"google.golang.org/grpc"
)

Expand Down Expand Up @@ -107,9 +108,11 @@ func TestDialWithDirectPathEnabled(t *testing.T) {
})

pool, err := Dial(ctx, true, &Options{
TokenProvider: staticTP("hey"),
GRPCDialOpts: []grpc.DialOption{userDialer},
Endpoint: "example.google.com:443",
Credentials: auth.NewCredentials(&auth.CredentialsOptions{
TokenProvider: staticTP("hey"),
}),
GRPCDialOpts: []grpc.DialOption{userDialer},
Endpoint: "example.google.com:443",
InternalOptions: &InternalOptions{
EnableDirectPath: true,
},
Expand Down
4 changes: 2 additions & 2 deletions auth/grpctransport/directpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ func isDirectPathXdsUsed(o *Options) bool {
// configureDirectPath returns some dial options and an endpoint to use if the
// configuration allows the use of direct path. If it does not the provided
// grpcOpts and endpoint are returned.
func configureDirectPath(grpcOpts []grpc.DialOption, opts *Options, endpoint string, creds auth.TokenProvider) ([]grpc.DialOption, string) {
func configureDirectPath(grpcOpts []grpc.DialOption, opts *Options, endpoint string, creds *auth.Credentials) ([]grpc.DialOption, string) {
if isDirectPathEnabled(endpoint, opts) && metadata.OnGCE() && isTokenProviderDirectPathCompatible(creds, opts) {
// Overwrite all of the previously specific DialOptions, DirectPath uses its own set of credentials and certificates.
grpcOpts = []grpc.DialOption{
grpc.WithCredentialsBundle(grpcgoogle.NewDefaultCredentialsWithOptions(grpcgoogle.DefaultCredentialsOptions{PerRPCCreds: &grpcTokenProvider{TokenProvider: creds}}))}
grpc.WithCredentialsBundle(grpcgoogle.NewDefaultCredentialsWithOptions(grpcgoogle.DefaultCredentialsOptions{PerRPCCreds: &grpcCredentialProvider{creds: creds}}))}
if timeoutDialerOption != nil {
grpcOpts = append(grpcOpts, timeoutDialerOption)
}
Expand Down
35 changes: 17 additions & 18 deletions auth/grpctransport/grpctransport.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ type Options struct {
// PoolSize is specifies how many connections to balance between when making
// requests. If unset or less than 1, the value defaults to 1.
PoolSize int
// TokenProvider specifies the provider used to add Authorization metadata
// Credentials specifies the provider used to add Authorization metadata
codyoss marked this conversation as resolved.
Show resolved Hide resolved
// to all requests. If set DetectOpts are ignored.
TokenProvider auth.TokenProvider
Credentials *auth.Credentials
// DetectOpts configures settings for detect Application Default
// Credentials.
DetectOpts *credentials.DetectOptions
Expand All @@ -90,7 +90,7 @@ func (o *Options) validate() error {
if o == nil {
return errors.New("grpctransport: opts required to be non-nil")
}
hasCreds := o.TokenProvider != nil ||
hasCreds := o.Credentials != nil ||
(o.DetectOpts != nil && len(o.DetectOpts.CredentialsJSON) > 0) ||
(o.DetectOpts != nil && o.DetectOpts.CredentialsFile != "")
if o.DisableAuthentication && hasCreds {
Expand Down Expand Up @@ -204,9 +204,8 @@ func dial(ctx context.Context, secure bool, opts *Options) (*grpc.ClientConn, er
if err != nil {
return nil, err
}
var tp auth.TokenProvider = creds
if opts.TokenProvider != nil {
tp = opts.TokenProvider
if opts.Credentials != nil {
creds = opts.Credentials
}

qp, err := creds.QuotaProjectID(ctx)
Expand All @@ -221,9 +220,9 @@ func dial(ctx context.Context, secure bool, opts *Options) (*grpc.ClientConn, er
}

grpcOpts = append(grpcOpts,
grpc.WithPerRPCCredentials(&grpcTokenProvider{
TokenProvider: tp,
metadata: metadata,
grpc.WithPerRPCCredentials(&grpcCredentialProvider{
creds: creds,
metadata: metadata,
}),
)

Expand All @@ -240,37 +239,37 @@ func dial(ctx context.Context, secure bool, opts *Options) (*grpc.ClientConn, er
return grpc.DialContext(ctx, endpoint, grpcOpts...)
}

// grpcTokenProvider satisfies https://pkg.go.dev/google.golang.org/grpc/credentials#PerRPCCredentials.
type grpcTokenProvider struct {
auth.TokenProvider
// grpcCredentialProvider satisfies https://pkg.go.dev/google.golang.org/grpc/credentials#PerRPCCredentials.
codyoss marked this conversation as resolved.
Show resolved Hide resolved
type grpcCredentialProvider struct {
creds *auth.Credentials

secure bool

// Additional metadata attached as headers.
metadata map[string]string
}

func (tp *grpcTokenProvider) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
token, err := tp.Token(ctx)
func (c *grpcCredentialProvider) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
token, err := c.creds.Token(ctx)
if err != nil {
return nil, err
}
if tp.secure {
if c.secure {
ri, _ := grpccreds.RequestInfoFromContext(ctx)
if err = grpccreds.CheckSecurityLevel(ri.AuthInfo, grpccreds.PrivacyAndIntegrity); err != nil {
return nil, fmt.Errorf("unable to transfer TokenProvider PerRPCCredentials: %v", err)
return nil, fmt.Errorf("unable to transfer credentials PerRPCCredentials: %v", err)
}
}
metadata := map[string]string{
"authorization": token.Type + " " + token.Value,
}
for k, v := range tp.metadata {
for k, v := range c.metadata {
metadata[k] = v
}
return metadata, nil
}

func (tp *grpcTokenProvider) RequireTransportSecurity() bool {
func (tp *grpcCredentialProvider) RequireTransportSecurity() bool {
return tp.secure
}

Expand Down
4 changes: 3 additions & 1 deletion auth/grpctransport/grpctransport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ func TestDial_FailsValidation(t *testing.T) {
name: "has creds with disable options, tp",
opts: &Options{
DisableAuthentication: true,
TokenProvider: staticTP("fakeToken"),
Credentials: auth.NewCredentials(&auth.CredentialsOptions{
TokenProvider: staticTP("fakeToken"),
}),
},
},
{
Expand Down