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