-
Notifications
You must be signed in to change notification settings - Fork 389
/
gcp.go
137 lines (128 loc) · 4.33 KB
/
gcp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package common
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"os"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/idtoken"
"google.golang.org/api/impersonate"
"google.golang.org/api/option"
)
// Configures an authorizer that uses credentials sourced from JSON or file.
// This auth mode does NOT use impersonation and it does NOT use Application
// Default Credentials (ADC).
func (c *DatabricksClient) configureWithGoogleCrendentials(
ctx context.Context) (func(r *http.Request) error, error) {
if c.GoogleCredentials == "" || !c.IsGcp() || c.Host == "" {
return nil, nil
}
json, err := readCredentials(c.GoogleCredentials)
if err != nil {
err = fmt.Errorf("could not read GoogleCredentials. "+
"Make sure the file exists, or the JSON content is valid: %w", err)
return nil, err
}
// Obtain token source for creating OIDC token.
audience := c.Host
oidcSource, err := idtoken.NewTokenSource(ctx, audience, option.WithCredentialsJSON([]byte(json)))
if err != nil {
return nil, fmt.Errorf("could not obtain OIDC token from JSON: %w", err)
}
// Obtain token source for creating Google Cloud Platform token.
creds, err := google.CredentialsFromJSON(ctx, []byte(json),
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/compute")
if err != nil {
return nil, fmt.Errorf("could not obtain OAuth2 token from JSON: %w", err)
}
return newOidcAuthorizerForAccountsAPI(oidcSource, creds.TokenSource), nil
}
// Reads credentials as JSON. Credentials can be either a path to JSON file,
// or actual JSON string.
func readCredentials(credentials string) (string, error) {
// Try to read credentials as file path.
if _, err := os.Stat(credentials); err == nil {
jsonContents, err := ioutil.ReadFile(credentials)
if err != nil {
return string(jsonContents), err
}
return string(jsonContents), nil
}
// Assume that credential is actually JSON string.
return credentials, nil
}
func (c *DatabricksClient) getGoogleOIDCSource(ctx context.Context) (oauth2.TokenSource, error) {
// source for generateIdToken
ts, err := impersonate.IDTokenSource(ctx, impersonate.IDTokenConfig{
Audience: c.Host,
TargetPrincipal: c.GoogleServiceAccount,
IncludeEmail: true,
}, c.googleAuthOptions...)
if err != nil {
err = fmt.Errorf("could not obtain OIDC token. %w Running 'gcloud auth application-default login' may help", err)
return nil, err
}
// TODO: verify that refreshers work...
ts = oauth2.ReuseTokenSource(nil, ts)
return ts, nil
}
func (c *DatabricksClient) configureWithGoogleForAccountsAPI(ctx context.Context) (func(*http.Request) error, error) {
if c.GoogleServiceAccount == "" || !c.IsGcp() || !c.isAccountsClient() {
return nil, nil
}
oidcSource, err := c.getGoogleOIDCSource(ctx)
if err != nil {
return nil, err
}
// source for generateAccessToken
platformSource, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{
TargetPrincipal: c.GoogleServiceAccount,
Scopes: []string{
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/compute",
},
}, c.googleAuthOptions...)
if err != nil {
return nil, err
}
return newOidcAuthorizerForAccountsAPI(oidcSource, platformSource), nil
}
func newOidcAuthorizerForAccountsAPI(oidcSource oauth2.TokenSource,
platformSource oauth2.TokenSource) func(r *http.Request) error {
return func(r *http.Request) error {
oidc, err := oidcSource.Token()
if err != nil {
return fmt.Errorf("failed to get oidc token: %w", err)
}
cloudAccess, err := platformSource.Token()
if err != nil {
return fmt.Errorf("failed to get access token: %w", err)
}
oidc.SetAuthHeader(r)
r.Header.Set("X-Databricks-GCP-SA-Access-Token", cloudAccess.AccessToken)
return nil
}
}
func (c *DatabricksClient) configureWithGoogleForWorkspace(ctx context.Context) (func(r *http.Request) error, error) {
if c.GoogleServiceAccount == "" || !c.IsGcp() || c.isAccountsClient() {
return nil, nil
}
oidcSource, err := c.getGoogleOIDCSource(ctx)
if err != nil {
return nil, err
}
return newOidcAuthorizerWithJustBearer(oidcSource), nil
}
func newOidcAuthorizerWithJustBearer(oidcSource oauth2.TokenSource) func(r *http.Request) error {
return func(r *http.Request) error {
oidc, err := oidcSource.Token()
if err != nil {
return err
}
oidc.SetAuthHeader(r)
return nil
}
}