From 5ff67cb04707113d984fad5cde8ec12325790078 Mon Sep 17 00:00:00 2001 From: The Magician Date: Thu, 15 Oct 2020 13:56:27 -0700 Subject: [PATCH] Implement Service Account Impersonation (#4015) (#551) * add service account impersonation * fix conflicts * add the scopes block removed by error * add service account impersonation * undo spacing * update docs for quota project * fix typos in docs * fix typo in test and the docs * use the new gce client * add impersonate to accesstoken + test * fix tokensource typo * replace the envs used for testing impersonation * add additional scopes * typo fix Signed-off-by: Modular Magician --- google/config.go | 54 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/google/config.go b/google/config.go index f4ec501e0..2d2175e28 100644 --- a/google/config.go +++ b/google/config.go @@ -48,6 +48,7 @@ import ( sqladmin "google.golang.org/api/sqladmin/v1beta4" "google.golang.org/api/storage/v1" "google.golang.org/api/storagetransfer/v1" + "google.golang.org/api/transport" ) type providerMeta struct { @@ -57,16 +58,18 @@ type providerMeta struct { // Config is the configuration structure used to instantiate the Google // provider. type Config struct { - Credentials string - AccessToken string - Project string - BillingProject string - Region string - Zone string - Scopes []string - BatchingConfig *batchingConfig - UserProjectOverride bool - RequestTimeout time.Duration + AccessToken string + Credentials string + ImpersonateServiceAccount string + ImpersonateServiceAccountDelegates []string + Project string + Region string + BillingProject string + Zone string + Scopes []string + BatchingConfig *batchingConfig + UserProjectOverride bool + RequestTimeout time.Duration // PollInterval is passed to resource.StateChangeConf in common_operation.go // It controls the interval at which we poll for successful operations PollInterval time.Duration @@ -838,15 +841,25 @@ type staticTokenSource struct { } func (c *Config) GetCredentials(clientScopes []string) (googleoauth.Credentials, error) { + if c.AccessToken != "" { contents, _, err := pathOrContents(c.AccessToken) if err != nil { return googleoauth.Credentials{}, fmt.Errorf("Error loading access token: %s", err) } + token := &oauth2.Token{AccessToken: contents} + + if c.ImpersonateServiceAccount != "" { + opts := []option.ClientOption{option.WithTokenSource(oauth2.StaticTokenSource(token)), option.ImpersonateCredentials(c.ImpersonateServiceAccount, c.ImpersonateServiceAccountDelegates...), option.WithScopes(clientScopes...)} + creds, err := transport.Creds(context.TODO(), opts...) + if err != nil { + return googleoauth.Credentials{}, err + } + return *creds, nil + } log.Printf("[INFO] Authenticating using configured Google JSON 'access_token'...") log.Printf("[INFO] -- Scopes: %s", clientScopes) - token := &oauth2.Token{AccessToken: contents} return googleoauth.Credentials{ TokenSource: staticTokenSource{oauth2.StaticTokenSource(token)}, @@ -858,7 +871,14 @@ func (c *Config) GetCredentials(clientScopes []string) (googleoauth.Credentials, if err != nil { return googleoauth.Credentials{}, fmt.Errorf("error loading credentials: %s", err) } - + if c.ImpersonateServiceAccount != "" { + opts := []option.ClientOption{option.WithCredentialsJSON([]byte(contents)), option.ImpersonateCredentials(c.ImpersonateServiceAccount, c.ImpersonateServiceAccountDelegates...), option.WithScopes(clientScopes...)} + creds, err := transport.Creds(context.TODO(), opts...) + if err != nil { + return googleoauth.Credentials{}, err + } + return *creds, nil + } creds, err := googleoauth.CredentialsFromJSON(c.context, []byte(contents), clientScopes...) if err != nil { return googleoauth.Credentials{}, fmt.Errorf("unable to parse credentials from '%s': %s", contents, err) @@ -869,6 +889,16 @@ func (c *Config) GetCredentials(clientScopes []string) (googleoauth.Credentials, return *creds, nil } + if c.ImpersonateServiceAccount != "" { + opts := option.ImpersonateCredentials(c.ImpersonateServiceAccount, c.ImpersonateServiceAccountDelegates...) + creds, err := transport.Creds(context.TODO(), opts, option.WithScopes(clientScopes...)) + if err != nil { + return googleoauth.Credentials{}, err + } + return *creds, nil + + } + log.Printf("[INFO] Authenticating using DefaultClient...") log.Printf("[INFO] -- Scopes: %s", clientScopes)