feat: gitea client
This commit is contained in:
318
vendor/google.golang.org/api/internal/cba.go
generated
vendored
Normal file
318
vendor/google.golang.org/api/internal/cba.go
generated
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
// Copyright 2020 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// cba.go (certificate-based access) contains utils for implementing Device Certificate
|
||||
// Authentication according to https://google.aip.dev/auth/4114 and Default Credentials
|
||||
// for Google Cloud Virtual Environments according to https://google.aip.dev/auth/4115.
|
||||
//
|
||||
// The overall logic for DCA is as follows:
|
||||
// 1. If both endpoint override and client certificate are specified, use them as is.
|
||||
// 2. If user does not specify client certificate, we will attempt to use default
|
||||
// client certificate.
|
||||
// 3. If user does not specify endpoint override, we will use defaultMtlsEndpoint if
|
||||
// client certificate is available and defaultEndpoint otherwise.
|
||||
//
|
||||
// Implications of the above logic:
|
||||
// 1. If the user specifies a non-mTLS endpoint override but client certificate is
|
||||
// available, we will pass along the cert anyway and let the server decide what to do.
|
||||
// 2. If the user specifies an mTLS endpoint override but client certificate is not
|
||||
// available, we will not fail-fast, but let backend throw error when connecting.
|
||||
//
|
||||
// If running within Google's cloud environment, and client certificate is not specified
|
||||
// and not available through DCA, we will try mTLS with credentials held by
|
||||
// the Secure Session Agent, which is part of Google's cloud infrastructure.
|
||||
//
|
||||
// We would like to avoid introducing client-side logic that parses whether the
|
||||
// endpoint override is an mTLS url, since the url pattern may change at anytime.
|
||||
//
|
||||
// This package is not intended for use by end developers. Use the
|
||||
// google.golang.org/api/option package to configure API clients.
|
||||
|
||||
// Package internal supports the options and transport packages.
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/google/s2a-go"
|
||||
"github.com/google/s2a-go/fallback"
|
||||
"google.golang.org/api/internal/cert"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
const (
|
||||
mTLSModeAlways = "always"
|
||||
mTLSModeNever = "never"
|
||||
mTLSModeAuto = "auto"
|
||||
|
||||
// Experimental: if true, the code will try MTLS with S2A as the default for transport security. Default value is false.
|
||||
googleAPIUseS2AEnv = "EXPERIMENTAL_GOOGLE_API_USE_S2A"
|
||||
|
||||
universeDomainPlaceholder = "UNIVERSE_DOMAIN"
|
||||
)
|
||||
|
||||
var (
|
||||
errUniverseNotSupportedMTLS = errors.New("mTLS is not supported in any universe other than googleapis.com")
|
||||
)
|
||||
|
||||
// getClientCertificateSourceAndEndpoint is a convenience function that invokes
|
||||
// getClientCertificateSource and getEndpoint sequentially and returns the client
|
||||
// cert source and endpoint as a tuple.
|
||||
func getClientCertificateSourceAndEndpoint(settings *DialSettings) (cert.Source, string, error) {
|
||||
clientCertSource, err := getClientCertificateSource(settings)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
endpoint, err := getEndpoint(settings, clientCertSource)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
// TODO(chrisdsmith): https://github.com/googleapis/google-api-go-client/issues/2359
|
||||
if settings.Endpoint == "" && !settings.IsUniverseDomainGDU() && settings.DefaultEndpointTemplate != "" {
|
||||
// TODO(chrisdsmith): https://github.com/googleapis/google-api-go-client/issues/2359
|
||||
// if settings.DefaultEndpointTemplate == "" {
|
||||
// return nil, "", errors.New("internaloption.WithDefaultEndpointTemplate is required if option.WithUniverseDomain is not googleapis.com")
|
||||
// }
|
||||
endpoint = resolvedDefaultEndpoint(settings)
|
||||
}
|
||||
return clientCertSource, endpoint, nil
|
||||
}
|
||||
|
||||
type transportConfig struct {
|
||||
clientCertSource cert.Source // The client certificate source.
|
||||
endpoint string // The corresponding endpoint to use based on client certificate source.
|
||||
s2aAddress string // The S2A address if it can be used, otherwise an empty string.
|
||||
s2aMTLSEndpoint string // The MTLS endpoint to use with S2A.
|
||||
}
|
||||
|
||||
func getTransportConfig(settings *DialSettings) (*transportConfig, error) {
|
||||
clientCertSource, endpoint, err := getClientCertificateSourceAndEndpoint(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaultTransportConfig := transportConfig{
|
||||
clientCertSource: clientCertSource,
|
||||
endpoint: endpoint,
|
||||
s2aAddress: "",
|
||||
s2aMTLSEndpoint: "",
|
||||
}
|
||||
|
||||
if !shouldUseS2A(clientCertSource, settings) {
|
||||
return &defaultTransportConfig, nil
|
||||
}
|
||||
if !settings.IsUniverseDomainGDU() {
|
||||
return nil, errUniverseNotSupportedMTLS
|
||||
}
|
||||
|
||||
s2aAddress := GetS2AAddress()
|
||||
if s2aAddress == "" {
|
||||
return &defaultTransportConfig, nil
|
||||
}
|
||||
return &transportConfig{
|
||||
clientCertSource: clientCertSource,
|
||||
endpoint: endpoint,
|
||||
s2aAddress: s2aAddress,
|
||||
s2aMTLSEndpoint: settings.DefaultMTLSEndpoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getClientCertificateSource returns a default client certificate source, if
|
||||
// not provided by the user.
|
||||
//
|
||||
// A nil default source can be returned if the source does not exist. Any exceptions
|
||||
// encountered while initializing the default source will be reported as client
|
||||
// error (ex. corrupt metadata file).
|
||||
//
|
||||
// Important Note: For now, the environment variable GOOGLE_API_USE_CLIENT_CERTIFICATE
|
||||
// must be set to "true" to allow certificate to be used (including user provided
|
||||
// certificates). For details, see AIP-4114.
|
||||
func getClientCertificateSource(settings *DialSettings) (cert.Source, error) {
|
||||
if !isClientCertificateEnabled() {
|
||||
return nil, nil
|
||||
} else if settings.ClientCertSource != nil {
|
||||
return settings.ClientCertSource, nil
|
||||
} else {
|
||||
return cert.DefaultSource()
|
||||
}
|
||||
}
|
||||
|
||||
func isClientCertificateEnabled() bool {
|
||||
useClientCert := os.Getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE")
|
||||
// TODO(andyrzhao): Update default to return "true" after DCA feature is fully released.
|
||||
return strings.ToLower(useClientCert) == "true"
|
||||
}
|
||||
|
||||
// getEndpoint returns the endpoint for the service, taking into account the
|
||||
// user-provided endpoint override "settings.Endpoint".
|
||||
//
|
||||
// If no endpoint override is specified, we will either return the default endpoint or
|
||||
// the default mTLS endpoint if a client certificate is available.
|
||||
//
|
||||
// You can override the default endpoint choice (mtls vs. regular) by setting the
|
||||
// GOOGLE_API_USE_MTLS_ENDPOINT environment variable.
|
||||
//
|
||||
// If the endpoint override is an address (host:port) rather than full base
|
||||
// URL (ex. https://...), then the user-provided address will be merged into
|
||||
// the default endpoint. For example, WithEndpoint("myhost:8000") and
|
||||
// WithDefaultEndpoint("https://foo.com/bar/baz") will return "https://myhost:8080/bar/baz"
|
||||
func getEndpoint(settings *DialSettings, clientCertSource cert.Source) (string, error) {
|
||||
if settings.Endpoint == "" {
|
||||
if isMTLS(clientCertSource) {
|
||||
if !settings.IsUniverseDomainGDU() {
|
||||
return "", errUniverseNotSupportedMTLS
|
||||
}
|
||||
return settings.DefaultMTLSEndpoint, nil
|
||||
}
|
||||
return resolvedDefaultEndpoint(settings), nil
|
||||
}
|
||||
if strings.Contains(settings.Endpoint, "://") {
|
||||
// User passed in a full URL path, use it verbatim.
|
||||
return settings.Endpoint, nil
|
||||
}
|
||||
if resolvedDefaultEndpoint(settings) == "" {
|
||||
// If DefaultEndpoint is not configured, use the user provided endpoint verbatim.
|
||||
// This allows a naked "host[:port]" URL to be used with GRPC Direct Path.
|
||||
return settings.Endpoint, nil
|
||||
}
|
||||
|
||||
// Assume user-provided endpoint is host[:port], merge it with the default endpoint.
|
||||
return mergeEndpoints(resolvedDefaultEndpoint(settings), settings.Endpoint)
|
||||
}
|
||||
|
||||
func isMTLS(clientCertSource cert.Source) bool {
|
||||
mtlsMode := getMTLSMode()
|
||||
return mtlsMode == mTLSModeAlways || (clientCertSource != nil && mtlsMode == mTLSModeAuto)
|
||||
}
|
||||
|
||||
// resolvedDefaultEndpoint returns the DefaultEndpointTemplate merged with the
|
||||
// Universe Domain if the DefaultEndpointTemplate is set, otherwise returns the
|
||||
// deprecated DefaultEndpoint value.
|
||||
func resolvedDefaultEndpoint(settings *DialSettings) string {
|
||||
if settings.DefaultEndpointTemplate == "" {
|
||||
return settings.DefaultEndpoint
|
||||
}
|
||||
return strings.Replace(settings.DefaultEndpointTemplate, universeDomainPlaceholder, settings.GetUniverseDomain(), 1)
|
||||
}
|
||||
|
||||
func getMTLSMode() string {
|
||||
mode := os.Getenv("GOOGLE_API_USE_MTLS_ENDPOINT")
|
||||
if mode == "" {
|
||||
mode = os.Getenv("GOOGLE_API_USE_MTLS") // Deprecated.
|
||||
}
|
||||
if mode == "" {
|
||||
return mTLSModeAuto
|
||||
}
|
||||
return strings.ToLower(mode)
|
||||
}
|
||||
|
||||
func mergeEndpoints(baseURL, newHost string) (string, error) {
|
||||
u, err := url.Parse(fixScheme(baseURL))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Replace(baseURL, u.Host, newHost, 1), nil
|
||||
}
|
||||
|
||||
func fixScheme(baseURL string) string {
|
||||
if !strings.Contains(baseURL, "://") {
|
||||
return "https://" + baseURL
|
||||
}
|
||||
return baseURL
|
||||
}
|
||||
|
||||
// GetGRPCTransportConfigAndEndpoint returns an instance of credentials.TransportCredentials, and the
|
||||
// corresponding endpoint to use for GRPC client.
|
||||
func GetGRPCTransportConfigAndEndpoint(settings *DialSettings) (credentials.TransportCredentials, string, error) {
|
||||
config, err := getTransportConfig(settings)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
defaultTransportCreds := credentials.NewTLS(&tls.Config{
|
||||
GetClientCertificate: config.clientCertSource,
|
||||
})
|
||||
if config.s2aAddress == "" {
|
||||
return defaultTransportCreds, config.endpoint, nil
|
||||
}
|
||||
|
||||
var fallbackOpts *s2a.FallbackOptions
|
||||
// In case of S2A failure, fall back to the endpoint that would've been used without S2A.
|
||||
if fallbackHandshake, err := fallback.DefaultFallbackClientHandshakeFunc(config.endpoint); err == nil {
|
||||
fallbackOpts = &s2a.FallbackOptions{
|
||||
FallbackClientHandshakeFunc: fallbackHandshake,
|
||||
}
|
||||
}
|
||||
|
||||
s2aTransportCreds, err := s2a.NewClientCreds(&s2a.ClientOptions{
|
||||
S2AAddress: config.s2aAddress,
|
||||
FallbackOpts: fallbackOpts,
|
||||
})
|
||||
if err != nil {
|
||||
// Use default if we cannot initialize S2A client transport credentials.
|
||||
return defaultTransportCreds, config.endpoint, nil
|
||||
}
|
||||
return s2aTransportCreds, config.s2aMTLSEndpoint, nil
|
||||
}
|
||||
|
||||
// GetHTTPTransportConfigAndEndpoint returns a client certificate source, a function for dialing MTLS with S2A,
|
||||
// and the endpoint to use for HTTP client.
|
||||
func GetHTTPTransportConfigAndEndpoint(settings *DialSettings) (cert.Source, func(context.Context, string, string) (net.Conn, error), string, error) {
|
||||
config, err := getTransportConfig(settings)
|
||||
if err != nil {
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
if config.s2aAddress == "" {
|
||||
return config.clientCertSource, nil, config.endpoint, nil
|
||||
}
|
||||
|
||||
var fallbackOpts *s2a.FallbackOptions
|
||||
// In case of S2A failure, fall back to the endpoint that would've been used without S2A.
|
||||
if fallbackURL, err := url.Parse(config.endpoint); err == nil {
|
||||
if fallbackDialer, fallbackServerAddr, err := fallback.DefaultFallbackDialerAndAddress(fallbackURL.Hostname()); err == nil {
|
||||
fallbackOpts = &s2a.FallbackOptions{
|
||||
FallbackDialer: &s2a.FallbackDialer{
|
||||
Dialer: fallbackDialer,
|
||||
ServerAddr: fallbackServerAddr,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dialTLSContextFunc := s2a.NewS2ADialTLSContextFunc(&s2a.ClientOptions{
|
||||
S2AAddress: config.s2aAddress,
|
||||
FallbackOpts: fallbackOpts,
|
||||
})
|
||||
return nil, dialTLSContextFunc, config.s2aMTLSEndpoint, nil
|
||||
}
|
||||
|
||||
func shouldUseS2A(clientCertSource cert.Source, settings *DialSettings) bool {
|
||||
// If client cert is found, use that over S2A.
|
||||
if clientCertSource != nil {
|
||||
return false
|
||||
}
|
||||
// If EXPERIMENTAL_GOOGLE_API_USE_S2A is not set to true, skip S2A.
|
||||
if !isGoogleS2AEnabled() {
|
||||
return false
|
||||
}
|
||||
// If DefaultMTLSEndpoint is not set or has endpoint override, skip S2A.
|
||||
if settings.DefaultMTLSEndpoint == "" || settings.Endpoint != "" {
|
||||
return false
|
||||
}
|
||||
// If custom HTTP client is provided, skip S2A.
|
||||
if settings.HTTPClient != nil {
|
||||
return false
|
||||
}
|
||||
return !settings.EnableDirectPath && !settings.EnableDirectPathXds
|
||||
}
|
||||
|
||||
func isGoogleS2AEnabled() bool {
|
||||
return strings.ToLower(os.Getenv(googleAPIUseS2AEnv)) == "true"
|
||||
}
|
||||
58
vendor/google.golang.org/api/internal/cert/default_cert.go
generated
vendored
Normal file
58
vendor/google.golang.org/api/internal/cert/default_cert.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2020 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package cert contains certificate tools for Google API clients.
|
||||
// This package is intended to be used with crypto/tls.Config.GetClientCertificate.
|
||||
//
|
||||
// The certificates can be used to satisfy Google's Endpoint Validation.
|
||||
// See https://cloud.google.com/endpoint-verification/docs/overview
|
||||
//
|
||||
// This package is not intended for use by end developers. Use the
|
||||
// google.golang.org/api/option package to configure API clients.
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// defaultCertData holds all the variables pertaining to
|
||||
// the default certficate source created by DefaultSource.
|
||||
//
|
||||
// A singleton model is used to allow the source to be reused
|
||||
// by the transport layer.
|
||||
type defaultCertData struct {
|
||||
once sync.Once
|
||||
source Source
|
||||
err error
|
||||
}
|
||||
|
||||
var (
|
||||
defaultCert defaultCertData
|
||||
)
|
||||
|
||||
// Source is a function that can be passed into crypto/tls.Config.GetClientCertificate.
|
||||
type Source func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
|
||||
|
||||
// errSourceUnavailable is a sentinel error to indicate certificate source is unavailable.
|
||||
var errSourceUnavailable = errors.New("certificate source is unavailable")
|
||||
|
||||
// DefaultSource returns a certificate source using the preferred EnterpriseCertificateProxySource.
|
||||
// If EnterpriseCertificateProxySource is not available, fall back to the legacy SecureConnectSource.
|
||||
//
|
||||
// If neither source is available (due to missing configurations), a nil Source and a nil Error are
|
||||
// returned to indicate that a default certificate source is unavailable.
|
||||
func DefaultSource() (Source, error) {
|
||||
defaultCert.once.Do(func() {
|
||||
defaultCert.source, defaultCert.err = NewEnterpriseCertificateProxySource("")
|
||||
if errors.Is(defaultCert.err, errSourceUnavailable) {
|
||||
defaultCert.source, defaultCert.err = NewSecureConnectSource("")
|
||||
if errors.Is(defaultCert.err, errSourceUnavailable) {
|
||||
defaultCert.source, defaultCert.err = nil, nil
|
||||
}
|
||||
}
|
||||
})
|
||||
return defaultCert.source, defaultCert.err
|
||||
}
|
||||
54
vendor/google.golang.org/api/internal/cert/enterprise_cert.go
generated
vendored
Normal file
54
vendor/google.golang.org/api/internal/cert/enterprise_cert.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2022 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package cert contains certificate tools for Google API clients.
|
||||
// This package is intended to be used with crypto/tls.Config.GetClientCertificate.
|
||||
//
|
||||
// The certificates can be used to satisfy Google's Endpoint Validation.
|
||||
// See https://cloud.google.com/endpoint-verification/docs/overview
|
||||
//
|
||||
// This package is not intended for use by end developers. Use the
|
||||
// google.golang.org/api/option package to configure API clients.
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
|
||||
"github.com/googleapis/enterprise-certificate-proxy/client"
|
||||
)
|
||||
|
||||
type ecpSource struct {
|
||||
key *client.Key
|
||||
}
|
||||
|
||||
// NewEnterpriseCertificateProxySource creates a certificate source
|
||||
// using the Enterprise Certificate Proxy client, which delegates
|
||||
// certifcate related operations to an OS-specific "signer binary"
|
||||
// that communicates with the native keystore (ex. keychain on MacOS).
|
||||
//
|
||||
// The configFilePath points to a config file containing relevant parameters
|
||||
// such as the certificate issuer and the location of the signer binary.
|
||||
// If configFilePath is empty, the client will attempt to load the config from
|
||||
// a well-known gcloud location.
|
||||
func NewEnterpriseCertificateProxySource(configFilePath string) (Source, error) {
|
||||
key, err := client.Cred(configFilePath)
|
||||
if err != nil {
|
||||
if errors.Is(err, client.ErrCredUnavailable) {
|
||||
return nil, errSourceUnavailable
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return (&ecpSource{
|
||||
key: key,
|
||||
}).getClientCertificate, nil
|
||||
}
|
||||
|
||||
func (s *ecpSource) getClientCertificate(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||
var cert tls.Certificate
|
||||
cert.PrivateKey = s.key
|
||||
cert.Certificate = s.key.CertificateChain()
|
||||
return &cert, nil
|
||||
}
|
||||
122
vendor/google.golang.org/api/internal/cert/secureconnect_cert.go
generated
vendored
Normal file
122
vendor/google.golang.org/api/internal/cert/secureconnect_cert.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright 2022 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package cert contains certificate tools for Google API clients.
|
||||
// This package is intended to be used with crypto/tls.Config.GetClientCertificate.
|
||||
//
|
||||
// The certificates can be used to satisfy Google's Endpoint Validation.
|
||||
// See https://cloud.google.com/endpoint-verification/docs/overview
|
||||
//
|
||||
// This package is not intended for use by end developers. Use the
|
||||
// google.golang.org/api/option package to configure API clients.
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
metadataPath = ".secureConnect"
|
||||
metadataFile = "context_aware_metadata.json"
|
||||
)
|
||||
|
||||
type secureConnectSource struct {
|
||||
metadata secureConnectMetadata
|
||||
|
||||
// Cache the cert to avoid executing helper command repeatedly.
|
||||
cachedCertMutex sync.Mutex
|
||||
cachedCert *tls.Certificate
|
||||
}
|
||||
|
||||
type secureConnectMetadata struct {
|
||||
Cmd []string `json:"cert_provider_command"`
|
||||
}
|
||||
|
||||
// NewSecureConnectSource creates a certificate source using
|
||||
// the Secure Connect Helper and its associated metadata file.
|
||||
//
|
||||
// The configFilePath points to the location of the context aware metadata file.
|
||||
// If configFilePath is empty, use the default context aware metadata location.
|
||||
func NewSecureConnectSource(configFilePath string) (Source, error) {
|
||||
if configFilePath == "" {
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
// Error locating the default config means Secure Connect is not supported.
|
||||
return nil, errSourceUnavailable
|
||||
}
|
||||
configFilePath = filepath.Join(user.HomeDir, metadataPath, metadataFile)
|
||||
}
|
||||
|
||||
file, err := os.ReadFile(configFilePath)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// Config file missing means Secure Connect is not supported.
|
||||
return nil, errSourceUnavailable
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var metadata secureConnectMetadata
|
||||
if err := json.Unmarshal(file, &metadata); err != nil {
|
||||
return nil, fmt.Errorf("cert: could not parse JSON in %q: %w", configFilePath, err)
|
||||
}
|
||||
if err := validateMetadata(metadata); err != nil {
|
||||
return nil, fmt.Errorf("cert: invalid config in %q: %w", configFilePath, err)
|
||||
}
|
||||
return (&secureConnectSource{
|
||||
metadata: metadata,
|
||||
}).getClientCertificate, nil
|
||||
}
|
||||
|
||||
func validateMetadata(metadata secureConnectMetadata) error {
|
||||
if len(metadata.Cmd) == 0 {
|
||||
return errors.New("empty cert_provider_command")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *secureConnectSource) getClientCertificate(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||
s.cachedCertMutex.Lock()
|
||||
defer s.cachedCertMutex.Unlock()
|
||||
if s.cachedCert != nil && !isCertificateExpired(s.cachedCert) {
|
||||
return s.cachedCert, nil
|
||||
}
|
||||
// Expand OS environment variables in the cert provider command such as "$HOME".
|
||||
for i := 0; i < len(s.metadata.Cmd); i++ {
|
||||
s.metadata.Cmd[i] = os.ExpandEnv(s.metadata.Cmd[i])
|
||||
}
|
||||
command := s.metadata.Cmd
|
||||
data, err := exec.Command(command[0], command[1:]...).Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert, err := tls.X509KeyPair(data, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.cachedCert = &cert
|
||||
return &cert, nil
|
||||
}
|
||||
|
||||
// isCertificateExpired returns true if the given cert is expired or invalid.
|
||||
func isCertificateExpired(cert *tls.Certificate) bool {
|
||||
if len(cert.Certificate) == 0 {
|
||||
return true
|
||||
}
|
||||
parsed, err := x509.ParseCertificate(cert.Certificate[0])
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return time.Now().After(parsed.NotAfter)
|
||||
}
|
||||
30
vendor/google.golang.org/api/internal/conn_pool.go
generated
vendored
Normal file
30
vendor/google.golang.org/api/internal/conn_pool.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2020 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// ConnPool is a pool of grpc.ClientConns.
|
||||
type ConnPool interface {
|
||||
// Conn returns a ClientConn from the pool.
|
||||
//
|
||||
// Conns aren't returned to the pool.
|
||||
Conn() *grpc.ClientConn
|
||||
|
||||
// Num returns the number of connections in the pool.
|
||||
//
|
||||
// It will always return the same value.
|
||||
Num() int
|
||||
|
||||
// Close closes every ClientConn in the pool.
|
||||
//
|
||||
// The error returned by Close may be a single error or multiple errors.
|
||||
Close() error
|
||||
|
||||
// ConnPool implements grpc.ClientConnInterface to enable it to be used directly with generated proto stubs.
|
||||
grpc.ClientConnInterface
|
||||
}
|
||||
315
vendor/google.golang.org/api/internal/creds.go
generated
vendored
Normal file
315
vendor/google.golang.org/api/internal/creds.go
generated
vendored
Normal file
@@ -0,0 +1,315 @@
|
||||
// Copyright 2017 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/auth/credentials"
|
||||
"cloud.google.com/go/auth/oauth2adapt"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/api/internal/cert"
|
||||
"google.golang.org/api/internal/impersonate"
|
||||
|
||||
"golang.org/x/oauth2/google"
|
||||
)
|
||||
|
||||
const quotaProjectEnvVar = "GOOGLE_CLOUD_QUOTA_PROJECT"
|
||||
|
||||
// Creds returns credential information obtained from DialSettings, or if none, then
|
||||
// it returns default credential information.
|
||||
func Creds(ctx context.Context, ds *DialSettings) (*google.Credentials, error) {
|
||||
if ds.IsNewAuthLibraryEnabled() {
|
||||
return credsNewAuth(ctx, ds)
|
||||
}
|
||||
creds, err := baseCreds(ctx, ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ds.ImpersonationConfig != nil {
|
||||
return impersonateCredentials(ctx, creds, ds)
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
// GetOAuth2Configuration determines configurations for the OAuth2 transport, which is separate from the API transport.
|
||||
// The OAuth2 transport and endpoint will be configured for mTLS if applicable.
|
||||
func GetOAuth2Configuration(ctx context.Context, settings *DialSettings) (string, *http.Client, error) {
|
||||
clientCertSource, err := getClientCertificateSource(settings)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
tokenURL := oAuth2Endpoint(clientCertSource)
|
||||
var oauth2Client *http.Client
|
||||
if clientCertSource != nil {
|
||||
tlsConfig := &tls.Config{
|
||||
GetClientCertificate: clientCertSource,
|
||||
}
|
||||
oauth2Client = customHTTPClient(tlsConfig)
|
||||
} else {
|
||||
oauth2Client = oauth2.NewClient(ctx, nil)
|
||||
}
|
||||
return tokenURL, oauth2Client, nil
|
||||
}
|
||||
|
||||
func credsNewAuth(ctx context.Context, settings *DialSettings) (*google.Credentials, error) {
|
||||
// Preserve old options behavior
|
||||
if settings.InternalCredentials != nil {
|
||||
return settings.InternalCredentials, nil
|
||||
} else if settings.Credentials != nil {
|
||||
return settings.Credentials, nil
|
||||
} else if settings.TokenSource != nil {
|
||||
return &google.Credentials{TokenSource: settings.TokenSource}, nil
|
||||
}
|
||||
|
||||
if settings.AuthCredentials != nil {
|
||||
return oauth2adapt.Oauth2CredentialsFromAuthCredentials(settings.AuthCredentials), nil
|
||||
}
|
||||
|
||||
var useSelfSignedJWT bool
|
||||
var aud string
|
||||
var scopes []string
|
||||
// If scoped JWTs are enabled user provided an aud, allow self-signed JWT.
|
||||
if settings.EnableJwtWithScope || len(settings.Audiences) > 0 {
|
||||
useSelfSignedJWT = true
|
||||
}
|
||||
|
||||
if len(settings.Scopes) > 0 {
|
||||
scopes = make([]string, len(settings.Scopes))
|
||||
copy(scopes, settings.Scopes)
|
||||
}
|
||||
if len(settings.Audiences) > 0 {
|
||||
aud = settings.Audiences[0]
|
||||
}
|
||||
// Only default scopes if user did not also set an audience.
|
||||
if len(settings.Scopes) == 0 && aud == "" && len(settings.DefaultScopes) > 0 {
|
||||
scopes = make([]string, len(settings.DefaultScopes))
|
||||
copy(scopes, settings.DefaultScopes)
|
||||
}
|
||||
if len(scopes) == 0 && aud == "" {
|
||||
aud = settings.DefaultAudience
|
||||
}
|
||||
|
||||
tokenURL, oauth2Client, err := GetOAuth2Configuration(ctx, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
creds, err := credentials.DetectDefault(&credentials.DetectOptions{
|
||||
Scopes: scopes,
|
||||
Audience: aud,
|
||||
CredentialsFile: settings.CredentialsFile,
|
||||
CredentialsJSON: settings.CredentialsJSON,
|
||||
UseSelfSignedJWT: useSelfSignedJWT,
|
||||
TokenURL: tokenURL,
|
||||
Client: oauth2Client,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return oauth2adapt.Oauth2CredentialsFromAuthCredentials(creds), nil
|
||||
}
|
||||
|
||||
func baseCreds(ctx context.Context, ds *DialSettings) (*google.Credentials, error) {
|
||||
if ds.InternalCredentials != nil {
|
||||
return ds.InternalCredentials, nil
|
||||
}
|
||||
if ds.Credentials != nil {
|
||||
return ds.Credentials, nil
|
||||
}
|
||||
if ds.CredentialsJSON != nil {
|
||||
return credentialsFromJSON(ctx, ds.CredentialsJSON, ds)
|
||||
}
|
||||
if ds.CredentialsFile != "" {
|
||||
data, err := os.ReadFile(ds.CredentialsFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read credentials file: %v", err)
|
||||
}
|
||||
return credentialsFromJSON(ctx, data, ds)
|
||||
}
|
||||
if ds.TokenSource != nil {
|
||||
return &google.Credentials{TokenSource: ds.TokenSource}, nil
|
||||
}
|
||||
cred, err := google.FindDefaultCredentials(ctx, ds.GetScopes()...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(cred.JSON) > 0 {
|
||||
return credentialsFromJSON(ctx, cred.JSON, ds)
|
||||
}
|
||||
// For GAE and GCE, the JSON is empty so return the default credentials directly.
|
||||
return cred, nil
|
||||
}
|
||||
|
||||
// JSON key file type.
|
||||
const (
|
||||
serviceAccountKey = "service_account"
|
||||
)
|
||||
|
||||
// credentialsFromJSON returns a google.Credentials from the JSON data
|
||||
//
|
||||
// - A self-signed JWT flow will be executed if the following conditions are
|
||||
// met:
|
||||
//
|
||||
// (1) At least one of the following is true:
|
||||
// (a) Scope for self-signed JWT flow is enabled
|
||||
// (b) Audiences are explicitly provided by users
|
||||
// (2) No service account impersontation
|
||||
//
|
||||
// - Otherwise, executes standard OAuth 2.0 flow
|
||||
// More details: google.aip.dev/auth/4111
|
||||
func credentialsFromJSON(ctx context.Context, data []byte, ds *DialSettings) (*google.Credentials, error) {
|
||||
var params google.CredentialsParams
|
||||
params.Scopes = ds.GetScopes()
|
||||
|
||||
tokenURL, oauth2Client, err := GetOAuth2Configuration(ctx, ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.TokenURL = tokenURL
|
||||
ctx = context.WithValue(ctx, oauth2.HTTPClient, oauth2Client)
|
||||
|
||||
// By default, a standard OAuth 2.0 token source is created
|
||||
cred, err := google.CredentialsFromJSONWithParams(ctx, data, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Override the token source to use self-signed JWT if conditions are met
|
||||
isJWTFlow, err := isSelfSignedJWTFlow(data, ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isJWTFlow {
|
||||
ts, err := selfSignedJWTTokenSource(data, ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cred.TokenSource = ts
|
||||
}
|
||||
|
||||
return cred, err
|
||||
}
|
||||
|
||||
func oAuth2Endpoint(clientCertSource cert.Source) string {
|
||||
if isMTLS(clientCertSource) {
|
||||
return google.MTLSTokenURL
|
||||
}
|
||||
return google.Endpoint.TokenURL
|
||||
}
|
||||
|
||||
func isSelfSignedJWTFlow(data []byte, ds *DialSettings) (bool, error) {
|
||||
// For non-GDU universe domains, token exchange is impossible and services
|
||||
// must support self-signed JWTs with scopes.
|
||||
if !ds.IsUniverseDomainGDU() {
|
||||
return typeServiceAccount(data)
|
||||
}
|
||||
if (ds.EnableJwtWithScope || ds.HasCustomAudience()) && ds.ImpersonationConfig == nil {
|
||||
return typeServiceAccount(data)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// typeServiceAccount checks if JSON data is for a service account.
|
||||
func typeServiceAccount(data []byte) (bool, error) {
|
||||
var f struct {
|
||||
Type string `json:"type"`
|
||||
// The remaining JSON fields are omitted because they are not used.
|
||||
}
|
||||
if err := json.Unmarshal(data, &f); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return f.Type == serviceAccountKey, nil
|
||||
}
|
||||
|
||||
func selfSignedJWTTokenSource(data []byte, ds *DialSettings) (oauth2.TokenSource, error) {
|
||||
if len(ds.GetScopes()) > 0 && !ds.HasCustomAudience() {
|
||||
// Scopes are preferred in self-signed JWT unless the scope is not available
|
||||
// or a custom audience is used.
|
||||
return google.JWTAccessTokenSourceWithScope(data, ds.GetScopes()...)
|
||||
} else if ds.GetAudience() != "" {
|
||||
// Fallback to audience if scope is not provided
|
||||
return google.JWTAccessTokenSourceFromJSON(data, ds.GetAudience())
|
||||
} else {
|
||||
return nil, errors.New("neither scopes or audience are available for the self-signed JWT")
|
||||
}
|
||||
}
|
||||
|
||||
// GetQuotaProject retrieves quota project with precedence being: client option,
|
||||
// environment variable, creds file.
|
||||
func GetQuotaProject(creds *google.Credentials, clientOpt string) string {
|
||||
if clientOpt != "" {
|
||||
return clientOpt
|
||||
}
|
||||
if env := os.Getenv(quotaProjectEnvVar); env != "" {
|
||||
return env
|
||||
}
|
||||
if creds == nil {
|
||||
return ""
|
||||
}
|
||||
var v struct {
|
||||
QuotaProject string `json:"quota_project_id"`
|
||||
}
|
||||
if err := json.Unmarshal(creds.JSON, &v); err != nil {
|
||||
return ""
|
||||
}
|
||||
return v.QuotaProject
|
||||
}
|
||||
|
||||
func impersonateCredentials(ctx context.Context, creds *google.Credentials, ds *DialSettings) (*google.Credentials, error) {
|
||||
if len(ds.ImpersonationConfig.Scopes) == 0 {
|
||||
ds.ImpersonationConfig.Scopes = ds.GetScopes()
|
||||
}
|
||||
ts, err := impersonate.TokenSource(ctx, creds.TokenSource, ds.ImpersonationConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &google.Credentials{
|
||||
TokenSource: ts,
|
||||
ProjectID: creds.ProjectID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// customHTTPClient constructs an HTTPClient using the provided tlsConfig, to support mTLS.
|
||||
func customHTTPClient(tlsConfig *tls.Config) *http.Client {
|
||||
trans := baseTransport()
|
||||
trans.TLSClientConfig = tlsConfig
|
||||
return &http.Client{Transport: trans}
|
||||
}
|
||||
|
||||
func baseTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
// ErrUniverseNotMatch composes an error string from the provided universe
|
||||
// domain sources (DialSettings and Credentials, respectively).
|
||||
func ErrUniverseNotMatch(settingsUD, credsUD string) error {
|
||||
return fmt.Errorf(
|
||||
"the configured universe domain (%q) does not match the universe "+
|
||||
"domain found in the credentials (%q). If you haven't configured "+
|
||||
"WithUniverseDomain explicitly, \"googleapis.com\" is the default",
|
||||
settingsUD,
|
||||
credsUD)
|
||||
}
|
||||
127
vendor/google.golang.org/api/internal/impersonate/impersonate.go
generated
vendored
Normal file
127
vendor/google.golang.org/api/internal/impersonate/impersonate.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package impersonate is used to impersonate Google Credentials.
|
||||
package impersonate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// Config for generating impersonated credentials.
|
||||
type Config struct {
|
||||
// Target is the service account to impersonate. Required.
|
||||
Target string
|
||||
// Scopes the impersonated credential should have. Required.
|
||||
Scopes []string
|
||||
// Delegates are the service accounts in a delegation chain. Each service
|
||||
// account must be granted roles/iam.serviceAccountTokenCreator on the next
|
||||
// service account in the chain. Optional.
|
||||
Delegates []string
|
||||
}
|
||||
|
||||
// TokenSource returns an impersonated TokenSource configured with the provided
|
||||
// config using ts as the base credential provider for making requests.
|
||||
func TokenSource(ctx context.Context, ts oauth2.TokenSource, config *Config) (oauth2.TokenSource, error) {
|
||||
if len(config.Scopes) == 0 {
|
||||
return nil, fmt.Errorf("impersonate: scopes must be provided")
|
||||
}
|
||||
its := impersonatedTokenSource{
|
||||
ctx: ctx,
|
||||
ts: ts,
|
||||
name: formatIAMServiceAccountName(config.Target),
|
||||
// Default to the longest acceptable value of one hour as the token will
|
||||
// be refreshed automatically.
|
||||
lifetime: "3600s",
|
||||
}
|
||||
|
||||
its.delegates = make([]string, len(config.Delegates))
|
||||
for i, v := range config.Delegates {
|
||||
its.delegates[i] = formatIAMServiceAccountName(v)
|
||||
}
|
||||
its.scopes = make([]string, len(config.Scopes))
|
||||
copy(its.scopes, config.Scopes)
|
||||
|
||||
return oauth2.ReuseTokenSource(nil, its), nil
|
||||
}
|
||||
|
||||
func formatIAMServiceAccountName(name string) string {
|
||||
return fmt.Sprintf("projects/-/serviceAccounts/%s", name)
|
||||
}
|
||||
|
||||
type generateAccessTokenReq struct {
|
||||
Delegates []string `json:"delegates,omitempty"`
|
||||
Lifetime string `json:"lifetime,omitempty"`
|
||||
Scope []string `json:"scope,omitempty"`
|
||||
}
|
||||
|
||||
type generateAccessTokenResp struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
ExpireTime string `json:"expireTime"`
|
||||
}
|
||||
|
||||
type impersonatedTokenSource struct {
|
||||
ctx context.Context
|
||||
ts oauth2.TokenSource
|
||||
|
||||
name string
|
||||
lifetime string
|
||||
scopes []string
|
||||
delegates []string
|
||||
}
|
||||
|
||||
// Token returns an impersonated Token.
|
||||
func (i impersonatedTokenSource) Token() (*oauth2.Token, error) {
|
||||
hc := oauth2.NewClient(i.ctx, i.ts)
|
||||
reqBody := generateAccessTokenReq{
|
||||
Delegates: i.delegates,
|
||||
Lifetime: i.lifetime,
|
||||
Scope: i.scopes,
|
||||
}
|
||||
b, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("impersonate: unable to marshal request: %v", err)
|
||||
}
|
||||
url := fmt.Sprintf("https://iamcredentials.googleapis.com/v1/%s:generateAccessToken", i.name)
|
||||
req, err := http.NewRequest("POST", url, bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("impersonate: unable to create request: %v", err)
|
||||
}
|
||||
req = req.WithContext(i.ctx)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := hc.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("impersonate: unable to generate access token: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("impersonate: unable to read body: %v", err)
|
||||
}
|
||||
if c := resp.StatusCode; c < 200 || c > 299 {
|
||||
return nil, fmt.Errorf("impersonate: status code %d: %s", c, body)
|
||||
}
|
||||
|
||||
var accessTokenResp generateAccessTokenResp
|
||||
if err := json.Unmarshal(body, &accessTokenResp); err != nil {
|
||||
return nil, fmt.Errorf("impersonate: unable to parse response: %v", err)
|
||||
}
|
||||
expiry, err := time.Parse(time.RFC3339, accessTokenResp.ExpireTime)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("impersonate: unable to parse expiry: %v", err)
|
||||
}
|
||||
return &oauth2.Token{
|
||||
AccessToken: accessTokenResp.AccessToken,
|
||||
Expiry: expiry,
|
||||
}, nil
|
||||
}
|
||||
136
vendor/google.golang.org/api/internal/s2a.go
generated
vendored
Normal file
136
vendor/google.golang.org/api/internal/s2a.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright 2023 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/compute/metadata"
|
||||
)
|
||||
|
||||
const configEndpointSuffix = "instance/platform-security/auto-mtls-configuration"
|
||||
|
||||
// The period an MTLS config can be reused before needing refresh.
|
||||
var configExpiry = time.Hour
|
||||
|
||||
// GetS2AAddress returns the S2A address to be reached via plaintext connection.
|
||||
func GetS2AAddress() string {
|
||||
c, err := getMetadataMTLSAutoConfig().Config()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if !c.Valid() {
|
||||
return ""
|
||||
}
|
||||
return c.S2A.PlaintextAddress
|
||||
}
|
||||
|
||||
type mtlsConfigSource interface {
|
||||
Config() (*mtlsConfig, error)
|
||||
}
|
||||
|
||||
// mdsMTLSAutoConfigSource is an instance of reuseMTLSConfigSource, with metadataMTLSAutoConfig as its config source.
|
||||
var (
|
||||
mdsMTLSAutoConfigSource mtlsConfigSource
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// getMetadataMTLSAutoConfig returns mdsMTLSAutoConfigSource, which is backed by config from MDS with auto-refresh.
|
||||
func getMetadataMTLSAutoConfig() mtlsConfigSource {
|
||||
once.Do(func() {
|
||||
mdsMTLSAutoConfigSource = &reuseMTLSConfigSource{
|
||||
src: &metadataMTLSAutoConfig{},
|
||||
}
|
||||
})
|
||||
return mdsMTLSAutoConfigSource
|
||||
}
|
||||
|
||||
// reuseMTLSConfigSource caches a valid version of mtlsConfig, and uses `src` to refresh upon config expiry.
|
||||
// It implements the mtlsConfigSource interface, so calling Config() on it returns an mtlsConfig.
|
||||
type reuseMTLSConfigSource struct {
|
||||
src mtlsConfigSource // src.Config() is called when config is expired
|
||||
mu sync.Mutex // mutex guards config
|
||||
config *mtlsConfig // cached config
|
||||
}
|
||||
|
||||
func (cs *reuseMTLSConfigSource) Config() (*mtlsConfig, error) {
|
||||
cs.mu.Lock()
|
||||
defer cs.mu.Unlock()
|
||||
|
||||
if cs.config.Valid() {
|
||||
return cs.config, nil
|
||||
}
|
||||
c, err := cs.src.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cs.config = c
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// metadataMTLSAutoConfig is an implementation of the interface mtlsConfigSource
|
||||
// It has the logic to query MDS and return an mtlsConfig
|
||||
type metadataMTLSAutoConfig struct{}
|
||||
|
||||
var httpGetMetadataMTLSConfig = func() (string, error) {
|
||||
return metadata.Get(configEndpointSuffix)
|
||||
}
|
||||
|
||||
func (cs *metadataMTLSAutoConfig) Config() (*mtlsConfig, error) {
|
||||
resp, err := httpGetMetadataMTLSConfig()
|
||||
if err != nil {
|
||||
log.Printf("querying MTLS config from MDS endpoint failed: %v", err)
|
||||
return defaultMTLSConfig(), nil
|
||||
}
|
||||
var config mtlsConfig
|
||||
err = json.Unmarshal([]byte(resp), &config)
|
||||
if err != nil {
|
||||
log.Printf("unmarshalling MTLS config from MDS endpoint failed: %v", err)
|
||||
return defaultMTLSConfig(), nil
|
||||
}
|
||||
|
||||
if config.S2A == nil {
|
||||
log.Printf("returned MTLS config from MDS endpoint is invalid: %v", config)
|
||||
return defaultMTLSConfig(), nil
|
||||
}
|
||||
|
||||
// set new expiry
|
||||
config.Expiry = time.Now().Add(configExpiry)
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func defaultMTLSConfig() *mtlsConfig {
|
||||
return &mtlsConfig{
|
||||
S2A: &s2aAddresses{
|
||||
PlaintextAddress: "",
|
||||
MTLSAddress: "",
|
||||
},
|
||||
Expiry: time.Now().Add(configExpiry),
|
||||
}
|
||||
}
|
||||
|
||||
// s2aAddresses contains the plaintext and/or MTLS S2A addresses.
|
||||
type s2aAddresses struct {
|
||||
// PlaintextAddress is the plaintext address to reach S2A
|
||||
PlaintextAddress string `json:"plaintext_address"`
|
||||
// MTLSAddress is the MTLS address to reach S2A
|
||||
MTLSAddress string `json:"mtls_address"`
|
||||
}
|
||||
|
||||
// mtlsConfig contains the configuration for establishing MTLS connections with Google APIs.
|
||||
type mtlsConfig struct {
|
||||
S2A *s2aAddresses `json:"s2a"`
|
||||
Expiry time.Time
|
||||
}
|
||||
|
||||
func (c *mtlsConfig) Valid() bool {
|
||||
return c != nil && c.S2A != nil && !c.expired()
|
||||
}
|
||||
func (c *mtlsConfig) expired() bool {
|
||||
return c.Expiry.Before(time.Now())
|
||||
}
|
||||
243
vendor/google.golang.org/api/internal/settings.go
generated
vendored
Normal file
243
vendor/google.golang.org/api/internal/settings.go
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
// Copyright 2017 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package internal supports the options and transport packages.
|
||||
package internal
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/auth"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/api/internal/impersonate"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const (
|
||||
newAuthLibEnvVar = "GOOGLE_API_GO_EXPERIMENTAL_ENABLE_NEW_AUTH_LIB"
|
||||
newAuthLibDisabledEnVar = "GOOGLE_API_GO_EXPERIMENTAL_DISABLE_NEW_AUTH_LIB"
|
||||
universeDomainEnvVar = "GOOGLE_CLOUD_UNIVERSE_DOMAIN"
|
||||
defaultUniverseDomain = "googleapis.com"
|
||||
)
|
||||
|
||||
// DialSettings holds information needed to establish a connection with a
|
||||
// Google API service.
|
||||
type DialSettings struct {
|
||||
Endpoint string
|
||||
DefaultEndpoint string
|
||||
DefaultEndpointTemplate string
|
||||
DefaultMTLSEndpoint string
|
||||
Scopes []string
|
||||
DefaultScopes []string
|
||||
EnableJwtWithScope bool
|
||||
TokenSource oauth2.TokenSource
|
||||
Credentials *google.Credentials
|
||||
CredentialsFile string // if set, Token Source is ignored.
|
||||
CredentialsJSON []byte
|
||||
InternalCredentials *google.Credentials
|
||||
UserAgent string
|
||||
APIKey string
|
||||
Audiences []string
|
||||
DefaultAudience string
|
||||
HTTPClient *http.Client
|
||||
GRPCDialOpts []grpc.DialOption
|
||||
GRPCConn *grpc.ClientConn
|
||||
GRPCConnPool ConnPool
|
||||
GRPCConnPoolSize int
|
||||
NoAuth bool
|
||||
TelemetryDisabled bool
|
||||
ClientCertSource func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
|
||||
CustomClaims map[string]interface{}
|
||||
SkipValidation bool
|
||||
ImpersonationConfig *impersonate.Config
|
||||
EnableDirectPath bool
|
||||
EnableDirectPathXds bool
|
||||
AllowNonDefaultServiceAccount bool
|
||||
DefaultUniverseDomain string
|
||||
UniverseDomain string
|
||||
// Google API system parameters. For more information please read:
|
||||
// https://cloud.google.com/apis/docs/system-parameters
|
||||
QuotaProject string
|
||||
RequestReason string
|
||||
|
||||
// New Auth library Options
|
||||
AuthCredentials *auth.Credentials
|
||||
EnableNewAuthLibrary bool
|
||||
}
|
||||
|
||||
// GetScopes returns the user-provided scopes, if set, or else falls back to the
|
||||
// default scopes.
|
||||
func (ds *DialSettings) GetScopes() []string {
|
||||
if len(ds.Scopes) > 0 {
|
||||
return ds.Scopes
|
||||
}
|
||||
return ds.DefaultScopes
|
||||
}
|
||||
|
||||
// GetAudience returns the user-provided audience, if set, or else falls back to the default audience.
|
||||
func (ds *DialSettings) GetAudience() string {
|
||||
if ds.HasCustomAudience() {
|
||||
return ds.Audiences[0]
|
||||
}
|
||||
return ds.DefaultAudience
|
||||
}
|
||||
|
||||
// HasCustomAudience returns true if a custom audience is provided by users.
|
||||
func (ds *DialSettings) HasCustomAudience() bool {
|
||||
return len(ds.Audiences) > 0
|
||||
}
|
||||
|
||||
// IsNewAuthLibraryEnabled returns true if the new auth library should be used.
|
||||
func (ds *DialSettings) IsNewAuthLibraryEnabled() bool {
|
||||
// Disabled env is for future rollouts to make sure there is a way to easily
|
||||
// disable this behaviour once we switch in on by default.
|
||||
if b, err := strconv.ParseBool(os.Getenv(newAuthLibDisabledEnVar)); err == nil && b {
|
||||
return false
|
||||
}
|
||||
if ds.EnableNewAuthLibrary {
|
||||
return true
|
||||
}
|
||||
if b, err := strconv.ParseBool(os.Getenv(newAuthLibEnvVar)); err == nil {
|
||||
return b
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Validate reports an error if ds is invalid.
|
||||
func (ds *DialSettings) Validate() error {
|
||||
if ds.SkipValidation {
|
||||
return nil
|
||||
}
|
||||
hasCreds := ds.APIKey != "" || ds.TokenSource != nil || ds.CredentialsFile != "" || ds.Credentials != nil
|
||||
if ds.NoAuth && hasCreds {
|
||||
return errors.New("options.WithoutAuthentication is incompatible with any option that provides credentials")
|
||||
}
|
||||
// Credentials should not appear with other options.
|
||||
// We currently allow TokenSource and CredentialsFile to coexist.
|
||||
// TODO(jba): make TokenSource & CredentialsFile an error (breaking change).
|
||||
nCreds := 0
|
||||
if ds.Credentials != nil {
|
||||
nCreds++
|
||||
}
|
||||
if ds.CredentialsJSON != nil {
|
||||
nCreds++
|
||||
}
|
||||
if ds.CredentialsFile != "" {
|
||||
nCreds++
|
||||
}
|
||||
if ds.APIKey != "" {
|
||||
nCreds++
|
||||
}
|
||||
if ds.TokenSource != nil {
|
||||
nCreds++
|
||||
}
|
||||
if len(ds.Scopes) > 0 && len(ds.Audiences) > 0 {
|
||||
return errors.New("WithScopes is incompatible with WithAudience")
|
||||
}
|
||||
// Accept only one form of credentials, except we allow TokenSource and CredentialsFile for backwards compatibility.
|
||||
if nCreds > 1 && !(nCreds == 2 && ds.TokenSource != nil && ds.CredentialsFile != "") {
|
||||
return errors.New("multiple credential options provided")
|
||||
}
|
||||
if ds.GRPCConn != nil && ds.GRPCConnPool != nil {
|
||||
return errors.New("WithGRPCConn is incompatible with WithConnPool")
|
||||
}
|
||||
if ds.HTTPClient != nil && ds.GRPCConnPool != nil {
|
||||
return errors.New("WithHTTPClient is incompatible with WithConnPool")
|
||||
}
|
||||
if ds.HTTPClient != nil && ds.GRPCConn != nil {
|
||||
return errors.New("WithHTTPClient is incompatible with WithGRPCConn")
|
||||
}
|
||||
if ds.HTTPClient != nil && ds.GRPCDialOpts != nil {
|
||||
return errors.New("WithHTTPClient is incompatible with gRPC dial options")
|
||||
}
|
||||
if ds.HTTPClient != nil && ds.QuotaProject != "" {
|
||||
return errors.New("WithHTTPClient is incompatible with QuotaProject")
|
||||
}
|
||||
if ds.HTTPClient != nil && ds.RequestReason != "" {
|
||||
return errors.New("WithHTTPClient is incompatible with RequestReason")
|
||||
}
|
||||
if ds.HTTPClient != nil && ds.ClientCertSource != nil {
|
||||
return errors.New("WithHTTPClient is incompatible with WithClientCertSource")
|
||||
}
|
||||
if ds.ClientCertSource != nil && (ds.GRPCConn != nil || ds.GRPCConnPool != nil || ds.GRPCConnPoolSize != 0 || ds.GRPCDialOpts != nil) {
|
||||
return errors.New("WithClientCertSource is currently only supported for HTTP. gRPC settings are incompatible")
|
||||
}
|
||||
if ds.ImpersonationConfig != nil && len(ds.ImpersonationConfig.Scopes) == 0 && len(ds.Scopes) == 0 {
|
||||
return errors.New("WithImpersonatedCredentials requires scopes being provided")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDefaultUniverseDomain returns the Google default universe domain
|
||||
// ("googleapis.com").
|
||||
func (ds *DialSettings) GetDefaultUniverseDomain() string {
|
||||
return defaultUniverseDomain
|
||||
}
|
||||
|
||||
// GetUniverseDomain returns the default service domain for a given Cloud
|
||||
// universe, with the following precedence:
|
||||
//
|
||||
// 1. A non-empty option.WithUniverseDomain.
|
||||
// 2. A non-empty environment variable GOOGLE_CLOUD_UNIVERSE_DOMAIN.
|
||||
// 3. The default value "googleapis.com".
|
||||
func (ds *DialSettings) GetUniverseDomain() string {
|
||||
if ds.UniverseDomain != "" {
|
||||
return ds.UniverseDomain
|
||||
}
|
||||
if envUD := os.Getenv(universeDomainEnvVar); envUD != "" {
|
||||
return envUD
|
||||
}
|
||||
return defaultUniverseDomain
|
||||
}
|
||||
|
||||
// IsUniverseDomainGDU returns true if the universe domain is the default Google
|
||||
// universe ("googleapis.com").
|
||||
func (ds *DialSettings) IsUniverseDomainGDU() bool {
|
||||
return ds.GetUniverseDomain() == defaultUniverseDomain
|
||||
}
|
||||
|
||||
// GetUniverseDomain returns the default service domain for a given Cloud
|
||||
// universe, from google.Credentials, for comparison with the value returned by
|
||||
// (*DialSettings).GetUniverseDomain. This wrapper function should be removed
|
||||
// to close https://github.com/googleapis/google-api-go-client/issues/2399.
|
||||
func GetUniverseDomain(creds *google.Credentials) (string, error) {
|
||||
timer := time.NewTimer(time.Second)
|
||||
defer timer.Stop()
|
||||
errors := make(chan error)
|
||||
results := make(chan string)
|
||||
|
||||
go func() {
|
||||
result, err := creds.GetUniverseDomain()
|
||||
if err != nil {
|
||||
errors <- err
|
||||
return
|
||||
}
|
||||
results <- result
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-errors:
|
||||
// An error that is returned before the timer expires is likely to be
|
||||
// connection refused. Temporarily (2024-03-21) return the GDU domain.
|
||||
return defaultUniverseDomain, nil
|
||||
case res := <-results:
|
||||
return res, nil
|
||||
case <-timer.C: // Timer is expired.
|
||||
// If err or res was not returned, it means that creds.GetUniverseDomain()
|
||||
// did not complete in 1s. Assume that MDS is likely never responding to
|
||||
// the endpoint and will timeout. This is the source of issues such as
|
||||
// https://github.com/googleapis/google-cloud-go/issues/9350.
|
||||
// Temporarily (2024-02-02) return the GDU domain. Restore the original
|
||||
// calls to creds.GetUniverseDomain() in grpc/dial.go and http/dial.go
|
||||
// and remove this method to close
|
||||
// https://github.com/googleapis/google-api-go-client/issues/2399.
|
||||
return defaultUniverseDomain, nil
|
||||
}
|
||||
}
|
||||
27
vendor/google.golang.org/api/internal/third_party/uritemplates/LICENSE
generated
vendored
Normal file
27
vendor/google.golang.org/api/internal/third_party/uritemplates/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2013 Joshua Tacoma. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
14
vendor/google.golang.org/api/internal/third_party/uritemplates/METADATA
generated
vendored
Normal file
14
vendor/google.golang.org/api/internal/third_party/uritemplates/METADATA
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: "uritemplates"
|
||||
description:
|
||||
"Package uritemplates is a level 4 implementation of RFC 6570 (URI "
|
||||
"Template, http://tools.ietf.org/html/rfc6570)."
|
||||
|
||||
third_party {
|
||||
url {
|
||||
type: GIT
|
||||
value: "https://github.com/jtacoma/uritemplates"
|
||||
}
|
||||
version: "0.1"
|
||||
last_upgrade_date { year: 2014 month: 8 day: 18 }
|
||||
license_type: NOTICE
|
||||
}
|
||||
248
vendor/google.golang.org/api/internal/third_party/uritemplates/uritemplates.go
generated
vendored
Normal file
248
vendor/google.golang.org/api/internal/third_party/uritemplates/uritemplates.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
// Copyright 2013 Joshua Tacoma. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package uritemplates is a level 3 implementation of RFC 6570 (URI
|
||||
// Template, http://tools.ietf.org/html/rfc6570).
|
||||
// uritemplates does not support composite values (in Go: slices or maps)
|
||||
// and so does not qualify as a level 4 implementation.
|
||||
package uritemplates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
unreserved = regexp.MustCompile("[^A-Za-z0-9\\-._~]")
|
||||
reserved = regexp.MustCompile("[^A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=]")
|
||||
validname = regexp.MustCompile("^([A-Za-z0-9_\\.]|%[0-9A-Fa-f][0-9A-Fa-f])+$")
|
||||
hex = []byte("0123456789ABCDEF")
|
||||
)
|
||||
|
||||
func pctEncode(src []byte) []byte {
|
||||
dst := make([]byte, len(src)*3)
|
||||
for i, b := range src {
|
||||
buf := dst[i*3 : i*3+3]
|
||||
buf[0] = 0x25
|
||||
buf[1] = hex[b/16]
|
||||
buf[2] = hex[b%16]
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// pairWriter is a convenience struct which allows escaped and unescaped
|
||||
// versions of the template to be written in parallel.
|
||||
type pairWriter struct {
|
||||
escaped, unescaped bytes.Buffer
|
||||
}
|
||||
|
||||
// Write writes the provided string directly without any escaping.
|
||||
func (w *pairWriter) Write(s string) {
|
||||
w.escaped.WriteString(s)
|
||||
w.unescaped.WriteString(s)
|
||||
}
|
||||
|
||||
// Escape writes the provided string, escaping the string for the
|
||||
// escaped output.
|
||||
func (w *pairWriter) Escape(s string, allowReserved bool) {
|
||||
w.unescaped.WriteString(s)
|
||||
if allowReserved {
|
||||
w.escaped.Write(reserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
} else {
|
||||
w.escaped.Write(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
}
|
||||
}
|
||||
|
||||
// Escaped returns the escaped string.
|
||||
func (w *pairWriter) Escaped() string {
|
||||
return w.escaped.String()
|
||||
}
|
||||
|
||||
// Unescaped returns the unescaped string.
|
||||
func (w *pairWriter) Unescaped() string {
|
||||
return w.unescaped.String()
|
||||
}
|
||||
|
||||
// A uriTemplate is a parsed representation of a URI template.
|
||||
type uriTemplate struct {
|
||||
raw string
|
||||
parts []templatePart
|
||||
}
|
||||
|
||||
// parse parses a URI template string into a uriTemplate object.
|
||||
func parse(rawTemplate string) (*uriTemplate, error) {
|
||||
split := strings.Split(rawTemplate, "{")
|
||||
parts := make([]templatePart, len(split)*2-1)
|
||||
for i, s := range split {
|
||||
if i == 0 {
|
||||
if strings.Contains(s, "}") {
|
||||
return nil, errors.New("unexpected }")
|
||||
}
|
||||
parts[i].raw = s
|
||||
continue
|
||||
}
|
||||
subsplit := strings.Split(s, "}")
|
||||
if len(subsplit) != 2 {
|
||||
return nil, errors.New("malformed template")
|
||||
}
|
||||
expression := subsplit[0]
|
||||
var err error
|
||||
parts[i*2-1], err = parseExpression(expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parts[i*2].raw = subsplit[1]
|
||||
}
|
||||
return &uriTemplate{
|
||||
raw: rawTemplate,
|
||||
parts: parts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type templatePart struct {
|
||||
raw string
|
||||
terms []templateTerm
|
||||
first string
|
||||
sep string
|
||||
named bool
|
||||
ifemp string
|
||||
allowReserved bool
|
||||
}
|
||||
|
||||
type templateTerm struct {
|
||||
name string
|
||||
explode bool
|
||||
truncate int
|
||||
}
|
||||
|
||||
func parseExpression(expression string) (result templatePart, err error) {
|
||||
switch expression[0] {
|
||||
case '+':
|
||||
result.sep = ","
|
||||
result.allowReserved = true
|
||||
expression = expression[1:]
|
||||
case '.':
|
||||
result.first = "."
|
||||
result.sep = "."
|
||||
expression = expression[1:]
|
||||
case '/':
|
||||
result.first = "/"
|
||||
result.sep = "/"
|
||||
expression = expression[1:]
|
||||
case ';':
|
||||
result.first = ";"
|
||||
result.sep = ";"
|
||||
result.named = true
|
||||
expression = expression[1:]
|
||||
case '?':
|
||||
result.first = "?"
|
||||
result.sep = "&"
|
||||
result.named = true
|
||||
result.ifemp = "="
|
||||
expression = expression[1:]
|
||||
case '&':
|
||||
result.first = "&"
|
||||
result.sep = "&"
|
||||
result.named = true
|
||||
result.ifemp = "="
|
||||
expression = expression[1:]
|
||||
case '#':
|
||||
result.first = "#"
|
||||
result.sep = ","
|
||||
result.allowReserved = true
|
||||
expression = expression[1:]
|
||||
default:
|
||||
result.sep = ","
|
||||
}
|
||||
rawterms := strings.Split(expression, ",")
|
||||
result.terms = make([]templateTerm, len(rawterms))
|
||||
for i, raw := range rawterms {
|
||||
result.terms[i], err = parseTerm(raw)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func parseTerm(term string) (result templateTerm, err error) {
|
||||
// TODO(djd): Remove "*" suffix parsing once we check that no APIs have
|
||||
// mistakenly used that attribute.
|
||||
if strings.HasSuffix(term, "*") {
|
||||
result.explode = true
|
||||
term = term[:len(term)-1]
|
||||
}
|
||||
split := strings.Split(term, ":")
|
||||
if len(split) == 1 {
|
||||
result.name = term
|
||||
} else if len(split) == 2 {
|
||||
result.name = split[0]
|
||||
var parsed int64
|
||||
parsed, err = strconv.ParseInt(split[1], 10, 0)
|
||||
result.truncate = int(parsed)
|
||||
} else {
|
||||
err = errors.New("multiple colons in same term")
|
||||
}
|
||||
if !validname.MatchString(result.name) {
|
||||
err = errors.New("not a valid name: " + result.name)
|
||||
}
|
||||
if result.explode && result.truncate > 0 {
|
||||
err = errors.New("both explode and prefix modifiers on same term")
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Expand expands a URI template with a set of values to produce the
|
||||
// resultant URI. Two forms of the result are returned: one with all the
|
||||
// elements escaped, and one with the elements unescaped.
|
||||
func (t *uriTemplate) Expand(values map[string]string) (escaped, unescaped string) {
|
||||
var w pairWriter
|
||||
for _, p := range t.parts {
|
||||
p.expand(&w, values)
|
||||
}
|
||||
return w.Escaped(), w.Unescaped()
|
||||
}
|
||||
|
||||
func (tp *templatePart) expand(w *pairWriter, values map[string]string) {
|
||||
if len(tp.raw) > 0 {
|
||||
w.Write(tp.raw)
|
||||
return
|
||||
}
|
||||
var first = true
|
||||
for _, term := range tp.terms {
|
||||
value, exists := values[term.name]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
if first {
|
||||
w.Write(tp.first)
|
||||
first = false
|
||||
} else {
|
||||
w.Write(tp.sep)
|
||||
}
|
||||
tp.expandString(w, term, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *templatePart) expandName(w *pairWriter, name string, empty bool) {
|
||||
if tp.named {
|
||||
w.Write(name)
|
||||
if empty {
|
||||
w.Write(tp.ifemp)
|
||||
} else {
|
||||
w.Write("=")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *templatePart) expandString(w *pairWriter, t templateTerm, s string) {
|
||||
if len(s) > t.truncate && t.truncate > 0 {
|
||||
s = s[:t.truncate]
|
||||
}
|
||||
tp.expandName(w, t.name, len(s) == 0)
|
||||
w.Escape(s, tp.allowReserved)
|
||||
}
|
||||
17
vendor/google.golang.org/api/internal/third_party/uritemplates/utils.go
generated
vendored
Normal file
17
vendor/google.golang.org/api/internal/third_party/uritemplates/utils.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uritemplates
|
||||
|
||||
// Expand parses then expands a URI template with a set of values to produce
|
||||
// the resultant URI. Two forms of the result are returned: one with all the
|
||||
// elements escaped, and one with the elements unescaped.
|
||||
func Expand(path string, values map[string]string) (escaped, unescaped string, err error) {
|
||||
template, err := parse(path)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
escaped, unescaped = template.Expand(values)
|
||||
return escaped, unescaped, nil
|
||||
}
|
||||
8
vendor/google.golang.org/api/internal/version.go
generated
vendored
Normal file
8
vendor/google.golang.org/api/internal/version.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2022 Google LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
// Version is the current tagged release of the library.
|
||||
const Version = "0.186.0"
|
||||
Reference in New Issue
Block a user