option.go raw

   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 option contains options for Google API clients.
   6  package option
   7  
   8  import (
   9  	"crypto/tls"
  10  	"log/slog"
  11  	"net/http"
  12  
  13  	"cloud.google.com/go/auth"
  14  	"golang.org/x/oauth2"
  15  	"golang.org/x/oauth2/google"
  16  	"google.golang.org/api/internal"
  17  	"google.golang.org/api/internal/credentialstype"
  18  	"google.golang.org/api/internal/impersonate"
  19  	"google.golang.org/grpc"
  20  )
  21  
  22  // CredentialsType specifies the type of JSON credentials being provided
  23  // to a loading function such as [WithAuthCredentialsFile] or
  24  // [WithAuthCredentialsJSON].
  25  type CredentialsType = credentialstype.CredType
  26  
  27  const (
  28  	// ServiceAccount represents a service account file type.
  29  	ServiceAccount = credentialstype.ServiceAccount
  30  	// AuthorizedUser represents an authorized user credentials file type.
  31  	AuthorizedUser = credentialstype.AuthorizedUser
  32  	// ImpersonatedServiceAccount represents an impersonated service account file type.
  33  	//
  34  	// IMPORTANT:
  35  	// This credential type does not validate the credential configuration. A security
  36  	// risk occurs when a credential configuration configured with malicious urls
  37  	// is used.
  38  	// You should validate credential configurations provided by untrusted sources.
  39  	// See [Security requirements when using credential configurations from an external
  40  	// source] https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
  41  	// for more details.
  42  	ImpersonatedServiceAccount = credentialstype.ImpersonatedServiceAccount
  43  	// ExternalAccount represents an external account file type.
  44  	//
  45  	// IMPORTANT:
  46  	// This credential type does not validate the credential configuration. A security
  47  	// risk occurs when a credential configuration configured with malicious urls
  48  	// is used.
  49  	// You should validate credential configurations provided by untrusted sources.
  50  	// See [Security requirements when using credential configurations from an external
  51  	// source] https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
  52  	// for more details.
  53  	ExternalAccount = credentialstype.ExternalAccount
  54  )
  55  
  56  // A ClientOption is an option for a Google API client.
  57  type ClientOption interface {
  58  	Apply(*internal.DialSettings)
  59  }
  60  
  61  // WithTokenSource returns a ClientOption that specifies an OAuth2 token
  62  // source to be used as the basis for authentication.
  63  func WithTokenSource(s oauth2.TokenSource) ClientOption {
  64  	return withTokenSource{s}
  65  }
  66  
  67  type withTokenSource struct{ ts oauth2.TokenSource }
  68  
  69  func (w withTokenSource) Apply(o *internal.DialSettings) {
  70  	o.TokenSource = w.ts
  71  }
  72  
  73  type withCredFile string
  74  
  75  func (w withCredFile) Apply(o *internal.DialSettings) {
  76  	o.CredentialsFile = string(w)
  77  }
  78  
  79  // WithCredentialsFile returns a ClientOption that authenticates
  80  // API calls with the given service account or refresh token JSON
  81  // credentials file.
  82  //
  83  // Deprecated: This function is being deprecated because of a potential security risk.
  84  //
  85  // This function does not validate the credential configuration. The security
  86  // risk occurs when a credential configuration is accepted from a source that
  87  // is not under your control and used without validation on your side.
  88  //
  89  // If you know that you will be loading credential configurations of a
  90  // specific type, it is recommended to use a credential-type-specific
  91  // option function.
  92  // This will ensure that an unexpected credential type with potential for
  93  // malicious intent is not loaded unintentionally. You might still have to do
  94  // validation for certain credential types. Please follow the recommendation
  95  // for that function. For example, if you want to load only service accounts,
  96  // you can use [WithAuthCredentialsFile] with [ServiceAccount]:
  97  //
  98  //	option.WithAuthCredentialsFile(option.ServiceAccount, "/path/to/file.json")
  99  //
 100  // If you are loading your credential configuration from an untrusted source and have
 101  // not mitigated the risks (e.g. by validating the configuration yourself), make
 102  // these changes as soon as possible to prevent security risks to your environment.
 103  //
 104  // Regardless of the function used, it is always your responsibility to validate
 105  // configurations received from external sources.
 106  func WithCredentialsFile(filename string) ClientOption {
 107  	return withCredFile(filename)
 108  }
 109  
 110  // WithAuthCredentialsFile returns a ClientOption that authenticates API calls
 111  // with the given JSON credentials file and credential type.
 112  //
 113  // Important: If you accept a credential configuration (credential
 114  // JSON/File/Stream) from an external source for authentication to Google
 115  // Cloud Platform, you must validate it before providing it to any Google
 116  // API or library. Providing an unvalidated credential configuration to
 117  // Google APIs can compromise the security of your systems and data. For
 118  // more information, refer to [Validate credential configurations from
 119  // external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
 120  func WithAuthCredentialsFile(credType CredentialsType, filename string) ClientOption {
 121  	return withAuthCredentialsFile{
 122  		credsType: credType,
 123  		filename:  filename,
 124  	}
 125  }
 126  
 127  type withAuthCredentialsFile struct {
 128  	credsType CredentialsType
 129  	filename  string
 130  }
 131  
 132  func (w withAuthCredentialsFile) Apply(o *internal.DialSettings) {
 133  	o.AuthCredentialsFile = w.filename
 134  	o.AuthCredentialsType = w.credsType
 135  }
 136  
 137  // WithServiceAccountFile returns a ClientOption that uses a Google service
 138  // account credentials file to authenticate.
 139  //
 140  // Important: If you accept a credential configuration (credential
 141  // JSON/File/Stream) from an external source for authentication to Google
 142  // Cloud Platform, you must validate it before providing it to any Google
 143  // API or library. Providing an unvalidated credential configuration to
 144  // Google APIs can compromise the security of your systems and data. For
 145  // more information, refer to [Validate credential configurations from
 146  // external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
 147  //
 148  // Deprecated: Use WithAuthCredentialsFile instead.
 149  func WithServiceAccountFile(filename string) ClientOption {
 150  	return WithAuthCredentialsFile(ServiceAccount, filename)
 151  }
 152  
 153  // WithCredentialsJSON returns a ClientOption that authenticates
 154  // API calls with the given service account or refresh token JSON
 155  // credentials.
 156  //
 157  // Deprecated: This function is being deprecated because of a potential security risk.
 158  //
 159  // This function does not validate the credential configuration. The security
 160  // risk occurs when a credential configuration is accepted from a source that
 161  // is not under your control and used without validation on your side.
 162  //
 163  // If you know that you will be loading credential configurations of a
 164  // specific type, it is recommended to use a credential-type-specific
 165  // option function.
 166  // This will ensure that an unexpected credential type with potential for
 167  // malicious intent is not loaded unintentionally. You might still have to do
 168  // validation for certain credential types. Please follow the recommendation
 169  // for that function. For example, if you want to load only service accounts,
 170  // you can use [WithAuthCredentialsJSON] with [ServiceAccount]:
 171  //
 172  //	option.WithAuthCredentialsJSON(option.ServiceAccount, json)
 173  //
 174  // If you are loading your credential configuration from an untrusted source and have
 175  // not mitigated the risks (e.g. by validating the configuration yourself), make
 176  // these changes as soon as possible to prevent security risks to your environment.
 177  //
 178  // Regardless of the function used, it is always your responsibility to validate
 179  // configurations received from external sources.
 180  func WithCredentialsJSON(p []byte) ClientOption {
 181  	return withCredentialsJSON(p)
 182  }
 183  
 184  type withCredentialsJSON []byte
 185  
 186  func (w withCredentialsJSON) Apply(o *internal.DialSettings) {
 187  	o.CredentialsJSON = make([]byte, len(w))
 188  	copy(o.CredentialsJSON, w)
 189  }
 190  
 191  // WithAuthCredentialsJSON returns a ClientOption that authenticates API calls
 192  // with the given JSON credentials and credential type.
 193  //
 194  // Important: If you accept a credential configuration (credential
 195  // JSON/File/Stream) from an external source for authentication to Google
 196  // Cloud Platform, you must validate it before providing it to any Google
 197  // API or library. Providing an unvalidated credential configuration to
 198  // Google APIs can compromise the security of your systems and data. For
 199  // more information, refer to [Validate credential configurations from
 200  // external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
 201  func WithAuthCredentialsJSON(credType CredentialsType, json []byte) ClientOption {
 202  	return withAuthCredentialsJSON{
 203  		credsType: credType,
 204  		json:      json,
 205  	}
 206  }
 207  
 208  type withAuthCredentialsJSON struct {
 209  	credsType CredentialsType
 210  	json      []byte
 211  }
 212  
 213  func (w withAuthCredentialsJSON) Apply(o *internal.DialSettings) {
 214  	o.AuthCredentialsJSON = w.json
 215  	o.AuthCredentialsType = w.credsType
 216  }
 217  
 218  // WithEndpoint returns a ClientOption that overrides the default endpoint
 219  // to be used for a service. Please note that by default Google APIs only
 220  // accept HTTPS traffic.
 221  //
 222  // For a gRPC client, the port number is typically included in the endpoint.
 223  // Example: "us-central1-speech.googleapis.com:443".
 224  //
 225  // For a REST client, the port number is typically not included. Example:
 226  // "https://speech.googleapis.com".
 227  func WithEndpoint(url string) ClientOption {
 228  	return withEndpoint(url)
 229  }
 230  
 231  type withEndpoint string
 232  
 233  func (w withEndpoint) Apply(o *internal.DialSettings) {
 234  	o.Endpoint = string(w)
 235  }
 236  
 237  // WithScopes returns a ClientOption that overrides the default OAuth2 scopes
 238  // to be used for a service.
 239  //
 240  // If both WithScopes and WithTokenSource are used, scope settings from the
 241  // token source will be used instead.
 242  func WithScopes(scope ...string) ClientOption {
 243  	return withScopes(scope)
 244  }
 245  
 246  type withScopes []string
 247  
 248  func (w withScopes) Apply(o *internal.DialSettings) {
 249  	o.Scopes = make([]string, len(w))
 250  	copy(o.Scopes, w)
 251  }
 252  
 253  // WithUserAgent returns a ClientOption that sets the User-Agent. This option
 254  // is incompatible with the [WithHTTPClient] option. If you wish to provide a
 255  // custom client you will need to add this header via RoundTripper middleware.
 256  func WithUserAgent(ua string) ClientOption {
 257  	return withUA(ua)
 258  }
 259  
 260  type withUA string
 261  
 262  func (w withUA) Apply(o *internal.DialSettings) { o.UserAgent = string(w) }
 263  
 264  // WithHTTPClient returns a ClientOption that specifies the HTTP client to use
 265  // as the basis of communications. This option may only be used with services
 266  // that support HTTP as their communication transport. When used, the
 267  // WithHTTPClient option takes precedent over all other supplied options.
 268  func WithHTTPClient(client *http.Client) ClientOption {
 269  	return withHTTPClient{client}
 270  }
 271  
 272  type withHTTPClient struct{ client *http.Client }
 273  
 274  func (w withHTTPClient) Apply(o *internal.DialSettings) {
 275  	o.HTTPClient = w.client
 276  }
 277  
 278  // WithGRPCConn returns a ClientOption that specifies the gRPC client
 279  // connection to use as the basis of communications. This option may only be
 280  // used with services that support gRPC as their communication transport. When
 281  // used, the WithGRPCConn option takes precedent over all other supplied
 282  // options.
 283  func WithGRPCConn(conn *grpc.ClientConn) ClientOption {
 284  	return withGRPCConn{conn}
 285  }
 286  
 287  type withGRPCConn struct{ conn *grpc.ClientConn }
 288  
 289  func (w withGRPCConn) Apply(o *internal.DialSettings) {
 290  	o.GRPCConn = w.conn
 291  }
 292  
 293  // WithGRPCDialOption returns a ClientOption that appends a new grpc.DialOption
 294  // to an underlying gRPC dial. It does not work with WithGRPCConn.
 295  func WithGRPCDialOption(opt grpc.DialOption) ClientOption {
 296  	return withGRPCDialOption{opt}
 297  }
 298  
 299  type withGRPCDialOption struct{ opt grpc.DialOption }
 300  
 301  func (w withGRPCDialOption) Apply(o *internal.DialSettings) {
 302  	o.GRPCDialOpts = append(o.GRPCDialOpts, w.opt)
 303  }
 304  
 305  // WithGRPCConnectionPool returns a ClientOption that creates a pool of gRPC
 306  // connections that requests will be balanced between.
 307  func WithGRPCConnectionPool(size int) ClientOption {
 308  	return withGRPCConnectionPool(size)
 309  }
 310  
 311  type withGRPCConnectionPool int
 312  
 313  func (w withGRPCConnectionPool) Apply(o *internal.DialSettings) {
 314  	o.GRPCConnPoolSize = int(w)
 315  }
 316  
 317  // WithAPIKey returns a ClientOption that specifies an API key to be used
 318  // as the basis for authentication.
 319  //
 320  // API Keys can only be used for JSON-over-HTTP APIs, including those under
 321  // the import path google.golang.org/api/....
 322  func WithAPIKey(apiKey string) ClientOption {
 323  	return withAPIKey(apiKey)
 324  }
 325  
 326  type withAPIKey string
 327  
 328  func (w withAPIKey) Apply(o *internal.DialSettings) { o.APIKey = string(w) }
 329  
 330  // WithAudiences returns a ClientOption that specifies an audience to be used
 331  // as the audience field ("aud") for the JWT token authentication.
 332  func WithAudiences(audience ...string) ClientOption {
 333  	return withAudiences(audience)
 334  }
 335  
 336  type withAudiences []string
 337  
 338  func (w withAudiences) Apply(o *internal.DialSettings) {
 339  	o.Audiences = make([]string, len(w))
 340  	copy(o.Audiences, w)
 341  }
 342  
 343  // WithoutAuthentication returns a ClientOption that specifies that no
 344  // authentication should be used. It is suitable only for testing and for
 345  // accessing public resources, like public Google Cloud Storage buckets.
 346  // It is an error to provide both WithoutAuthentication and any of WithAPIKey,
 347  // WithTokenSource, WithCredentialsFile or WithServiceAccountFile.
 348  func WithoutAuthentication() ClientOption {
 349  	return withoutAuthentication{}
 350  }
 351  
 352  type withoutAuthentication struct{}
 353  
 354  func (w withoutAuthentication) Apply(o *internal.DialSettings) { o.NoAuth = true }
 355  
 356  // WithQuotaProject returns a ClientOption that specifies the project used
 357  // for quota and billing purposes.
 358  //
 359  // For more information please read:
 360  // https://cloud.google.com/apis/docs/system-parameters
 361  func WithQuotaProject(quotaProject string) ClientOption {
 362  	return withQuotaProject(quotaProject)
 363  }
 364  
 365  type withQuotaProject string
 366  
 367  func (w withQuotaProject) Apply(o *internal.DialSettings) {
 368  	o.QuotaProject = string(w)
 369  }
 370  
 371  // WithRequestReason returns a ClientOption that specifies a reason for
 372  // making the request, which is intended to be recorded in audit logging.
 373  // An example reason would be a support-case ticket number.
 374  //
 375  // For more information please read:
 376  // https://cloud.google.com/apis/docs/system-parameters
 377  func WithRequestReason(requestReason string) ClientOption {
 378  	return withRequestReason(requestReason)
 379  }
 380  
 381  type withRequestReason string
 382  
 383  func (w withRequestReason) Apply(o *internal.DialSettings) {
 384  	o.RequestReason = string(w)
 385  }
 386  
 387  // WithTelemetryDisabled returns a ClientOption that disables default telemetry (OpenCensus)
 388  // settings on gRPC and HTTP clients.
 389  // An example reason would be to bind custom telemetry that overrides the defaults.
 390  func WithTelemetryDisabled() ClientOption {
 391  	return withTelemetryDisabled{}
 392  }
 393  
 394  type withTelemetryDisabled struct{}
 395  
 396  func (w withTelemetryDisabled) Apply(o *internal.DialSettings) {
 397  	o.TelemetryDisabled = true
 398  }
 399  
 400  // ClientCertSource is a function that returns a TLS client certificate to be used
 401  // when opening TLS connections.
 402  //
 403  // It follows the same semantics as crypto/tls.Config.GetClientCertificate.
 404  //
 405  // This is an EXPERIMENTAL API and may be changed or removed in the future.
 406  type ClientCertSource = func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
 407  
 408  // WithClientCertSource returns a ClientOption that specifies a
 409  // callback function for obtaining a TLS client certificate.
 410  //
 411  // This option is used for supporting mTLS authentication, where the
 412  // server validates the client certifcate when establishing a connection.
 413  //
 414  // The callback function will be invoked whenever the server requests a
 415  // certificate from the client. Implementations of the callback function
 416  // should try to ensure that a valid certificate can be repeatedly returned
 417  // on demand for the entire life cycle of the transport client. If a nil
 418  // Certificate is returned (i.e. no Certificate can be obtained), an error
 419  // should be returned.
 420  //
 421  // This is an EXPERIMENTAL API and may be changed or removed in the future.
 422  func WithClientCertSource(s ClientCertSource) ClientOption {
 423  	return withClientCertSource{s}
 424  }
 425  
 426  type withClientCertSource struct{ s ClientCertSource }
 427  
 428  func (w withClientCertSource) Apply(o *internal.DialSettings) {
 429  	o.ClientCertSource = w.s
 430  }
 431  
 432  // ImpersonateCredentials returns a ClientOption that will impersonate the
 433  // target service account.
 434  //
 435  // In order to impersonate the target service account
 436  // the base service account must have the Service Account Token Creator role,
 437  // roles/iam.serviceAccountTokenCreator, on the target service account.
 438  // See https://cloud.google.com/iam/docs/understanding-service-accounts.
 439  //
 440  // Optionally, delegates can be used during impersonation if the base service
 441  // account lacks the token creator role on the target. When using delegates,
 442  // each service account must be granted roles/iam.serviceAccountTokenCreator
 443  // on the next service account in the chain.
 444  //
 445  // For example, if a base service account of SA1 is trying to impersonate target
 446  // service account SA2 while using delegate service accounts DSA1 and DSA2,
 447  // the following must be true:
 448  //
 449  //  1. Base service account SA1 has roles/iam.serviceAccountTokenCreator on
 450  //     DSA1.
 451  //  2. DSA1 has roles/iam.serviceAccountTokenCreator on DSA2.
 452  //  3. DSA2 has roles/iam.serviceAccountTokenCreator on target SA2.
 453  //
 454  // The resulting impersonated credential will either have the default scopes of
 455  // the client being instantiating or the scopes from WithScopes if provided.
 456  // Scopes are required for creating impersonated credentials, so if this option
 457  // is used while not using a NewClient/NewService function, WithScopes must also
 458  // be explicitly passed in as well.
 459  //
 460  // If the base credential is an authorized user and not a service account, or if
 461  // the option WithQuotaProject is set, the target service account must have a
 462  // role that grants the serviceusage.services.use permission such as
 463  // roles/serviceusage.serviceUsageConsumer.
 464  //
 465  // This is an EXPERIMENTAL API and may be changed or removed in the future.
 466  //
 467  // Deprecated: This option has been replaced by `impersonate` package:
 468  // `google.golang.org/api/impersonate`. Please use the `impersonate` package
 469  // instead with the WithTokenSource option.
 470  func ImpersonateCredentials(target string, delegates ...string) ClientOption {
 471  	return impersonateServiceAccount{
 472  		target:    target,
 473  		delegates: delegates,
 474  	}
 475  }
 476  
 477  type impersonateServiceAccount struct {
 478  	target    string
 479  	delegates []string
 480  }
 481  
 482  func (i impersonateServiceAccount) Apply(o *internal.DialSettings) {
 483  	o.ImpersonationConfig = &impersonate.Config{
 484  		Target: i.target,
 485  	}
 486  	o.ImpersonationConfig.Delegates = make([]string, len(i.delegates))
 487  	copy(o.ImpersonationConfig.Delegates, i.delegates)
 488  }
 489  
 490  type withCreds google.Credentials
 491  
 492  func (w *withCreds) Apply(o *internal.DialSettings) {
 493  	o.Credentials = (*google.Credentials)(w)
 494  }
 495  
 496  // WithCredentials returns a ClientOption that authenticates API calls.
 497  func WithCredentials(creds *google.Credentials) ClientOption {
 498  	return (*withCreds)(creds)
 499  }
 500  
 501  // WithAuthCredentials returns a ClientOption that specifies an
 502  // [cloud.google.com/go/auth.Credentials] to be used as the basis for
 503  // authentication.
 504  func WithAuthCredentials(creds *auth.Credentials) ClientOption {
 505  	return withAuthCredentials{creds}
 506  }
 507  
 508  type withAuthCredentials struct{ creds *auth.Credentials }
 509  
 510  func (w withAuthCredentials) Apply(o *internal.DialSettings) {
 511  	o.AuthCredentials = w.creds
 512  }
 513  
 514  // WithUniverseDomain returns a ClientOption that sets the universe domain.
 515  func WithUniverseDomain(ud string) ClientOption {
 516  	return withUniverseDomain(ud)
 517  }
 518  
 519  type withUniverseDomain string
 520  
 521  func (w withUniverseDomain) Apply(o *internal.DialSettings) {
 522  	o.UniverseDomain = string(w)
 523  }
 524  
 525  // WithLogger returns a ClientOption that sets the logger used throughout the
 526  // client library call stack. If this option is provided it takes precedence
 527  // over the value set in GOOGLE_SDK_GO_LOGGING_LEVEL. Specifying this option
 528  // enables logging at the provided logger's configured level.
 529  func WithLogger(l *slog.Logger) ClientOption {
 530  	return withLogger{l}
 531  }
 532  
 533  type withLogger struct{ l *slog.Logger }
 534  
 535  func (w withLogger) Apply(o *internal.DialSettings) {
 536  	o.Logger = w.l
 537  }
 538