settings.go 7.8 KB

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