| /* |
| * |
| * Copyright 2018 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| // Package google defines credentials for google cloud services. |
| package google |
| |
| import ( |
| "context" |
| "fmt" |
| |
| "google.golang.org/grpc/credentials" |
| "google.golang.org/grpc/credentials/alts" |
| "google.golang.org/grpc/credentials/oauth" |
| "google.golang.org/grpc/grpclog" |
| "google.golang.org/grpc/internal" |
| ) |
| |
| const defaultCloudPlatformScope = "https://www.googleapis.com/auth/cloud-platform" |
| |
| var logger = grpclog.Component("credentials") |
| |
| // DefaultCredentialsOptions constructs options to build DefaultCredentials. |
| type DefaultCredentialsOptions struct { |
| // PerRPCCreds is a per RPC credentials that is passed to a bundle. |
| PerRPCCreds credentials.PerRPCCredentials |
| // ALTSPerRPCCreds is a per RPC credentials that, if specified, will |
| // supercede PerRPCCreds above for and only for ALTS connections. |
| ALTSPerRPCCreds credentials.PerRPCCredentials |
| } |
| |
| // NewDefaultCredentialsWithOptions returns a credentials bundle that is |
| // configured to work with google services. |
| // |
| // This API is experimental. |
| func NewDefaultCredentialsWithOptions(opts DefaultCredentialsOptions) credentials.Bundle { |
| if opts.PerRPCCreds == nil { |
| var err error |
| // If the ADC ends up being Compute Engine Credentials, this context |
| // won't be used. Otherwise, the context dictates all the subsequent |
| // token requests via HTTP. So we cannot have any deadline or timeout. |
| opts.PerRPCCreds, err = newADC(context.TODO()) |
| if err != nil { |
| logger.Warningf("NewDefaultCredentialsWithOptions: failed to create application oauth: %v", err) |
| } |
| } |
| if opts.ALTSPerRPCCreds != nil { |
| opts.PerRPCCreds = &dualPerRPCCreds{ |
| perRPCCreds: opts.PerRPCCreds, |
| altsPerRPCCreds: opts.ALTSPerRPCCreds, |
| } |
| } |
| c := &creds{opts: opts} |
| bundle, err := c.NewWithMode(internal.CredsBundleModeFallback) |
| if err != nil { |
| logger.Warningf("NewDefaultCredentialsWithOptions: failed to create new creds: %v", err) |
| } |
| return bundle |
| } |
| |
| // NewDefaultCredentials returns a credentials bundle that is configured to work |
| // with google services. |
| // |
| // This API is experimental. |
| func NewDefaultCredentials() credentials.Bundle { |
| return NewDefaultCredentialsWithOptions(DefaultCredentialsOptions{}) |
| } |
| |
| // NewComputeEngineCredentials returns a credentials bundle that is configured to work |
| // with google services. This API must only be used when running on GCE. Authentication configured |
| // by this API represents the GCE VM's default service account. |
| // |
| // This API is experimental. |
| func NewComputeEngineCredentials() credentials.Bundle { |
| return NewDefaultCredentialsWithOptions(DefaultCredentialsOptions{ |
| PerRPCCreds: oauth.NewComputeEngine(), |
| }) |
| } |
| |
| // creds implements credentials.Bundle. |
| type creds struct { |
| opts DefaultCredentialsOptions |
| |
| // Supported modes are defined in internal/internal.go. |
| mode string |
| // The active transport credentials associated with this bundle. |
| transportCreds credentials.TransportCredentials |
| // The active per RPC credentials associated with this bundle. |
| perRPCCreds credentials.PerRPCCredentials |
| } |
| |
| func (c *creds) TransportCredentials() credentials.TransportCredentials { |
| return c.transportCreds |
| } |
| |
| func (c *creds) PerRPCCredentials() credentials.PerRPCCredentials { |
| if c == nil { |
| return nil |
| } |
| return c.perRPCCreds |
| } |
| |
| var ( |
| newTLS = func() credentials.TransportCredentials { |
| return credentials.NewTLS(nil) |
| } |
| newALTS = func() credentials.TransportCredentials { |
| return alts.NewClientCreds(alts.DefaultClientOptions()) |
| } |
| newADC = func(ctx context.Context) (credentials.PerRPCCredentials, error) { |
| return oauth.NewApplicationDefault(ctx, defaultCloudPlatformScope) |
| } |
| ) |
| |
| // NewWithMode should make a copy of Bundle, and switch mode. Modifying the |
| // existing Bundle may cause races. |
| func (c *creds) NewWithMode(mode string) (credentials.Bundle, error) { |
| newCreds := &creds{ |
| opts: c.opts, |
| mode: mode, |
| } |
| |
| // Create transport credentials. |
| switch mode { |
| case internal.CredsBundleModeFallback: |
| newCreds.transportCreds = newClusterTransportCreds(newTLS(), newALTS()) |
| case internal.CredsBundleModeBackendFromBalancer, internal.CredsBundleModeBalancer: |
| // Only the clients can use google default credentials, so we only need |
| // to create new ALTS client creds here. |
| newCreds.transportCreds = newALTS() |
| default: |
| return nil, fmt.Errorf("unsupported mode: %v", mode) |
| } |
| |
| if mode == internal.CredsBundleModeFallback || mode == internal.CredsBundleModeBackendFromBalancer { |
| newCreds.perRPCCreds = newCreds.opts.PerRPCCreds |
| } |
| |
| return newCreds, nil |
| } |
| |
| // dualPerRPCCreds implements credentials.PerRPCCredentials by embedding the |
| // fallback PerRPCCredentials and the ALTS one. It pickes one of them based on |
| // the channel type. |
| type dualPerRPCCreds struct { |
| perRPCCreds credentials.PerRPCCredentials |
| altsPerRPCCreds credentials.PerRPCCredentials |
| } |
| |
| func (d *dualPerRPCCreds) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { |
| ri, ok := credentials.RequestInfoFromContext(ctx) |
| if !ok { |
| return nil, fmt.Errorf("request info not found from context") |
| } |
| if authType := ri.AuthInfo.AuthType(); authType == "alts" { |
| return d.altsPerRPCCreds.GetRequestMetadata(ctx, uri...) |
| } |
| // This ensures backward compatibility even if authType is not "tls". |
| return d.perRPCCreds.GetRequestMetadata(ctx, uri...) |
| } |
| |
| func (d *dualPerRPCCreds) RequireTransportSecurity() bool { |
| return d.altsPerRPCCreds.RequireTransportSecurity() || d.perRPCCreds.RequireTransportSecurity() |
| } |