google.golang.org/grpc@v1.72.2/credentials/google/google.go (about)

     1  /*
     2   *
     3   * Copyright 2018 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  // Package google defines credentials for google cloud services.
    20  package google
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  
    26  	"google.golang.org/grpc/credentials"
    27  	"google.golang.org/grpc/credentials/alts"
    28  	"google.golang.org/grpc/credentials/oauth"
    29  	"google.golang.org/grpc/grpclog"
    30  	"google.golang.org/grpc/internal"
    31  )
    32  
    33  const defaultCloudPlatformScope = "https://www.googleapis.com/auth/cloud-platform"
    34  
    35  var logger = grpclog.Component("credentials")
    36  
    37  // DefaultCredentialsOptions constructs options to build DefaultCredentials.
    38  type DefaultCredentialsOptions struct {
    39  	// PerRPCCreds is a per RPC credentials that is passed to a bundle.
    40  	PerRPCCreds credentials.PerRPCCredentials
    41  	// ALTSPerRPCCreds is a per RPC credentials that, if specified, will
    42  	// supercede PerRPCCreds above for and only for ALTS connections.
    43  	ALTSPerRPCCreds credentials.PerRPCCredentials
    44  }
    45  
    46  // NewDefaultCredentialsWithOptions returns a credentials bundle that is
    47  // configured to work with google services.
    48  //
    49  // This API is experimental.
    50  func NewDefaultCredentialsWithOptions(opts DefaultCredentialsOptions) credentials.Bundle {
    51  	if opts.PerRPCCreds == nil {
    52  		var err error
    53  		// If the ADC ends up being Compute Engine Credentials, this context
    54  		// won't be used. Otherwise, the context dictates all the subsequent
    55  		// token requests via HTTP. So we cannot have any deadline or timeout.
    56  		opts.PerRPCCreds, err = newADC(context.TODO())
    57  		if err != nil {
    58  			logger.Warningf("NewDefaultCredentialsWithOptions: failed to create application oauth: %v", err)
    59  		}
    60  	}
    61  	if opts.ALTSPerRPCCreds != nil {
    62  		opts.PerRPCCreds = &dualPerRPCCreds{
    63  			perRPCCreds:     opts.PerRPCCreds,
    64  			altsPerRPCCreds: opts.ALTSPerRPCCreds,
    65  		}
    66  	}
    67  	c := &creds{opts: opts}
    68  	bundle, err := c.NewWithMode(internal.CredsBundleModeFallback)
    69  	if err != nil {
    70  		logger.Warningf("NewDefaultCredentialsWithOptions: failed to create new creds: %v", err)
    71  	}
    72  	return bundle
    73  }
    74  
    75  // NewDefaultCredentials returns a credentials bundle that is configured to work
    76  // with google services.
    77  //
    78  // This API is experimental.
    79  func NewDefaultCredentials() credentials.Bundle {
    80  	return NewDefaultCredentialsWithOptions(DefaultCredentialsOptions{})
    81  }
    82  
    83  // NewComputeEngineCredentials returns a credentials bundle that is configured to work
    84  // with google services. This API must only be used when running on GCE. Authentication configured
    85  // by this API represents the GCE VM's default service account.
    86  //
    87  // This API is experimental.
    88  func NewComputeEngineCredentials() credentials.Bundle {
    89  	return NewDefaultCredentialsWithOptions(DefaultCredentialsOptions{
    90  		PerRPCCreds: oauth.NewComputeEngine(),
    91  	})
    92  }
    93  
    94  // creds implements credentials.Bundle.
    95  type creds struct {
    96  	opts DefaultCredentialsOptions
    97  
    98  	// Supported modes are defined in internal/internal.go.
    99  	mode string
   100  	// The active transport credentials associated with this bundle.
   101  	transportCreds credentials.TransportCredentials
   102  	// The active per RPC credentials associated with this bundle.
   103  	perRPCCreds credentials.PerRPCCredentials
   104  }
   105  
   106  func (c *creds) TransportCredentials() credentials.TransportCredentials {
   107  	return c.transportCreds
   108  }
   109  
   110  func (c *creds) PerRPCCredentials() credentials.PerRPCCredentials {
   111  	if c == nil {
   112  		return nil
   113  	}
   114  	return c.perRPCCreds
   115  }
   116  
   117  var (
   118  	newTLS = func() credentials.TransportCredentials {
   119  		return credentials.NewTLS(nil)
   120  	}
   121  	newALTS = func() credentials.TransportCredentials {
   122  		return alts.NewClientCreds(alts.DefaultClientOptions())
   123  	}
   124  	newADC = func(ctx context.Context) (credentials.PerRPCCredentials, error) {
   125  		return oauth.NewApplicationDefault(ctx, defaultCloudPlatformScope)
   126  	}
   127  )
   128  
   129  // NewWithMode should make a copy of Bundle, and switch mode. Modifying the
   130  // existing Bundle may cause races.
   131  func (c *creds) NewWithMode(mode string) (credentials.Bundle, error) {
   132  	newCreds := &creds{
   133  		opts: c.opts,
   134  		mode: mode,
   135  	}
   136  
   137  	// Create transport credentials.
   138  	switch mode {
   139  	case internal.CredsBundleModeFallback:
   140  		newCreds.transportCreds = newClusterTransportCreds(newTLS(), newALTS())
   141  	case internal.CredsBundleModeBackendFromBalancer, internal.CredsBundleModeBalancer:
   142  		// Only the clients can use google default credentials, so we only need
   143  		// to create new ALTS client creds here.
   144  		newCreds.transportCreds = newALTS()
   145  	default:
   146  		return nil, fmt.Errorf("unsupported mode: %v", mode)
   147  	}
   148  
   149  	if mode == internal.CredsBundleModeFallback || mode == internal.CredsBundleModeBackendFromBalancer {
   150  		newCreds.perRPCCreds = newCreds.opts.PerRPCCreds
   151  	}
   152  
   153  	return newCreds, nil
   154  }
   155  
   156  // dualPerRPCCreds implements credentials.PerRPCCredentials by embedding the
   157  // fallback PerRPCCredentials and the ALTS one. It pickes one of them based on
   158  // the channel type.
   159  type dualPerRPCCreds struct {
   160  	perRPCCreds     credentials.PerRPCCredentials
   161  	altsPerRPCCreds credentials.PerRPCCredentials
   162  }
   163  
   164  func (d *dualPerRPCCreds) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
   165  	ri, ok := credentials.RequestInfoFromContext(ctx)
   166  	if !ok {
   167  		return nil, fmt.Errorf("request info not found from context")
   168  	}
   169  	if authType := ri.AuthInfo.AuthType(); authType == "alts" {
   170  		return d.altsPerRPCCreds.GetRequestMetadata(ctx, uri...)
   171  	}
   172  	// This ensures backward compatibility even if authType is not "tls".
   173  	return d.perRPCCreds.GetRequestMetadata(ctx, uri...)
   174  }
   175  
   176  func (d *dualPerRPCCreds) RequireTransportSecurity() bool {
   177  	return d.altsPerRPCCreds.RequireTransportSecurity() || d.perRPCCreds.RequireTransportSecurity()
   178  }