1// Copyright 2017 Google LLC.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package internal supports the options and transport packages.
6package internal
7
8import (
9	"crypto/tls"
10	"errors"
11	"net/http"
12
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
19// DialSettings holds information needed to establish a connection with a
20// Google API service.
21type DialSettings struct {
22	Endpoint            string
23	DefaultEndpoint     string
24	DefaultMTLSEndpoint string
25	Scopes              []string
26	TokenSource         oauth2.TokenSource
27	Credentials         *google.Credentials
28	CredentialsFile     string // if set, Token Source is ignored.
29	CredentialsJSON     []byte
30	UserAgent           string
31	APIKey              string
32	Audiences           []string
33	HTTPClient          *http.Client
34	GRPCDialOpts        []grpc.DialOption
35	GRPCConn            *grpc.ClientConn
36	GRPCConnPool        ConnPool
37	GRPCConnPoolSize    int
38	NoAuth              bool
39	TelemetryDisabled   bool
40	ClientCertSource    func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
41	CustomClaims        map[string]interface{}
42	SkipValidation      bool
43	ImpersonationConfig *impersonate.Config
44
45	// Google API system parameters. For more information please read:
46	// https://cloud.google.com/apis/docs/system-parameters
47	QuotaProject  string
48	RequestReason string
49}
50
51// Validate reports an error if ds is invalid.
52func (ds *DialSettings) Validate() error {
53	if ds.SkipValidation {
54		return nil
55	}
56	hasCreds := ds.APIKey != "" || ds.TokenSource != nil || ds.CredentialsFile != "" || ds.Credentials != nil
57	if ds.NoAuth && hasCreds {
58		return errors.New("options.WithoutAuthentication is incompatible with any option that provides credentials")
59	}
60	// Credentials should not appear with other options.
61	// We currently allow TokenSource and CredentialsFile to coexist.
62	// TODO(jba): make TokenSource & CredentialsFile an error (breaking change).
63	nCreds := 0
64	if ds.Credentials != nil {
65		nCreds++
66	}
67	if ds.CredentialsJSON != nil {
68		nCreds++
69	}
70	if ds.CredentialsFile != "" {
71		nCreds++
72	}
73	if ds.APIKey != "" {
74		nCreds++
75	}
76	if ds.TokenSource != nil {
77		nCreds++
78	}
79	if len(ds.Scopes) > 0 && len(ds.Audiences) > 0 {
80		return errors.New("WithScopes is incompatible with WithAudience")
81	}
82	// Accept only one form of credentials, except we allow TokenSource and CredentialsFile for backwards compatibility.
83	if nCreds > 1 && !(nCreds == 2 && ds.TokenSource != nil && ds.CredentialsFile != "") {
84		return errors.New("multiple credential options provided")
85	}
86	if ds.GRPCConn != nil && ds.GRPCConnPool != nil {
87		return errors.New("WithGRPCConn is incompatible with WithConnPool")
88	}
89	if ds.HTTPClient != nil && ds.GRPCConnPool != nil {
90		return errors.New("WithHTTPClient is incompatible with WithConnPool")
91	}
92	if ds.HTTPClient != nil && ds.GRPCConn != nil {
93		return errors.New("WithHTTPClient is incompatible with WithGRPCConn")
94	}
95	if ds.HTTPClient != nil && ds.GRPCDialOpts != nil {
96		return errors.New("WithHTTPClient is incompatible with gRPC dial options")
97	}
98	if ds.HTTPClient != nil && ds.QuotaProject != "" {
99		return errors.New("WithHTTPClient is incompatible with QuotaProject")
100	}
101	if ds.HTTPClient != nil && ds.RequestReason != "" {
102		return errors.New("WithHTTPClient is incompatible with RequestReason")
103	}
104	if ds.HTTPClient != nil && ds.ClientCertSource != nil {
105		return errors.New("WithHTTPClient is incompatible with WithClientCertSource")
106	}
107	if ds.ClientCertSource != nil && (ds.GRPCConn != nil || ds.GRPCConnPool != nil || ds.GRPCConnPoolSize != 0 || ds.GRPCDialOpts != nil) {
108		return errors.New("WithClientCertSource is currently only supported for HTTP. gRPC settings are incompatible")
109	}
110	if ds.ImpersonationConfig != nil && len(ds.ImpersonationConfig.Scopes) == 0 && len(ds.Scopes) == 0 {
111		return errors.New("WithImpersonatedCredentials requires scopes being provided")
112	}
113	return nil
114}
115