...

Source file src/google.golang.org/api/internal/settings.go

Documentation: google.golang.org/api/internal

     1  // Copyright 2017 Google LLC.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package internal supports the options and transport packages.
     6  package internal
     7  
     8  import (
     9  	"crypto/tls"
    10  	"errors"
    11  	"net/http"
    12  	"os"
    13  	"strconv"
    14  	"time"
    15  
    16  	"cloud.google.com/go/auth"
    17  	"golang.org/x/oauth2"
    18  	"golang.org/x/oauth2/google"
    19  	"google.golang.org/api/internal/impersonate"
    20  	"google.golang.org/grpc"
    21  )
    22  
    23  const (
    24  	newAuthLibEnvVar        = "GOOGLE_API_GO_EXPERIMENTAL_ENABLE_NEW_AUTH_LIB"
    25  	newAuthLibDisabledEnVar = "GOOGLE_API_GO_EXPERIMENTAL_DISABLE_NEW_AUTH_LIB"
    26  	universeDomainEnvVar    = "GOOGLE_CLOUD_UNIVERSE_DOMAIN"
    27  	defaultUniverseDomain   = "googleapis.com"
    28  )
    29  
    30  // DialSettings holds information needed to establish a connection with a
    31  // Google API service.
    32  type DialSettings struct {
    33  	Endpoint                      string
    34  	DefaultEndpoint               string
    35  	DefaultEndpointTemplate       string
    36  	DefaultMTLSEndpoint           string
    37  	Scopes                        []string
    38  	DefaultScopes                 []string
    39  	EnableJwtWithScope            bool
    40  	TokenSource                   oauth2.TokenSource
    41  	Credentials                   *google.Credentials
    42  	CredentialsFile               string // if set, Token Source is ignored.
    43  	CredentialsJSON               []byte
    44  	InternalCredentials           *google.Credentials
    45  	UserAgent                     string
    46  	APIKey                        string
    47  	Audiences                     []string
    48  	DefaultAudience               string
    49  	HTTPClient                    *http.Client
    50  	GRPCDialOpts                  []grpc.DialOption
    51  	GRPCConn                      *grpc.ClientConn
    52  	GRPCConnPool                  ConnPool
    53  	GRPCConnPoolSize              int
    54  	NoAuth                        bool
    55  	TelemetryDisabled             bool
    56  	ClientCertSource              func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
    57  	CustomClaims                  map[string]interface{}
    58  	SkipValidation                bool
    59  	ImpersonationConfig           *impersonate.Config
    60  	EnableDirectPath              bool
    61  	EnableDirectPathXds           bool
    62  	AllowNonDefaultServiceAccount bool
    63  	DefaultUniverseDomain         string
    64  	UniverseDomain                string
    65  	// Google API system parameters. For more information please read:
    66  	// https://cloud.google.com/apis/docs/system-parameters
    67  	QuotaProject  string
    68  	RequestReason string
    69  
    70  	// New Auth library Options
    71  	AuthCredentials      *auth.Credentials
    72  	EnableNewAuthLibrary bool
    73  }
    74  
    75  // GetScopes returns the user-provided scopes, if set, or else falls back to the
    76  // default scopes.
    77  func (ds *DialSettings) GetScopes() []string {
    78  	if len(ds.Scopes) > 0 {
    79  		return ds.Scopes
    80  	}
    81  	return ds.DefaultScopes
    82  }
    83  
    84  // GetAudience returns the user-provided audience, if set, or else falls back to the default audience.
    85  func (ds *DialSettings) GetAudience() string {
    86  	if ds.HasCustomAudience() {
    87  		return ds.Audiences[0]
    88  	}
    89  	return ds.DefaultAudience
    90  }
    91  
    92  // HasCustomAudience returns true if a custom audience is provided by users.
    93  func (ds *DialSettings) HasCustomAudience() bool {
    94  	return len(ds.Audiences) > 0
    95  }
    96  
    97  // IsNewAuthLibraryEnabled returns true if the new auth library should be used.
    98  func (ds *DialSettings) IsNewAuthLibraryEnabled() bool {
    99  	// Disabled env is for future rollouts to make sure there is a way to easily
   100  	// disable this behaviour once we switch in on by default.
   101  	if b, err := strconv.ParseBool(os.Getenv(newAuthLibDisabledEnVar)); err == nil && b {
   102  		return false
   103  	}
   104  	if ds.EnableNewAuthLibrary {
   105  		return true
   106  	}
   107  	if b, err := strconv.ParseBool(os.Getenv(newAuthLibEnvVar)); err == nil {
   108  		return b
   109  	}
   110  	return false
   111  }
   112  
   113  // Validate reports an error if ds is invalid.
   114  func (ds *DialSettings) Validate() error {
   115  	if ds.SkipValidation {
   116  		return nil
   117  	}
   118  	hasCreds := ds.APIKey != "" || ds.TokenSource != nil || ds.CredentialsFile != "" || ds.Credentials != nil
   119  	if ds.NoAuth && hasCreds {
   120  		return errors.New("options.WithoutAuthentication is incompatible with any option that provides credentials")
   121  	}
   122  	// Credentials should not appear with other options.
   123  	// We currently allow TokenSource and CredentialsFile to coexist.
   124  	// TODO(jba): make TokenSource & CredentialsFile an error (breaking change).
   125  	nCreds := 0
   126  	if ds.Credentials != nil {
   127  		nCreds++
   128  	}
   129  	if ds.CredentialsJSON != nil {
   130  		nCreds++
   131  	}
   132  	if ds.CredentialsFile != "" {
   133  		nCreds++
   134  	}
   135  	if ds.APIKey != "" {
   136  		nCreds++
   137  	}
   138  	if ds.TokenSource != nil {
   139  		nCreds++
   140  	}
   141  	if len(ds.Scopes) > 0 && len(ds.Audiences) > 0 {
   142  		return errors.New("WithScopes is incompatible with WithAudience")
   143  	}
   144  	// Accept only one form of credentials, except we allow TokenSource and CredentialsFile for backwards compatibility.
   145  	if nCreds > 1 && !(nCreds == 2 && ds.TokenSource != nil && ds.CredentialsFile != "") {
   146  		return errors.New("multiple credential options provided")
   147  	}
   148  	if ds.GRPCConn != nil && ds.GRPCConnPool != nil {
   149  		return errors.New("WithGRPCConn is incompatible with WithConnPool")
   150  	}
   151  	if ds.HTTPClient != nil && ds.GRPCConnPool != nil {
   152  		return errors.New("WithHTTPClient is incompatible with WithConnPool")
   153  	}
   154  	if ds.HTTPClient != nil && ds.GRPCConn != nil {
   155  		return errors.New("WithHTTPClient is incompatible with WithGRPCConn")
   156  	}
   157  	if ds.HTTPClient != nil && ds.GRPCDialOpts != nil {
   158  		return errors.New("WithHTTPClient is incompatible with gRPC dial options")
   159  	}
   160  	if ds.HTTPClient != nil && ds.QuotaProject != "" {
   161  		return errors.New("WithHTTPClient is incompatible with QuotaProject")
   162  	}
   163  	if ds.HTTPClient != nil && ds.RequestReason != "" {
   164  		return errors.New("WithHTTPClient is incompatible with RequestReason")
   165  	}
   166  	if ds.HTTPClient != nil && ds.ClientCertSource != nil {
   167  		return errors.New("WithHTTPClient is incompatible with WithClientCertSource")
   168  	}
   169  	if ds.ClientCertSource != nil && (ds.GRPCConn != nil || ds.GRPCConnPool != nil || ds.GRPCConnPoolSize != 0 || ds.GRPCDialOpts != nil) {
   170  		return errors.New("WithClientCertSource is currently only supported for HTTP. gRPC settings are incompatible")
   171  	}
   172  	if ds.ImpersonationConfig != nil && len(ds.ImpersonationConfig.Scopes) == 0 && len(ds.Scopes) == 0 {
   173  		return errors.New("WithImpersonatedCredentials requires scopes being provided")
   174  	}
   175  	return nil
   176  }
   177  
   178  // GetDefaultUniverseDomain returns the Google default universe domain
   179  // ("googleapis.com").
   180  func (ds *DialSettings) GetDefaultUniverseDomain() string {
   181  	return defaultUniverseDomain
   182  }
   183  
   184  // GetUniverseDomain returns the default service domain for a given Cloud
   185  // universe, with the following precedence:
   186  //
   187  // 1. A non-empty option.WithUniverseDomain.
   188  // 2. A non-empty environment variable GOOGLE_CLOUD_UNIVERSE_DOMAIN.
   189  // 3. The default value "googleapis.com".
   190  func (ds *DialSettings) GetUniverseDomain() string {
   191  	if ds.UniverseDomain != "" {
   192  		return ds.UniverseDomain
   193  	}
   194  	if envUD := os.Getenv(universeDomainEnvVar); envUD != "" {
   195  		return envUD
   196  	}
   197  	return defaultUniverseDomain
   198  }
   199  
   200  // IsUniverseDomainGDU returns true if the universe domain is the default Google
   201  // universe ("googleapis.com").
   202  func (ds *DialSettings) IsUniverseDomainGDU() bool {
   203  	return ds.GetUniverseDomain() == defaultUniverseDomain
   204  }
   205  
   206  // GetUniverseDomain returns the default service domain for a given Cloud
   207  // universe, from google.Credentials, for comparison with the value returned by
   208  // (*DialSettings).GetUniverseDomain. This wrapper function should be removed
   209  // to close https://github.com/googleapis/google-api-go-client/issues/2399.
   210  func GetUniverseDomain(creds *google.Credentials) (string, error) {
   211  	timer := time.NewTimer(time.Second)
   212  	defer timer.Stop()
   213  	errors := make(chan error)
   214  	results := make(chan string)
   215  
   216  	go func() {
   217  		result, err := creds.GetUniverseDomain()
   218  		if err != nil {
   219  			errors <- err
   220  			return
   221  		}
   222  		results <- result
   223  	}()
   224  
   225  	select {
   226  	case <-errors:
   227  		// An error that is returned before the timer expires is likely to be
   228  		// connection refused. Temporarily (2024-03-21) return the GDU domain.
   229  		return defaultUniverseDomain, nil
   230  	case res := <-results:
   231  		return res, nil
   232  	case <-timer.C: // Timer is expired.
   233  		// If err or res was not returned, it means that creds.GetUniverseDomain()
   234  		// did not complete in 1s. Assume that MDS is likely never responding to
   235  		// the endpoint and will timeout. This is the source of issues such as
   236  		// https://github.com/googleapis/google-cloud-go/issues/9350.
   237  		// Temporarily (2024-02-02) return the GDU domain. Restore the original
   238  		// calls to creds.GetUniverseDomain() in grpc/dial.go and http/dial.go
   239  		// and remove this method to close
   240  		// https://github.com/googleapis/google-api-go-client/issues/2399.
   241  		return defaultUniverseDomain, nil
   242  	}
   243  }
   244  

View as plain text