From ff9f0cd227d6bc5a93005534713a8cd4686c82f4 Mon Sep 17 00:00:00 2001 From: salmaan rashid Date: Mon, 11 Mar 2019 08:49:44 -0700 Subject: [PATCH 1/9] Add google_impersonated_credential datasource --- ...a_source_google_impersonated_credential.go | 98 +++++++++++++++++++ ...rce_google_impersonated_credential_test.go | 50 ++++++++++ google/validation.go | 2 +- ...ogle_impersonated_credential.html.markdown | 84 ++++++++++++++++ 4 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 google/data_source_google_impersonated_credential.go create mode 100644 google/data_source_google_impersonated_credential_test.go create mode 100644 website/docs/d/google_impersonated_credential.html.markdown diff --git a/google/data_source_google_impersonated_credential.go b/google/data_source_google_impersonated_credential.go new file mode 100644 index 00000000000..30e72a3bbcd --- /dev/null +++ b/google/data_source_google_impersonated_credential.go @@ -0,0 +1,98 @@ +package google + +import ( + "context" + "fmt" + "log" + "net/http" + + "strings" + "time" + + "github.com/hashicorp/terraform/helper/schema" + "golang.org/x/oauth2" + iamcredentials "google.golang.org/api/iamcredentials/v1" +) + +func dataSourceGoogleImpersonatedCredential() *schema.Resource { + + return &schema.Resource{ + Read: dataSourceImpersonatedCredentialRead, + Schema: map[string]*schema.Schema{ + "source_access_token": { + Type: schema.TypeString, + Optional: true, + }, + "target_service_account": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateRegexp("(" + strings.Join(PossibleServiceAccountNames, "|") + ")"), + }, + "access_token": { + Type: schema.TypeString, + Sensitive: true, + Computed: true, + }, + "scopes": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + StateFunc: func(v interface{}) string { + return canonicalizeServiceScope(v.(string)) + }, + }, + // ValidateFunc is not yet supported on lists or sets. + }, + "delegates": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateRegexp(ServiceAccountLinkRegex), + }, + }, + "lifetime": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateDuration(), // duration <=3600s; TODO: support validteDuration(min,max) + Default: "3600s", + }, + }, + } +} + +func dataSourceImpersonatedCredentialRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + log.Printf("[INFO] Acquire Impersonated credentials for %s", d.Get("target_service_account").(string)) + + d.SetId(time.Now().UTC().String()) + var client *http.Client + if d.Get("source_access_token") != "" { + rootTokenSource := oauth2.StaticTokenSource(&oauth2.Token{ + AccessToken: d.Get("source_access_token").(string), + }) + client = oauth2.NewClient(context.TODO(), rootTokenSource) + } else { + client = config.client + } + + service, err := iamcredentials.New(client) + if err != nil { + return err + } + name := fmt.Sprintf("projects/-/serviceAccounts/%s", d.Get("target_service_account").(string)) + tokenRequest := &iamcredentials.GenerateAccessTokenRequest{ + Lifetime: d.Get("lifetime").(string), + Delegates: convertStringSet(d.Get("delegates").(*schema.Set)), + Scope: convertStringSet(d.Get("scopes").(*schema.Set)), + } + at, err := service.Projects.ServiceAccounts.GenerateAccessToken(name, tokenRequest).Do() + if err != nil { + return err + } + + d.Set("access_token", at.AccessToken) + + return nil +} diff --git a/google/data_source_google_impersonated_credential_test.go b/google/data_source_google_impersonated_credential_test.go new file mode 100644 index 00000000000..b4581f6954e --- /dev/null +++ b/google/data_source_google_impersonated_credential_test.go @@ -0,0 +1,50 @@ +package google + +import ( + "testing" + + "fmt" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataSourceGoogleImpersonatedCredential_basic(t *testing.T) { + t.Parallel() + + resourceName := "data.google_impersonated_credential.current" + + sourceAccessToken := "foo" + targetServiceAccount := getTestServiceAccountFromEnv(t) + scopes := []string{"https://www.googleapis.com/auth/cloud-platform"} + delegates := []string{"projects/-/serviceAccounts/impersonated-account@some-project-111.iam.gserviceaccount.com"} + lifetime := "30s" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleImpersonatedCredential_datasource(sourceAccessToken, targetServiceAccount, scopes, delegates, lifetime), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "source_access_token", sourceAccessToken), + resource.TestCheckResourceAttr(resourceName, "target_service_account", targetServiceAccount), + resource.TestCheckResourceAttrSet(resourceName, "scopes"), + resource.TestCheckResourceAttrSet(resourceName, "delegates"), + resource.TestCheckResourceAttr(resourceName, "lifetime", lifetime), + ), + }, + }, + }) +} + +func testAccCheckGoogleImpersonatedCredential_datasource(sourceAccessToken string, targetServiceAccount string, scopes []string, delegates []string, lifetime string) string { + return fmt.Sprintf(` + data "google_impersonated_credential" "current" { + source_access_token = "%s" + target_service_account = "%s" + scopes = "%s" + delegates = "%s" + lifetime = "%s" +} + `, sourceAccessToken, targetServiceAccount, scopes, delegates, lifetime) +} diff --git a/google/validation.go b/google/validation.go index ffd879529b8..ef717cfcbd0 100644 --- a/google/validation.go +++ b/google/validation.go @@ -14,7 +14,7 @@ import ( const ( // Copied from the official Google Cloud auto-generated client. - ProjectRegex = "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?))" + ProjectRegex = "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?)|-)" RegionRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?" SubnetworkRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?" diff --git a/website/docs/d/google_impersonated_credential.html.markdown b/website/docs/d/google_impersonated_credential.html.markdown new file mode 100644 index 00000000000..677364d3910 --- /dev/null +++ b/website/docs/d/google_impersonated_credential.html.markdown @@ -0,0 +1,84 @@ +--- +layout: "google" +page_title: "Google: google_impersonated_credential" +sidebar_current: "docs-google-impersonated-credential" +description: |- + Produces access_token for impersonated service accounts +--- + +# google\_impersonated\_credential + +This data source provides a google `oauth2` `access_token` for a different service account than the one initially running the script. You can +then use this new token to access resources the original caller would not have permissions on otherwise. + +For more information see +[the official documentation](https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials) as well as [iamcredentials.generateAccessToken()](https://cloud.google.com/iam/credentials/reference/rest/v1/projects.serviceAccounts/generateAccessToken) + +## Example Usage + +To allow `service_A` to impersonate `service_B`, grant the [Service Account Token Creator](https://cloud.google.com/iam/docs/service-accounts#the_service_account_token_creator_role) on B to A. + +In the IAM policy below, `service_A` is given the Token Creator role impersonate `service_B` + +```hcl +$ cat service_policy.json +{ +"bindings": [ + { + "members": [ + "service_A@projectA.iam.gserviceaccount.com " + ], + "role": "roles/iam.serviceAccountTokenCreator", + } + ] +} + +$ gcloud iam service-accounts set-iam-policy service_B@projectB.iam.gserviceaccount.com service_policy.json +``` + +Once the IAM permissions are set, you can apply the new token to a provider bootstrapped with it. Any resources that references the new provider will run as the new identity. + +In the example below, `google_project` will run as `service_B`. + +```hcl +provider "google" {} + +data "google_client_config" "default" { + provider = "google" +} + +data "google_impersonated_credential" "default" { + provider = "google" + target_service_account = "service_B@projectB.iam.gserviceaccount.com" + scopes = ["devstorage.read_only", "cloud-platform"] + lifetime = "300s" +} + +provider "google" { + alias = "impersonated" + access_token = "${data.google_impersonated_credential.default.access_token}" +} + +data "google_project" "project" { + provider = "google.impersonated" + project_id = "target-project" +} +``` + +> *Note*: the generated token is non-refreshable and can have a maximum `lifetime` of `3600` seconds. + +## Argument Reference + +The following arguments are supported: + +* `target_service_account` (Required) - The service account _to_ impersonate (e.g. `service_B@your-project-id.iam.gserviceaccount.com`) +* `scopes` (Required) - The scopes the new credential should have (e.g. `["devstorage.read_only", "cloud-platform"]`) +* `delegates` (Optional) - Deegate chain of approvals needed to perform full impersonation. Specify the fully qualified service account name. (e.g. `["projects/-/serviceAccounts/delegate-svc-account@project-id.iam.gserviceaccount.com"]`) +* `lifetime` (Optional) Lifetime of the impersonated token (defaults to its max: `3600s`). +* `source_access_token` (Optional) - The source token to bootstrap this module. + +## Attributes Reference + +The following attribute is exported: + +* `access_token` - The `access_token` representing the new generated identity. From d7e0ab6cdec3ba865d6e193538e964ad680dd88e Mon Sep 17 00:00:00 2001 From: salmaan rashid Date: Mon, 11 Mar 2019 09:35:58 -0700 Subject: [PATCH 2/9] vendor google.golang.org/api/iamcredentials/v1 ? --- go.mod | 5 +---- go.sum | 3 +++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 3a266ea6e3e..84a61611719 100644 --- a/go.mod +++ b/go.mod @@ -36,13 +36,10 @@ require ( github.com/stretchr/testify v1.3.0 // indirect github.com/terraform-providers/terraform-provider-random v2.0.0+incompatible github.com/zclconf/go-cty v0.0.0-20181218225846-4fe1e489ee06 // indirect - go.opencensus.io v0.18.0 // indirect golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 // indirect golang.org/x/net v0.0.0-20190119204137-ed066c81e75e // indirect golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 golang.org/x/sys v0.0.0-20190123074212-c6b37f3e9285 // indirect - google.golang.org/api v0.0.0-20181217000635-41dc4b66e69d - google.golang.org/appengine v1.3.0 // indirect + google.golang.org/api v0.1.0 google.golang.org/genproto v0.0.0-20181218023534-67d6565462c5 // indirect - google.golang.org/grpc v1.17.0 // indirect ) diff --git a/go.sum b/go.sum index b34685eb2eb..b40b9628bd7 100644 --- a/go.sum +++ b/go.sum @@ -187,11 +187,14 @@ golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGm google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181217000635-41dc4b66e69d h1:VhRqKr7/NDe5MpNpIj6Cy1xiwcVL4ZPs2GjTYziBRRg= google.golang.org/api v0.0.0-20181217000635-41dc4b66e69d/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20181218023534-67d6565462c5 h1:ZcmLUbATcwP+ZlsJ5pn5qeF9O3pC7U7mYpSnHH7RFFs= google.golang.org/genproto v0.0.0-20181218023534-67d6565462c5/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= From 230378d7aa6bbf74e8317464385f848bc52361ae Mon Sep 17 00:00:00 2001 From: salmaan rashid Date: Fri, 15 Mar 2019 14:13:12 -0700 Subject: [PATCH 3/9] add provider, tests --- go.mod | 5 +- go.sum | 3 -- ...a_source_google_impersonated_credential.go | 2 +- ...rce_google_impersonated_credential_test.go | 48 ++++++++++++------- google/provider.go | 1 + 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 84a61611719..3a266ea6e3e 100644 --- a/go.mod +++ b/go.mod @@ -36,10 +36,13 @@ require ( github.com/stretchr/testify v1.3.0 // indirect github.com/terraform-providers/terraform-provider-random v2.0.0+incompatible github.com/zclconf/go-cty v0.0.0-20181218225846-4fe1e489ee06 // indirect + go.opencensus.io v0.18.0 // indirect golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 // indirect golang.org/x/net v0.0.0-20190119204137-ed066c81e75e // indirect golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 golang.org/x/sys v0.0.0-20190123074212-c6b37f3e9285 // indirect - google.golang.org/api v0.1.0 + google.golang.org/api v0.0.0-20181217000635-41dc4b66e69d + google.golang.org/appengine v1.3.0 // indirect google.golang.org/genproto v0.0.0-20181218023534-67d6565462c5 // indirect + google.golang.org/grpc v1.17.0 // indirect ) diff --git a/go.sum b/go.sum index b40b9628bd7..b34685eb2eb 100644 --- a/go.sum +++ b/go.sum @@ -187,14 +187,11 @@ golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGm google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181217000635-41dc4b66e69d h1:VhRqKr7/NDe5MpNpIj6Cy1xiwcVL4ZPs2GjTYziBRRg= google.golang.org/api v0.0.0-20181217000635-41dc4b66e69d/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20181218023534-67d6565462c5 h1:ZcmLUbATcwP+ZlsJ5pn5qeF9O3pC7U7mYpSnHH7RFFs= google.golang.org/genproto v0.0.0-20181218023534-67d6565462c5/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= diff --git a/google/data_source_google_impersonated_credential.go b/google/data_source_google_impersonated_credential.go index 30e72a3bbcd..45cca211de6 100644 --- a/google/data_source_google_impersonated_credential.go +++ b/google/data_source_google_impersonated_credential.go @@ -85,7 +85,7 @@ func dataSourceImpersonatedCredentialRead(d *schema.ResourceData, meta interface tokenRequest := &iamcredentials.GenerateAccessTokenRequest{ Lifetime: d.Get("lifetime").(string), Delegates: convertStringSet(d.Get("delegates").(*schema.Set)), - Scope: convertStringSet(d.Get("scopes").(*schema.Set)), + Scope: canonicalizeServiceScopes(convertStringSet(d.Get("scopes").(*schema.Set))), } at, err := service.Projects.ServiceAccounts.GenerateAccessToken(name, tokenRequest).Do() if err != nil { diff --git a/google/data_source_google_impersonated_credential_test.go b/google/data_source_google_impersonated_credential_test.go index b4581f6954e..593831c1ba4 100644 --- a/google/data_source_google_impersonated_credential_test.go +++ b/google/data_source_google_impersonated_credential_test.go @@ -11,25 +11,22 @@ import ( func TestAccDataSourceGoogleImpersonatedCredential_basic(t *testing.T) { t.Parallel() - resourceName := "data.google_impersonated_credential.current" + resourceName := "data.google_impersonated_credential.default" - sourceAccessToken := "foo" targetServiceAccount := getTestServiceAccountFromEnv(t) - scopes := []string{"https://www.googleapis.com/auth/cloud-platform"} - delegates := []string{"projects/-/serviceAccounts/impersonated-account@some-project-111.iam.gserviceaccount.com"} + scopes := []string{"storage-ro", "https://www.googleapis.com/auth/cloud-platform"} + delegates := []string{} lifetime := "30s" + targetProject := getTestProjectFromEnv() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccCheckGoogleImpersonatedCredential_datasource(sourceAccessToken, targetServiceAccount, scopes, delegates, lifetime), + Config: testAccCheckGoogleImpersonatedCredential_datasource(targetServiceAccount, scopes, delegates, lifetime, targetProject), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "source_access_token", sourceAccessToken), resource.TestCheckResourceAttr(resourceName, "target_service_account", targetServiceAccount), - resource.TestCheckResourceAttrSet(resourceName, "scopes"), - resource.TestCheckResourceAttrSet(resourceName, "delegates"), resource.TestCheckResourceAttr(resourceName, "lifetime", lifetime), ), }, @@ -37,14 +34,31 @@ func TestAccDataSourceGoogleImpersonatedCredential_basic(t *testing.T) { }) } -func testAccCheckGoogleImpersonatedCredential_datasource(sourceAccessToken string, targetServiceAccount string, scopes []string, delegates []string, lifetime string) string { +func testAccCheckGoogleImpersonatedCredential_datasource(targetServiceAccount string, scopes []string, delegates []string, lifetime string, target_project string) string { return fmt.Sprintf(` - data "google_impersonated_credential" "current" { - source_access_token = "%s" - target_service_account = "%s" - scopes = "%s" - delegates = "%s" - lifetime = "%s" -} - `, sourceAccessToken, targetServiceAccount, scopes, delegates, lifetime) + + provider "google" {} + + data "google_client_config" "default" { + provider = "google" + } + + data "google_impersonated_credential" "default" { + provider = "google" + target_service_account = "%s" + scopes = ["storage-ro", "https://www.googleapis.com/auth/cloud-platform"] + lifetime = "%s" + } + + provider "google" { + alias = "impersonated" + access_token = "${data.google_impersonated_credential.default.access_token}" + } + + data "google_project" "project" { + provider = "google.impersonated" + project_id = "%s" + } + + `, targetServiceAccount, lifetime, target_project) } diff --git a/google/provider.go b/google/provider.go index cd3d3d4d973..b7af13fb0e8 100644 --- a/google/provider.go +++ b/google/provider.go @@ -105,6 +105,7 @@ func Provider() terraform.ResourceProvider { "google_container_registry_image": dataSourceGoogleContainerImage(), "google_iam_policy": dataSourceGoogleIamPolicy(), "google_iam_role": dataSourceGoogleIamRole(), + "google_impersonated_credential": dataSourceGoogleImpersonatedCredential(), "google_kms_secret": dataSourceGoogleKmsSecret(), "google_kms_key_ring": dataSourceGoogleKmsKeyRing(), "google_kms_crypto_key": dataSourceGoogleKmsCryptoKey(), From de4fc0867beb31ba4c96819be6d89929022cd067 Mon Sep 17 00:00:00 2001 From: salmaan rashid Date: Tue, 19 Mar 2019 10:48:38 -0700 Subject: [PATCH 4/9] try acctest --- ...rce_google_impersonated_credential_test.go | 67 +- .../iamcredentials/v1/iamcredentials-api.json | 428 ++++++ .../iamcredentials/v1/iamcredentials-gen.go | 1320 +++++++++++++++++ ...ogle_impersonated_credential.html.markdown | 18 +- 4 files changed, 1808 insertions(+), 25 deletions(-) create mode 100644 vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-api.json create mode 100644 vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-gen.go diff --git a/google/data_source_google_impersonated_credential_test.go b/google/data_source_google_impersonated_credential_test.go index 593831c1ba4..28c8ca9d226 100644 --- a/google/data_source_google_impersonated_credential_test.go +++ b/google/data_source_google_impersonated_credential_test.go @@ -5,6 +5,7 @@ import ( "fmt" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" ) @@ -13,52 +14,74 @@ func TestAccDataSourceGoogleImpersonatedCredential_basic(t *testing.T) { resourceName := "data.google_impersonated_credential.default" - targetServiceAccount := getTestServiceAccountFromEnv(t) - scopes := []string{"storage-ro", "https://www.googleapis.com/auth/cloud-platform"} - delegates := []string{} - lifetime := "30s" - targetProject := getTestProjectFromEnv() + sourceServiceAccountEmail := getTestServiceAccountFromEnv(t) + targetServiceAccountID := acctest.RandomWithPrefix("tf-test") + targetServiceAccountEmail := fmt.Sprintf( + "%s@%s.iam.gserviceaccount.com", + targetServiceAccountID, + getTestProjectFromEnv(), + ) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccCheckGoogleImpersonatedCredential_datasource(targetServiceAccount, scopes, delegates, lifetime, targetProject), + Config: testAccCheckGoogleImpersonatedCredential_datasource(sourceServiceAccountEmail, targetServiceAccountID), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "target_service_account", targetServiceAccount), - resource.TestCheckResourceAttr(resourceName, "lifetime", lifetime), + resource.TestCheckResourceAttr(resourceName, "target_service_account", targetServiceAccountEmail), + resource.TestCheckOutput("target-email", targetServiceAccountEmail), ), }, }, }) } -func testAccCheckGoogleImpersonatedCredential_datasource(targetServiceAccount string, scopes []string, delegates []string, lifetime string, target_project string) string { +func testAccCheckGoogleImpersonatedCredential_datasource(sourceServiceAccountEmail string, targetServiceAccountID string) string { + return fmt.Sprintf(` - provider "google" {} + provider "google" { + scopes = [ + "https://www.googleapis.com/auth/cloud-platform", + ] + } - data "google_client_config" "default" { - provider = "google" + resource "google_service_account" "targetSA" { + account_id = "%s" + } + + resource "google_service_account_iam_binding" "token-creator-iam" { + service_account_id = "${google_service_account.targetSA.name}" + role = "roles/iam.serviceAccountTokenCreator" + members = [ + "serviceAccount:%s", + ] } data "google_impersonated_credential" "default" { - provider = "google" - target_service_account = "%s" - scopes = ["storage-ro", "https://www.googleapis.com/auth/cloud-platform"] - lifetime = "%s" + target_service_account = "${google_service_account.targetSA.email}" + scopes = ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/cloud-platform"] + lifetime = "60s" + + depends_on = ["google_service_account_iam_binding.token-creator-iam"] } provider "google" { - alias = "impersonated" - access_token = "${data.google_impersonated_credential.default.access_token}" + alias = "impersonated" + scopes = [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/userinfo.email", + ] + access_token = "${data.google_impersonated_credential.default.access_token}" } - data "google_project" "project" { - provider = "google.impersonated" - project_id = "%s" + data "google_client_openid_userinfo" "me" { + provider = "google.impersonated" } - `, targetServiceAccount, lifetime, target_project) + output "target-email" { + value = "${data.google_client_openid_userinfo.me.email}" + } + `, targetServiceAccountID, sourceServiceAccountEmail) } diff --git a/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-api.json b/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-api.json new file mode 100644 index 00000000000..34c5af8798b --- /dev/null +++ b/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-api.json @@ -0,0 +1,428 @@ +{ + "auth": { + "oauth2": { + "scopes": { + "https://www.googleapis.com/auth/cloud-platform": { + "description": "View and manage your data across Google Cloud Platform services" + } + } + } + }, + "basePath": "", + "baseUrl": "https://iamcredentials.googleapis.com/", + "batchPath": "batch", + "canonicalName": "IAM Credentials", + "description": "Creates short-lived, limited-privilege credentials for IAM service accounts.", + "discoveryVersion": "v1", + "documentationLink": "https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials", + "fullyEncodeReservedExpansion": true, + "icons": { + "x16": "http://www.google.com/images/icons/product/search-16.gif", + "x32": "http://www.google.com/images/icons/product/search-32.gif" + }, + "id": "iamcredentials:v1", + "kind": "discovery#restDescription", + "name": "iamcredentials", + "ownerDomain": "google.com", + "ownerName": "Google", + "parameters": { + "$.xgafv": { + "description": "V1 error format.", + "enum": [ + "1", + "2" + ], + "enumDescriptions": [ + "v1 error format", + "v2 error format" + ], + "location": "query", + "type": "string" + }, + "access_token": { + "description": "OAuth access token.", + "location": "query", + "type": "string" + }, + "alt": { + "default": "json", + "description": "Data format for response.", + "enum": [ + "json", + "media", + "proto" + ], + "enumDescriptions": [ + "Responses with Content-Type of application/json", + "Media download with context-dependent Content-Type", + "Responses with Content-Type of application/x-protobuf" + ], + "location": "query", + "type": "string" + }, + "callback": { + "description": "JSONP", + "location": "query", + "type": "string" + }, + "fields": { + "description": "Selector specifying which fields to include in a partial response.", + "location": "query", + "type": "string" + }, + "key": { + "description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.", + "location": "query", + "type": "string" + }, + "oauth_token": { + "description": "OAuth 2.0 token for the current user.", + "location": "query", + "type": "string" + }, + "prettyPrint": { + "default": "true", + "description": "Returns response with indentations and line breaks.", + "location": "query", + "type": "boolean" + }, + "quotaUser": { + "description": "Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters.", + "location": "query", + "type": "string" + }, + "uploadType": { + "description": "Legacy upload protocol for media (e.g. \"media\", \"multipart\").", + "location": "query", + "type": "string" + }, + "upload_protocol": { + "description": "Upload protocol for media (e.g. \"raw\", \"multipart\").", + "location": "query", + "type": "string" + } + }, + "protocol": "rest", + "resources": { + "projects": { + "resources": { + "serviceAccounts": { + "methods": { + "generateAccessToken": { + "description": "Generates an OAuth 2.0 access token for a service account.", + "flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:generateAccessToken", + "httpMethod": "POST", + "id": "iamcredentials.projects.serviceAccounts.generateAccessToken", + "parameterOrder": [ + "name" + ], + "parameters": { + "name": { + "description": "The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + "location": "path", + "pattern": "^projects/[^/]+/serviceAccounts/[^/]+$", + "required": true, + "type": "string" + } + }, + "path": "v1/{+name}:generateAccessToken", + "request": { + "$ref": "GenerateAccessTokenRequest" + }, + "response": { + "$ref": "GenerateAccessTokenResponse" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform" + ] + }, + "generateIdToken": { + "description": "Generates an OpenID Connect ID token for a service account.", + "flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:generateIdToken", + "httpMethod": "POST", + "id": "iamcredentials.projects.serviceAccounts.generateIdToken", + "parameterOrder": [ + "name" + ], + "parameters": { + "name": { + "description": "The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + "location": "path", + "pattern": "^projects/[^/]+/serviceAccounts/[^/]+$", + "required": true, + "type": "string" + } + }, + "path": "v1/{+name}:generateIdToken", + "request": { + "$ref": "GenerateIdTokenRequest" + }, + "response": { + "$ref": "GenerateIdTokenResponse" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform" + ] + }, + "generateIdentityBindingAccessToken": { + "description": "", + "flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:generateIdentityBindingAccessToken", + "httpMethod": "POST", + "id": "iamcredentials.projects.serviceAccounts.generateIdentityBindingAccessToken", + "parameterOrder": [ + "name" + ], + "parameters": { + "name": { + "description": "The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + "location": "path", + "pattern": "^projects/[^/]+/serviceAccounts/[^/]+$", + "required": true, + "type": "string" + } + }, + "path": "v1/{+name}:generateIdentityBindingAccessToken", + "request": { + "$ref": "GenerateIdentityBindingAccessTokenRequest" + }, + "response": { + "$ref": "GenerateIdentityBindingAccessTokenResponse" + } + }, + "signBlob": { + "description": "Signs a blob using a service account's system-managed private key.", + "flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:signBlob", + "httpMethod": "POST", + "id": "iamcredentials.projects.serviceAccounts.signBlob", + "parameterOrder": [ + "name" + ], + "parameters": { + "name": { + "description": "The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + "location": "path", + "pattern": "^projects/[^/]+/serviceAccounts/[^/]+$", + "required": true, + "type": "string" + } + }, + "path": "v1/{+name}:signBlob", + "request": { + "$ref": "SignBlobRequest" + }, + "response": { + "$ref": "SignBlobResponse" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform" + ] + }, + "signJwt": { + "description": "Signs a JWT using a service account's system-managed private key.", + "flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:signJwt", + "httpMethod": "POST", + "id": "iamcredentials.projects.serviceAccounts.signJwt", + "parameterOrder": [ + "name" + ], + "parameters": { + "name": { + "description": "The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + "location": "path", + "pattern": "^projects/[^/]+/serviceAccounts/[^/]+$", + "required": true, + "type": "string" + } + }, + "path": "v1/{+name}:signJwt", + "request": { + "$ref": "SignJwtRequest" + }, + "response": { + "$ref": "SignJwtResponse" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform" + ] + } + } + } + } + } + }, + "revision": "20190222", + "rootUrl": "https://iamcredentials.googleapis.com/", + "schemas": { + "GenerateAccessTokenRequest": { + "id": "GenerateAccessTokenRequest", + "properties": { + "delegates": { + "description": "The sequence of service accounts in a delegation chain. Each service\naccount must be granted the `roles/iam.serviceAccountTokenCreator` role\non its next service account in the chain. The last service account in the\nchain must be granted the `roles/iam.serviceAccountTokenCreator` role\non the service account that is specified in the `name` field of the\nrequest.\n\nThe delegates must have the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + "items": { + "type": "string" + }, + "type": "array" + }, + "lifetime": { + "description": "The desired lifetime duration of the access token in seconds.\nMust be set to a value less than or equal to 3600 (1 hour). If a value is\nnot specified, the token's lifetime will be set to a default value of one\nhour.", + "format": "google-duration", + "type": "string" + }, + "scope": { + "description": "Code to identify the scopes to be included in the OAuth 2.0 access token.\nSee https://developers.google.com/identity/protocols/googlescopes for more\ninformation.\nAt least one value required.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "GenerateAccessTokenResponse": { + "id": "GenerateAccessTokenResponse", + "properties": { + "accessToken": { + "description": "The OAuth 2.0 access token.", + "type": "string" + }, + "expireTime": { + "description": "Token expiration time.\nThe expiration time is always set.", + "format": "google-datetime", + "type": "string" + } + }, + "type": "object" + }, + "GenerateIdTokenRequest": { + "id": "GenerateIdTokenRequest", + "properties": { + "audience": { + "description": "The audience for the token, such as the API or account that this token\ngrants access to.", + "type": "string" + }, + "delegates": { + "description": "The sequence of service accounts in a delegation chain. Each service\naccount must be granted the `roles/iam.serviceAccountTokenCreator` role\non its next service account in the chain. The last service account in the\nchain must be granted the `roles/iam.serviceAccountTokenCreator` role\non the service account that is specified in the `name` field of the\nrequest.\n\nThe delegates must have the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + "items": { + "type": "string" + }, + "type": "array" + }, + "includeEmail": { + "description": "Include the service account email in the token. If set to `true`, the\ntoken will contain `email` and `email_verified` claims.", + "type": "boolean" + } + }, + "type": "object" + }, + "GenerateIdTokenResponse": { + "id": "GenerateIdTokenResponse", + "properties": { + "token": { + "description": "The OpenId Connect ID token.", + "type": "string" + } + }, + "type": "object" + }, + "GenerateIdentityBindingAccessTokenRequest": { + "id": "GenerateIdentityBindingAccessTokenRequest", + "properties": { + "jwt": { + "description": "Required. Input token.\nMust be in JWT format according to\nRFC7523 (https://tools.ietf.org/html/rfc7523)\nand must have 'kid' field in the header.\nSupported signing algorithms: RS256 (RS512, ES256, ES512 coming soon).\nMandatory payload fields (along the lines of RFC 7523, section 3):\n- iss: issuer of the token. Must provide a discovery document at\n $iss/.well-known/openid-configuration . The document needs to be\n formatted according to section 4.2 of the OpenID Connect Discovery\n 1.0 specification.\n- iat: Issue time in seconds since epoch. Must be in the past.\n- exp: Expiration time in seconds since epoch. Must be less than 48 hours\n after iat. We recommend to create tokens that last shorter than 6\n hours to improve security unless business reasons mandate longer\n expiration times. Shorter token lifetimes are generally more secure\n since tokens that have been exfiltrated by attackers can be used for\n a shorter time. you can configure the maximum lifetime of the\n incoming token in the configuration of the mapper.\n The resulting Google token will expire within an hour or at \"exp\",\n whichever is earlier.\n- sub: JWT subject, identity asserted in the JWT.\n- aud: Configured in the mapper policy. By default the service account\n email.\n\nClaims from the incoming token can be transferred into the output token\naccoding to the mapper configuration. The outgoing claim size is limited.\nOutgoing claims size must be less than 4kB serialized as JSON without\nwhitespace.\n\nExample header:\n{\n \"alg\": \"RS256\",\n \"kid\": \"92a4265e14ab04d4d228a48d10d4ca31610936f8\"\n}\nExample payload:\n{\n \"iss\": \"https://accounts.google.com\",\n \"iat\": 1517963104,\n \"exp\": 1517966704,\n \"aud\":\n \"https://iamcredentials.googleapis.com/google.iam.credentials.v1.CloudGaia\",\n \"sub\": \"113475438248934895348\",\n \"my_claims\": {\n \"additional_claim\": \"value\"\n }\n}", + "type": "string" + }, + "scope": { + "description": "Code to identify the scopes to be included in the OAuth 2.0 access token.\nSee https://developers.google.com/identity/protocols/googlescopes for more\ninformation.\nAt least one value required.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "GenerateIdentityBindingAccessTokenResponse": { + "id": "GenerateIdentityBindingAccessTokenResponse", + "properties": { + "accessToken": { + "description": "The OAuth 2.0 access token.", + "type": "string" + }, + "expireTime": { + "description": "Token expiration time.\nThe expiration time is always set.", + "format": "google-datetime", + "type": "string" + } + }, + "type": "object" + }, + "SignBlobRequest": { + "id": "SignBlobRequest", + "properties": { + "delegates": { + "description": "The sequence of service accounts in a delegation chain. Each service\naccount must be granted the `roles/iam.serviceAccountTokenCreator` role\non its next service account in the chain. The last service account in the\nchain must be granted the `roles/iam.serviceAccountTokenCreator` role\non the service account that is specified in the `name` field of the\nrequest.\n\nThe delegates must have the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + "items": { + "type": "string" + }, + "type": "array" + }, + "payload": { + "description": "The bytes to sign.", + "format": "byte", + "type": "string" + } + }, + "type": "object" + }, + "SignBlobResponse": { + "id": "SignBlobResponse", + "properties": { + "keyId": { + "description": "The ID of the key used to sign the blob.", + "type": "string" + }, + "signedBlob": { + "description": "The signed blob.", + "format": "byte", + "type": "string" + } + }, + "type": "object" + }, + "SignJwtRequest": { + "id": "SignJwtRequest", + "properties": { + "delegates": { + "description": "The sequence of service accounts in a delegation chain. Each service\naccount must be granted the `roles/iam.serviceAccountTokenCreator` role\non its next service account in the chain. The last service account in the\nchain must be granted the `roles/iam.serviceAccountTokenCreator` role\non the service account that is specified in the `name` field of the\nrequest.\n\nThe delegates must have the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + "items": { + "type": "string" + }, + "type": "array" + }, + "payload": { + "description": "The JWT payload to sign: a JSON object that contains a JWT Claims Set.", + "type": "string" + } + }, + "type": "object" + }, + "SignJwtResponse": { + "id": "SignJwtResponse", + "properties": { + "keyId": { + "description": "The ID of the key used to sign the JWT.", + "type": "string" + }, + "signedJwt": { + "description": "The signed JWT.", + "type": "string" + } + }, + "type": "object" + } + }, + "servicePath": "", + "title": "IAM Service Account Credentials API", + "version": "v1", + "version_module": true +} \ No newline at end of file diff --git a/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-gen.go b/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-gen.go new file mode 100644 index 00000000000..3c3e0c86036 --- /dev/null +++ b/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-gen.go @@ -0,0 +1,1320 @@ +// Copyright 2019 Google LLC. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated file. DO NOT EDIT. + +// Package iamcredentials provides access to the IAM Service Account Credentials API. +// +// For product documentation, see: https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials +// +// Creating a client +// +// Usage example: +// +// import "google.golang.org/api/iamcredentials/v1" +// ... +// ctx := context.Background() +// iamcredentialsService, err := iamcredentials.NewService(ctx) +// +// In this example, Google Application Default Credentials are used for authentication. +// +// For information on how to create and obtain Application Default Credentials, see https://developers.google.com/identity/protocols/application-default-credentials. +// +// Other authentication options +// +// To use an API key for authentication (note: some APIs do not support API keys), use option.WithAPIKey: +// +// iamcredentialsService, err := iamcredentials.NewService(ctx, option.WithAPIKey("AIza...")) +// +// To use an OAuth token (e.g., a user token obtained via a three-legged OAuth flow), use option.WithTokenSource: +// +// config := &oauth2.Config{...} +// // ... +// token, err := config.Exchange(ctx, ...) +// iamcredentialsService, err := iamcredentials.NewService(ctx, option.WithTokenSource(config.TokenSource(ctx, token))) +// +// See https://godoc.org/google.golang.org/api/option/ for details on options. +package iamcredentials // import "google.golang.org/api/iamcredentials/v1" + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "strings" + + gensupport "google.golang.org/api/gensupport" + googleapi "google.golang.org/api/googleapi" + option "google.golang.org/api/option" + htransport "google.golang.org/api/transport/http" +) + +// Always reference these packages, just in case the auto-generated code +// below doesn't. +var _ = bytes.NewBuffer +var _ = strconv.Itoa +var _ = fmt.Sprintf +var _ = json.NewDecoder +var _ = io.Copy +var _ = url.Parse +var _ = gensupport.MarshalJSON +var _ = googleapi.Version +var _ = errors.New +var _ = strings.Replace +var _ = context.Canceled + +const apiId = "iamcredentials:v1" +const apiName = "iamcredentials" +const apiVersion = "v1" +const basePath = "https://iamcredentials.googleapis.com/" + +// OAuth2 scopes used by this API. +const ( + // View and manage your data across Google Cloud Platform services + CloudPlatformScope = "https://www.googleapis.com/auth/cloud-platform" +) + +// NewService creates a new Service. +func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) { + scopesOption := option.WithScopes( + "https://www.googleapis.com/auth/cloud-platform", + ) + // NOTE: prepend, so we don't override user-specified scopes. + opts = append([]option.ClientOption{scopesOption}, opts...) + client, endpoint, err := htransport.NewClient(ctx, opts...) + if err != nil { + return nil, err + } + s, err := New(client) + if err != nil { + return nil, err + } + if endpoint != "" { + s.BasePath = endpoint + } + return s, nil +} + +// New creates a new Service. It uses the provided http.Client for requests. +// +// Deprecated: please use NewService instead. +// To provide a custom HTTP client, use option.WithHTTPClient. +// If you are using google.golang.org/api/googleapis/transport.APIKey, use option.WithAPIKey with NewService instead. +func New(client *http.Client) (*Service, error) { + if client == nil { + return nil, errors.New("client is nil") + } + s := &Service{client: client, BasePath: basePath} + s.Projects = NewProjectsService(s) + return s, nil +} + +type Service struct { + client *http.Client + BasePath string // API endpoint base URL + UserAgent string // optional additional User-Agent fragment + + Projects *ProjectsService +} + +func (s *Service) userAgent() string { + if s.UserAgent == "" { + return googleapi.UserAgent + } + return googleapi.UserAgent + " " + s.UserAgent +} + +func NewProjectsService(s *Service) *ProjectsService { + rs := &ProjectsService{s: s} + rs.ServiceAccounts = NewProjectsServiceAccountsService(s) + return rs +} + +type ProjectsService struct { + s *Service + + ServiceAccounts *ProjectsServiceAccountsService +} + +func NewProjectsServiceAccountsService(s *Service) *ProjectsServiceAccountsService { + rs := &ProjectsServiceAccountsService{s: s} + return rs +} + +type ProjectsServiceAccountsService struct { + s *Service +} + +type GenerateAccessTokenRequest struct { + // Delegates: The sequence of service accounts in a delegation chain. + // Each service + // account must be granted the `roles/iam.serviceAccountTokenCreator` + // role + // on its next service account in the chain. The last service account in + // the + // chain must be granted the `roles/iam.serviceAccountTokenCreator` + // role + // on the service account that is specified in the `name` field of + // the + // request. + // + // The delegates must have the following + // format: + // `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` + // wildcard + // character is required; replacing it with a project ID is invalid. + Delegates []string `json:"delegates,omitempty"` + + // Lifetime: The desired lifetime duration of the access token in + // seconds. + // Must be set to a value less than or equal to 3600 (1 hour). If a + // value is + // not specified, the token's lifetime will be set to a default value of + // one + // hour. + Lifetime string `json:"lifetime,omitempty"` + + // Scope: Code to identify the scopes to be included in the OAuth 2.0 + // access token. + // See https://developers.google.com/identity/protocols/googlescopes for + // more + // information. + // At least one value required. + Scope []string `json:"scope,omitempty"` + + // ForceSendFields is a list of field names (e.g. "Delegates") to + // unconditionally include in API requests. By default, fields with + // empty values are omitted from API requests. However, any non-pointer, + // non-interface field appearing in ForceSendFields will be sent to the + // server regardless of whether the field is empty or not. This may be + // used to include empty fields in Patch requests. + ForceSendFields []string `json:"-"` + + // NullFields is a list of field names (e.g. "Delegates") to include in + // API requests with the JSON null value. By default, fields with empty + // values are omitted from API requests. However, any field with an + // empty value appearing in NullFields will be sent to the server as + // null. It is an error if a field in this list has a non-empty value. + // This may be used to include null fields in Patch requests. + NullFields []string `json:"-"` +} + +func (s *GenerateAccessTokenRequest) MarshalJSON() ([]byte, error) { + type NoMethod GenerateAccessTokenRequest + raw := NoMethod(*s) + return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields) +} + +type GenerateAccessTokenResponse struct { + // AccessToken: The OAuth 2.0 access token. + AccessToken string `json:"accessToken,omitempty"` + + // ExpireTime: Token expiration time. + // The expiration time is always set. + ExpireTime string `json:"expireTime,omitempty"` + + // ServerResponse contains the HTTP response code and headers from the + // server. + googleapi.ServerResponse `json:"-"` + + // ForceSendFields is a list of field names (e.g. "AccessToken") to + // unconditionally include in API requests. By default, fields with + // empty values are omitted from API requests. However, any non-pointer, + // non-interface field appearing in ForceSendFields will be sent to the + // server regardless of whether the field is empty or not. This may be + // used to include empty fields in Patch requests. + ForceSendFields []string `json:"-"` + + // NullFields is a list of field names (e.g. "AccessToken") to include + // in API requests with the JSON null value. By default, fields with + // empty values are omitted from API requests. However, any field with + // an empty value appearing in NullFields will be sent to the server as + // null. It is an error if a field in this list has a non-empty value. + // This may be used to include null fields in Patch requests. + NullFields []string `json:"-"` +} + +func (s *GenerateAccessTokenResponse) MarshalJSON() ([]byte, error) { + type NoMethod GenerateAccessTokenResponse + raw := NoMethod(*s) + return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields) +} + +type GenerateIdTokenRequest struct { + // Audience: The audience for the token, such as the API or account that + // this token + // grants access to. + Audience string `json:"audience,omitempty"` + + // Delegates: The sequence of service accounts in a delegation chain. + // Each service + // account must be granted the `roles/iam.serviceAccountTokenCreator` + // role + // on its next service account in the chain. The last service account in + // the + // chain must be granted the `roles/iam.serviceAccountTokenCreator` + // role + // on the service account that is specified in the `name` field of + // the + // request. + // + // The delegates must have the following + // format: + // `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` + // wildcard + // character is required; replacing it with a project ID is invalid. + Delegates []string `json:"delegates,omitempty"` + + // IncludeEmail: Include the service account email in the token. If set + // to `true`, the + // token will contain `email` and `email_verified` claims. + IncludeEmail bool `json:"includeEmail,omitempty"` + + // ForceSendFields is a list of field names (e.g. "Audience") to + // unconditionally include in API requests. By default, fields with + // empty values are omitted from API requests. However, any non-pointer, + // non-interface field appearing in ForceSendFields will be sent to the + // server regardless of whether the field is empty or not. This may be + // used to include empty fields in Patch requests. + ForceSendFields []string `json:"-"` + + // NullFields is a list of field names (e.g. "Audience") to include in + // API requests with the JSON null value. By default, fields with empty + // values are omitted from API requests. However, any field with an + // empty value appearing in NullFields will be sent to the server as + // null. It is an error if a field in this list has a non-empty value. + // This may be used to include null fields in Patch requests. + NullFields []string `json:"-"` +} + +func (s *GenerateIdTokenRequest) MarshalJSON() ([]byte, error) { + type NoMethod GenerateIdTokenRequest + raw := NoMethod(*s) + return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields) +} + +type GenerateIdTokenResponse struct { + // Token: The OpenId Connect ID token. + Token string `json:"token,omitempty"` + + // ServerResponse contains the HTTP response code and headers from the + // server. + googleapi.ServerResponse `json:"-"` + + // ForceSendFields is a list of field names (e.g. "Token") to + // unconditionally include in API requests. By default, fields with + // empty values are omitted from API requests. However, any non-pointer, + // non-interface field appearing in ForceSendFields will be sent to the + // server regardless of whether the field is empty or not. This may be + // used to include empty fields in Patch requests. + ForceSendFields []string `json:"-"` + + // NullFields is a list of field names (e.g. "Token") to include in API + // requests with the JSON null value. By default, fields with empty + // values are omitted from API requests. However, any field with an + // empty value appearing in NullFields will be sent to the server as + // null. It is an error if a field in this list has a non-empty value. + // This may be used to include null fields in Patch requests. + NullFields []string `json:"-"` +} + +func (s *GenerateIdTokenResponse) MarshalJSON() ([]byte, error) { + type NoMethod GenerateIdTokenResponse + raw := NoMethod(*s) + return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields) +} + +type GenerateIdentityBindingAccessTokenRequest struct { + // Jwt: Required. Input token. + // Must be in JWT format according to + // RFC7523 (https://tools.ietf.org/html/rfc7523) + // and must have 'kid' field in the header. + // Supported signing algorithms: RS256 (RS512, ES256, ES512 coming + // soon). + // Mandatory payload fields (along the lines of RFC 7523, section 3): + // - iss: issuer of the token. Must provide a discovery document at + // $iss/.well-known/openid-configuration . The document needs to + // be + // formatted according to section 4.2 of the OpenID Connect + // Discovery + // 1.0 specification. + // - iat: Issue time in seconds since epoch. Must be in the past. + // - exp: Expiration time in seconds since epoch. Must be less than 48 + // hours + // after iat. We recommend to create tokens that last shorter + // than 6 + // hours to improve security unless business reasons mandate + // longer + // expiration times. Shorter token lifetimes are generally more + // secure + // since tokens that have been exfiltrated by attackers can be + // used for + // a shorter time. you can configure the maximum lifetime of the + // incoming token in the configuration of the mapper. + // The resulting Google token will expire within an hour or at + // "exp", + // whichever is earlier. + // - sub: JWT subject, identity asserted in the JWT. + // - aud: Configured in the mapper policy. By default the service + // account + // email. + // + // Claims from the incoming token can be transferred into the output + // token + // accoding to the mapper configuration. The outgoing claim size is + // limited. + // Outgoing claims size must be less than 4kB serialized as JSON + // without + // whitespace. + // + // Example header: + // { + // "alg": "RS256", + // "kid": "92a4265e14ab04d4d228a48d10d4ca31610936f8" + // } + // Example payload: + // { + // "iss": "https://accounts.google.com", + // "iat": 1517963104, + // "exp": 1517966704, + // "aud": + // + // "https://iamcredentials.googleapis.com/google.iam.credentials.v1.Cloud + // Gaia", + // "sub": "113475438248934895348", + // "my_claims": { + // "additional_claim": "value" + // } + // } + Jwt string `json:"jwt,omitempty"` + + // Scope: Code to identify the scopes to be included in the OAuth 2.0 + // access token. + // See https://developers.google.com/identity/protocols/googlescopes for + // more + // information. + // At least one value required. + Scope []string `json:"scope,omitempty"` + + // ForceSendFields is a list of field names (e.g. "Jwt") to + // unconditionally include in API requests. By default, fields with + // empty values are omitted from API requests. However, any non-pointer, + // non-interface field appearing in ForceSendFields will be sent to the + // server regardless of whether the field is empty or not. This may be + // used to include empty fields in Patch requests. + ForceSendFields []string `json:"-"` + + // NullFields is a list of field names (e.g. "Jwt") to include in API + // requests with the JSON null value. By default, fields with empty + // values are omitted from API requests. However, any field with an + // empty value appearing in NullFields will be sent to the server as + // null. It is an error if a field in this list has a non-empty value. + // This may be used to include null fields in Patch requests. + NullFields []string `json:"-"` +} + +func (s *GenerateIdentityBindingAccessTokenRequest) MarshalJSON() ([]byte, error) { + type NoMethod GenerateIdentityBindingAccessTokenRequest + raw := NoMethod(*s) + return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields) +} + +type GenerateIdentityBindingAccessTokenResponse struct { + // AccessToken: The OAuth 2.0 access token. + AccessToken string `json:"accessToken,omitempty"` + + // ExpireTime: Token expiration time. + // The expiration time is always set. + ExpireTime string `json:"expireTime,omitempty"` + + // ServerResponse contains the HTTP response code and headers from the + // server. + googleapi.ServerResponse `json:"-"` + + // ForceSendFields is a list of field names (e.g. "AccessToken") to + // unconditionally include in API requests. By default, fields with + // empty values are omitted from API requests. However, any non-pointer, + // non-interface field appearing in ForceSendFields will be sent to the + // server regardless of whether the field is empty or not. This may be + // used to include empty fields in Patch requests. + ForceSendFields []string `json:"-"` + + // NullFields is a list of field names (e.g. "AccessToken") to include + // in API requests with the JSON null value. By default, fields with + // empty values are omitted from API requests. However, any field with + // an empty value appearing in NullFields will be sent to the server as + // null. It is an error if a field in this list has a non-empty value. + // This may be used to include null fields in Patch requests. + NullFields []string `json:"-"` +} + +func (s *GenerateIdentityBindingAccessTokenResponse) MarshalJSON() ([]byte, error) { + type NoMethod GenerateIdentityBindingAccessTokenResponse + raw := NoMethod(*s) + return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields) +} + +type SignBlobRequest struct { + // Delegates: The sequence of service accounts in a delegation chain. + // Each service + // account must be granted the `roles/iam.serviceAccountTokenCreator` + // role + // on its next service account in the chain. The last service account in + // the + // chain must be granted the `roles/iam.serviceAccountTokenCreator` + // role + // on the service account that is specified in the `name` field of + // the + // request. + // + // The delegates must have the following + // format: + // `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` + // wildcard + // character is required; replacing it with a project ID is invalid. + Delegates []string `json:"delegates,omitempty"` + + // Payload: The bytes to sign. + Payload string `json:"payload,omitempty"` + + // ForceSendFields is a list of field names (e.g. "Delegates") to + // unconditionally include in API requests. By default, fields with + // empty values are omitted from API requests. However, any non-pointer, + // non-interface field appearing in ForceSendFields will be sent to the + // server regardless of whether the field is empty or not. This may be + // used to include empty fields in Patch requests. + ForceSendFields []string `json:"-"` + + // NullFields is a list of field names (e.g. "Delegates") to include in + // API requests with the JSON null value. By default, fields with empty + // values are omitted from API requests. However, any field with an + // empty value appearing in NullFields will be sent to the server as + // null. It is an error if a field in this list has a non-empty value. + // This may be used to include null fields in Patch requests. + NullFields []string `json:"-"` +} + +func (s *SignBlobRequest) MarshalJSON() ([]byte, error) { + type NoMethod SignBlobRequest + raw := NoMethod(*s) + return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields) +} + +type SignBlobResponse struct { + // KeyId: The ID of the key used to sign the blob. + KeyId string `json:"keyId,omitempty"` + + // SignedBlob: The signed blob. + SignedBlob string `json:"signedBlob,omitempty"` + + // ServerResponse contains the HTTP response code and headers from the + // server. + googleapi.ServerResponse `json:"-"` + + // ForceSendFields is a list of field names (e.g. "KeyId") to + // unconditionally include in API requests. By default, fields with + // empty values are omitted from API requests. However, any non-pointer, + // non-interface field appearing in ForceSendFields will be sent to the + // server regardless of whether the field is empty or not. This may be + // used to include empty fields in Patch requests. + ForceSendFields []string `json:"-"` + + // NullFields is a list of field names (e.g. "KeyId") to include in API + // requests with the JSON null value. By default, fields with empty + // values are omitted from API requests. However, any field with an + // empty value appearing in NullFields will be sent to the server as + // null. It is an error if a field in this list has a non-empty value. + // This may be used to include null fields in Patch requests. + NullFields []string `json:"-"` +} + +func (s *SignBlobResponse) MarshalJSON() ([]byte, error) { + type NoMethod SignBlobResponse + raw := NoMethod(*s) + return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields) +} + +type SignJwtRequest struct { + // Delegates: The sequence of service accounts in a delegation chain. + // Each service + // account must be granted the `roles/iam.serviceAccountTokenCreator` + // role + // on its next service account in the chain. The last service account in + // the + // chain must be granted the `roles/iam.serviceAccountTokenCreator` + // role + // on the service account that is specified in the `name` field of + // the + // request. + // + // The delegates must have the following + // format: + // `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` + // wildcard + // character is required; replacing it with a project ID is invalid. + Delegates []string `json:"delegates,omitempty"` + + // Payload: The JWT payload to sign: a JSON object that contains a JWT + // Claims Set. + Payload string `json:"payload,omitempty"` + + // ForceSendFields is a list of field names (e.g. "Delegates") to + // unconditionally include in API requests. By default, fields with + // empty values are omitted from API requests. However, any non-pointer, + // non-interface field appearing in ForceSendFields will be sent to the + // server regardless of whether the field is empty or not. This may be + // used to include empty fields in Patch requests. + ForceSendFields []string `json:"-"` + + // NullFields is a list of field names (e.g. "Delegates") to include in + // API requests with the JSON null value. By default, fields with empty + // values are omitted from API requests. However, any field with an + // empty value appearing in NullFields will be sent to the server as + // null. It is an error if a field in this list has a non-empty value. + // This may be used to include null fields in Patch requests. + NullFields []string `json:"-"` +} + +func (s *SignJwtRequest) MarshalJSON() ([]byte, error) { + type NoMethod SignJwtRequest + raw := NoMethod(*s) + return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields) +} + +type SignJwtResponse struct { + // KeyId: The ID of the key used to sign the JWT. + KeyId string `json:"keyId,omitempty"` + + // SignedJwt: The signed JWT. + SignedJwt string `json:"signedJwt,omitempty"` + + // ServerResponse contains the HTTP response code and headers from the + // server. + googleapi.ServerResponse `json:"-"` + + // ForceSendFields is a list of field names (e.g. "KeyId") to + // unconditionally include in API requests. By default, fields with + // empty values are omitted from API requests. However, any non-pointer, + // non-interface field appearing in ForceSendFields will be sent to the + // server regardless of whether the field is empty or not. This may be + // used to include empty fields in Patch requests. + ForceSendFields []string `json:"-"` + + // NullFields is a list of field names (e.g. "KeyId") to include in API + // requests with the JSON null value. By default, fields with empty + // values are omitted from API requests. However, any field with an + // empty value appearing in NullFields will be sent to the server as + // null. It is an error if a field in this list has a non-empty value. + // This may be used to include null fields in Patch requests. + NullFields []string `json:"-"` +} + +func (s *SignJwtResponse) MarshalJSON() ([]byte, error) { + type NoMethod SignJwtResponse + raw := NoMethod(*s) + return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields) +} + +// method id "iamcredentials.projects.serviceAccounts.generateAccessToken": + +type ProjectsServiceAccountsGenerateAccessTokenCall struct { + s *Service + name string + generateaccesstokenrequest *GenerateAccessTokenRequest + urlParams_ gensupport.URLParams + ctx_ context.Context + header_ http.Header +} + +// GenerateAccessToken: Generates an OAuth 2.0 access token for a +// service account. +func (r *ProjectsServiceAccountsService) GenerateAccessToken(name string, generateaccesstokenrequest *GenerateAccessTokenRequest) *ProjectsServiceAccountsGenerateAccessTokenCall { + c := &ProjectsServiceAccountsGenerateAccessTokenCall{s: r.s, urlParams_: make(gensupport.URLParams)} + c.name = name + c.generateaccesstokenrequest = generateaccesstokenrequest + return c +} + +// Fields allows partial responses to be retrieved. See +// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse +// for more information. +func (c *ProjectsServiceAccountsGenerateAccessTokenCall) Fields(s ...googleapi.Field) *ProjectsServiceAccountsGenerateAccessTokenCall { + c.urlParams_.Set("fields", googleapi.CombineFields(s)) + return c +} + +// Context sets the context to be used in this call's Do method. Any +// pending HTTP request will be aborted if the provided context is +// canceled. +func (c *ProjectsServiceAccountsGenerateAccessTokenCall) Context(ctx context.Context) *ProjectsServiceAccountsGenerateAccessTokenCall { + c.ctx_ = ctx + return c +} + +// Header returns an http.Header that can be modified by the caller to +// add HTTP headers to the request. +func (c *ProjectsServiceAccountsGenerateAccessTokenCall) Header() http.Header { + if c.header_ == nil { + c.header_ = make(http.Header) + } + return c.header_ +} + +func (c *ProjectsServiceAccountsGenerateAccessTokenCall) doRequest(alt string) (*http.Response, error) { + reqHeaders := make(http.Header) + for k, v := range c.header_ { + reqHeaders[k] = v + } + reqHeaders.Set("User-Agent", c.s.userAgent()) + var body io.Reader = nil + body, err := googleapi.WithoutDataWrapper.JSONReader(c.generateaccesstokenrequest) + if err != nil { + return nil, err + } + reqHeaders.Set("Content-Type", "application/json") + c.urlParams_.Set("alt", alt) + c.urlParams_.Set("prettyPrint", "false") + urls := googleapi.ResolveRelative(c.s.BasePath, "v1/{+name}:generateAccessToken") + urls += "?" + c.urlParams_.Encode() + req, err := http.NewRequest("POST", urls, body) + if err != nil { + return nil, err + } + req.Header = reqHeaders + googleapi.Expand(req.URL, map[string]string{ + "name": c.name, + }) + return gensupport.SendRequest(c.ctx_, c.s.client, req) +} + +// Do executes the "iamcredentials.projects.serviceAccounts.generateAccessToken" call. +// Exactly one of *GenerateAccessTokenResponse or error will be non-nil. +// Any non-2xx status code is an error. Response headers are in either +// *GenerateAccessTokenResponse.ServerResponse.Header or (if a response +// was returned at all) in error.(*googleapi.Error).Header. Use +// googleapi.IsNotModified to check whether the returned error was +// because http.StatusNotModified was returned. +func (c *ProjectsServiceAccountsGenerateAccessTokenCall) Do(opts ...googleapi.CallOption) (*GenerateAccessTokenResponse, error) { + gensupport.SetOptions(c.urlParams_, opts...) + res, err := c.doRequest("json") + if res != nil && res.StatusCode == http.StatusNotModified { + if res.Body != nil { + res.Body.Close() + } + return nil, &googleapi.Error{ + Code: res.StatusCode, + Header: res.Header, + } + } + if err != nil { + return nil, err + } + defer googleapi.CloseBody(res) + if err := googleapi.CheckResponse(res); err != nil { + return nil, err + } + ret := &GenerateAccessTokenResponse{ + ServerResponse: googleapi.ServerResponse{ + Header: res.Header, + HTTPStatusCode: res.StatusCode, + }, + } + target := &ret + if err := gensupport.DecodeResponse(target, res); err != nil { + return nil, err + } + return ret, nil + // { + // "description": "Generates an OAuth 2.0 access token for a service account.", + // "flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:generateAccessToken", + // "httpMethod": "POST", + // "id": "iamcredentials.projects.serviceAccounts.generateAccessToken", + // "parameterOrder": [ + // "name" + // ], + // "parameters": { + // "name": { + // "description": "The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + // "location": "path", + // "pattern": "^projects/[^/]+/serviceAccounts/[^/]+$", + // "required": true, + // "type": "string" + // } + // }, + // "path": "v1/{+name}:generateAccessToken", + // "request": { + // "$ref": "GenerateAccessTokenRequest" + // }, + // "response": { + // "$ref": "GenerateAccessTokenResponse" + // }, + // "scopes": [ + // "https://www.googleapis.com/auth/cloud-platform" + // ] + // } + +} + +// method id "iamcredentials.projects.serviceAccounts.generateIdToken": + +type ProjectsServiceAccountsGenerateIdTokenCall struct { + s *Service + name string + generateidtokenrequest *GenerateIdTokenRequest + urlParams_ gensupport.URLParams + ctx_ context.Context + header_ http.Header +} + +// GenerateIdToken: Generates an OpenID Connect ID token for a service +// account. +func (r *ProjectsServiceAccountsService) GenerateIdToken(name string, generateidtokenrequest *GenerateIdTokenRequest) *ProjectsServiceAccountsGenerateIdTokenCall { + c := &ProjectsServiceAccountsGenerateIdTokenCall{s: r.s, urlParams_: make(gensupport.URLParams)} + c.name = name + c.generateidtokenrequest = generateidtokenrequest + return c +} + +// Fields allows partial responses to be retrieved. See +// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse +// for more information. +func (c *ProjectsServiceAccountsGenerateIdTokenCall) Fields(s ...googleapi.Field) *ProjectsServiceAccountsGenerateIdTokenCall { + c.urlParams_.Set("fields", googleapi.CombineFields(s)) + return c +} + +// Context sets the context to be used in this call's Do method. Any +// pending HTTP request will be aborted if the provided context is +// canceled. +func (c *ProjectsServiceAccountsGenerateIdTokenCall) Context(ctx context.Context) *ProjectsServiceAccountsGenerateIdTokenCall { + c.ctx_ = ctx + return c +} + +// Header returns an http.Header that can be modified by the caller to +// add HTTP headers to the request. +func (c *ProjectsServiceAccountsGenerateIdTokenCall) Header() http.Header { + if c.header_ == nil { + c.header_ = make(http.Header) + } + return c.header_ +} + +func (c *ProjectsServiceAccountsGenerateIdTokenCall) doRequest(alt string) (*http.Response, error) { + reqHeaders := make(http.Header) + for k, v := range c.header_ { + reqHeaders[k] = v + } + reqHeaders.Set("User-Agent", c.s.userAgent()) + var body io.Reader = nil + body, err := googleapi.WithoutDataWrapper.JSONReader(c.generateidtokenrequest) + if err != nil { + return nil, err + } + reqHeaders.Set("Content-Type", "application/json") + c.urlParams_.Set("alt", alt) + c.urlParams_.Set("prettyPrint", "false") + urls := googleapi.ResolveRelative(c.s.BasePath, "v1/{+name}:generateIdToken") + urls += "?" + c.urlParams_.Encode() + req, err := http.NewRequest("POST", urls, body) + if err != nil { + return nil, err + } + req.Header = reqHeaders + googleapi.Expand(req.URL, map[string]string{ + "name": c.name, + }) + return gensupport.SendRequest(c.ctx_, c.s.client, req) +} + +// Do executes the "iamcredentials.projects.serviceAccounts.generateIdToken" call. +// Exactly one of *GenerateIdTokenResponse or error will be non-nil. Any +// non-2xx status code is an error. Response headers are in either +// *GenerateIdTokenResponse.ServerResponse.Header or (if a response was +// returned at all) in error.(*googleapi.Error).Header. Use +// googleapi.IsNotModified to check whether the returned error was +// because http.StatusNotModified was returned. +func (c *ProjectsServiceAccountsGenerateIdTokenCall) Do(opts ...googleapi.CallOption) (*GenerateIdTokenResponse, error) { + gensupport.SetOptions(c.urlParams_, opts...) + res, err := c.doRequest("json") + if res != nil && res.StatusCode == http.StatusNotModified { + if res.Body != nil { + res.Body.Close() + } + return nil, &googleapi.Error{ + Code: res.StatusCode, + Header: res.Header, + } + } + if err != nil { + return nil, err + } + defer googleapi.CloseBody(res) + if err := googleapi.CheckResponse(res); err != nil { + return nil, err + } + ret := &GenerateIdTokenResponse{ + ServerResponse: googleapi.ServerResponse{ + Header: res.Header, + HTTPStatusCode: res.StatusCode, + }, + } + target := &ret + if err := gensupport.DecodeResponse(target, res); err != nil { + return nil, err + } + return ret, nil + // { + // "description": "Generates an OpenID Connect ID token for a service account.", + // "flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:generateIdToken", + // "httpMethod": "POST", + // "id": "iamcredentials.projects.serviceAccounts.generateIdToken", + // "parameterOrder": [ + // "name" + // ], + // "parameters": { + // "name": { + // "description": "The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + // "location": "path", + // "pattern": "^projects/[^/]+/serviceAccounts/[^/]+$", + // "required": true, + // "type": "string" + // } + // }, + // "path": "v1/{+name}:generateIdToken", + // "request": { + // "$ref": "GenerateIdTokenRequest" + // }, + // "response": { + // "$ref": "GenerateIdTokenResponse" + // }, + // "scopes": [ + // "https://www.googleapis.com/auth/cloud-platform" + // ] + // } + +} + +// method id "iamcredentials.projects.serviceAccounts.generateIdentityBindingAccessToken": + +type ProjectsServiceAccountsGenerateIdentityBindingAccessTokenCall struct { + s *Service + name string + generateidentitybindingaccesstokenrequest *GenerateIdentityBindingAccessTokenRequest + urlParams_ gensupport.URLParams + ctx_ context.Context + header_ http.Header +} + +// GenerateIdentityBindingAccessToken: +func (r *ProjectsServiceAccountsService) GenerateIdentityBindingAccessToken(name string, generateidentitybindingaccesstokenrequest *GenerateIdentityBindingAccessTokenRequest) *ProjectsServiceAccountsGenerateIdentityBindingAccessTokenCall { + c := &ProjectsServiceAccountsGenerateIdentityBindingAccessTokenCall{s: r.s, urlParams_: make(gensupport.URLParams)} + c.name = name + c.generateidentitybindingaccesstokenrequest = generateidentitybindingaccesstokenrequest + return c +} + +// Fields allows partial responses to be retrieved. See +// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse +// for more information. +func (c *ProjectsServiceAccountsGenerateIdentityBindingAccessTokenCall) Fields(s ...googleapi.Field) *ProjectsServiceAccountsGenerateIdentityBindingAccessTokenCall { + c.urlParams_.Set("fields", googleapi.CombineFields(s)) + return c +} + +// Context sets the context to be used in this call's Do method. Any +// pending HTTP request will be aborted if the provided context is +// canceled. +func (c *ProjectsServiceAccountsGenerateIdentityBindingAccessTokenCall) Context(ctx context.Context) *ProjectsServiceAccountsGenerateIdentityBindingAccessTokenCall { + c.ctx_ = ctx + return c +} + +// Header returns an http.Header that can be modified by the caller to +// add HTTP headers to the request. +func (c *ProjectsServiceAccountsGenerateIdentityBindingAccessTokenCall) Header() http.Header { + if c.header_ == nil { + c.header_ = make(http.Header) + } + return c.header_ +} + +func (c *ProjectsServiceAccountsGenerateIdentityBindingAccessTokenCall) doRequest(alt string) (*http.Response, error) { + reqHeaders := make(http.Header) + for k, v := range c.header_ { + reqHeaders[k] = v + } + reqHeaders.Set("User-Agent", c.s.userAgent()) + var body io.Reader = nil + body, err := googleapi.WithoutDataWrapper.JSONReader(c.generateidentitybindingaccesstokenrequest) + if err != nil { + return nil, err + } + reqHeaders.Set("Content-Type", "application/json") + c.urlParams_.Set("alt", alt) + c.urlParams_.Set("prettyPrint", "false") + urls := googleapi.ResolveRelative(c.s.BasePath, "v1/{+name}:generateIdentityBindingAccessToken") + urls += "?" + c.urlParams_.Encode() + req, err := http.NewRequest("POST", urls, body) + if err != nil { + return nil, err + } + req.Header = reqHeaders + googleapi.Expand(req.URL, map[string]string{ + "name": c.name, + }) + return gensupport.SendRequest(c.ctx_, c.s.client, req) +} + +// Do executes the "iamcredentials.projects.serviceAccounts.generateIdentityBindingAccessToken" call. +// Exactly one of *GenerateIdentityBindingAccessTokenResponse or error +// will be non-nil. Any non-2xx status code is an error. Response +// headers are in either +// *GenerateIdentityBindingAccessTokenResponse.ServerResponse.Header or +// (if a response was returned at all) in +// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check +// whether the returned error was because http.StatusNotModified was +// returned. +func (c *ProjectsServiceAccountsGenerateIdentityBindingAccessTokenCall) Do(opts ...googleapi.CallOption) (*GenerateIdentityBindingAccessTokenResponse, error) { + gensupport.SetOptions(c.urlParams_, opts...) + res, err := c.doRequest("json") + if res != nil && res.StatusCode == http.StatusNotModified { + if res.Body != nil { + res.Body.Close() + } + return nil, &googleapi.Error{ + Code: res.StatusCode, + Header: res.Header, + } + } + if err != nil { + return nil, err + } + defer googleapi.CloseBody(res) + if err := googleapi.CheckResponse(res); err != nil { + return nil, err + } + ret := &GenerateIdentityBindingAccessTokenResponse{ + ServerResponse: googleapi.ServerResponse{ + Header: res.Header, + HTTPStatusCode: res.StatusCode, + }, + } + target := &ret + if err := gensupport.DecodeResponse(target, res); err != nil { + return nil, err + } + return ret, nil + // { + // "description": "", + // "flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:generateIdentityBindingAccessToken", + // "httpMethod": "POST", + // "id": "iamcredentials.projects.serviceAccounts.generateIdentityBindingAccessToken", + // "parameterOrder": [ + // "name" + // ], + // "parameters": { + // "name": { + // "description": "The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + // "location": "path", + // "pattern": "^projects/[^/]+/serviceAccounts/[^/]+$", + // "required": true, + // "type": "string" + // } + // }, + // "path": "v1/{+name}:generateIdentityBindingAccessToken", + // "request": { + // "$ref": "GenerateIdentityBindingAccessTokenRequest" + // }, + // "response": { + // "$ref": "GenerateIdentityBindingAccessTokenResponse" + // } + // } + +} + +// method id "iamcredentials.projects.serviceAccounts.signBlob": + +type ProjectsServiceAccountsSignBlobCall struct { + s *Service + name string + signblobrequest *SignBlobRequest + urlParams_ gensupport.URLParams + ctx_ context.Context + header_ http.Header +} + +// SignBlob: Signs a blob using a service account's system-managed +// private key. +func (r *ProjectsServiceAccountsService) SignBlob(name string, signblobrequest *SignBlobRequest) *ProjectsServiceAccountsSignBlobCall { + c := &ProjectsServiceAccountsSignBlobCall{s: r.s, urlParams_: make(gensupport.URLParams)} + c.name = name + c.signblobrequest = signblobrequest + return c +} + +// Fields allows partial responses to be retrieved. See +// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse +// for more information. +func (c *ProjectsServiceAccountsSignBlobCall) Fields(s ...googleapi.Field) *ProjectsServiceAccountsSignBlobCall { + c.urlParams_.Set("fields", googleapi.CombineFields(s)) + return c +} + +// Context sets the context to be used in this call's Do method. Any +// pending HTTP request will be aborted if the provided context is +// canceled. +func (c *ProjectsServiceAccountsSignBlobCall) Context(ctx context.Context) *ProjectsServiceAccountsSignBlobCall { + c.ctx_ = ctx + return c +} + +// Header returns an http.Header that can be modified by the caller to +// add HTTP headers to the request. +func (c *ProjectsServiceAccountsSignBlobCall) Header() http.Header { + if c.header_ == nil { + c.header_ = make(http.Header) + } + return c.header_ +} + +func (c *ProjectsServiceAccountsSignBlobCall) doRequest(alt string) (*http.Response, error) { + reqHeaders := make(http.Header) + for k, v := range c.header_ { + reqHeaders[k] = v + } + reqHeaders.Set("User-Agent", c.s.userAgent()) + var body io.Reader = nil + body, err := googleapi.WithoutDataWrapper.JSONReader(c.signblobrequest) + if err != nil { + return nil, err + } + reqHeaders.Set("Content-Type", "application/json") + c.urlParams_.Set("alt", alt) + c.urlParams_.Set("prettyPrint", "false") + urls := googleapi.ResolveRelative(c.s.BasePath, "v1/{+name}:signBlob") + urls += "?" + c.urlParams_.Encode() + req, err := http.NewRequest("POST", urls, body) + if err != nil { + return nil, err + } + req.Header = reqHeaders + googleapi.Expand(req.URL, map[string]string{ + "name": c.name, + }) + return gensupport.SendRequest(c.ctx_, c.s.client, req) +} + +// Do executes the "iamcredentials.projects.serviceAccounts.signBlob" call. +// Exactly one of *SignBlobResponse or error will be non-nil. Any +// non-2xx status code is an error. Response headers are in either +// *SignBlobResponse.ServerResponse.Header or (if a response was +// returned at all) in error.(*googleapi.Error).Header. Use +// googleapi.IsNotModified to check whether the returned error was +// because http.StatusNotModified was returned. +func (c *ProjectsServiceAccountsSignBlobCall) Do(opts ...googleapi.CallOption) (*SignBlobResponse, error) { + gensupport.SetOptions(c.urlParams_, opts...) + res, err := c.doRequest("json") + if res != nil && res.StatusCode == http.StatusNotModified { + if res.Body != nil { + res.Body.Close() + } + return nil, &googleapi.Error{ + Code: res.StatusCode, + Header: res.Header, + } + } + if err != nil { + return nil, err + } + defer googleapi.CloseBody(res) + if err := googleapi.CheckResponse(res); err != nil { + return nil, err + } + ret := &SignBlobResponse{ + ServerResponse: googleapi.ServerResponse{ + Header: res.Header, + HTTPStatusCode: res.StatusCode, + }, + } + target := &ret + if err := gensupport.DecodeResponse(target, res); err != nil { + return nil, err + } + return ret, nil + // { + // "description": "Signs a blob using a service account's system-managed private key.", + // "flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:signBlob", + // "httpMethod": "POST", + // "id": "iamcredentials.projects.serviceAccounts.signBlob", + // "parameterOrder": [ + // "name" + // ], + // "parameters": { + // "name": { + // "description": "The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + // "location": "path", + // "pattern": "^projects/[^/]+/serviceAccounts/[^/]+$", + // "required": true, + // "type": "string" + // } + // }, + // "path": "v1/{+name}:signBlob", + // "request": { + // "$ref": "SignBlobRequest" + // }, + // "response": { + // "$ref": "SignBlobResponse" + // }, + // "scopes": [ + // "https://www.googleapis.com/auth/cloud-platform" + // ] + // } + +} + +// method id "iamcredentials.projects.serviceAccounts.signJwt": + +type ProjectsServiceAccountsSignJwtCall struct { + s *Service + name string + signjwtrequest *SignJwtRequest + urlParams_ gensupport.URLParams + ctx_ context.Context + header_ http.Header +} + +// SignJwt: Signs a JWT using a service account's system-managed private +// key. +func (r *ProjectsServiceAccountsService) SignJwt(name string, signjwtrequest *SignJwtRequest) *ProjectsServiceAccountsSignJwtCall { + c := &ProjectsServiceAccountsSignJwtCall{s: r.s, urlParams_: make(gensupport.URLParams)} + c.name = name + c.signjwtrequest = signjwtrequest + return c +} + +// Fields allows partial responses to be retrieved. See +// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse +// for more information. +func (c *ProjectsServiceAccountsSignJwtCall) Fields(s ...googleapi.Field) *ProjectsServiceAccountsSignJwtCall { + c.urlParams_.Set("fields", googleapi.CombineFields(s)) + return c +} + +// Context sets the context to be used in this call's Do method. Any +// pending HTTP request will be aborted if the provided context is +// canceled. +func (c *ProjectsServiceAccountsSignJwtCall) Context(ctx context.Context) *ProjectsServiceAccountsSignJwtCall { + c.ctx_ = ctx + return c +} + +// Header returns an http.Header that can be modified by the caller to +// add HTTP headers to the request. +func (c *ProjectsServiceAccountsSignJwtCall) Header() http.Header { + if c.header_ == nil { + c.header_ = make(http.Header) + } + return c.header_ +} + +func (c *ProjectsServiceAccountsSignJwtCall) doRequest(alt string) (*http.Response, error) { + reqHeaders := make(http.Header) + for k, v := range c.header_ { + reqHeaders[k] = v + } + reqHeaders.Set("User-Agent", c.s.userAgent()) + var body io.Reader = nil + body, err := googleapi.WithoutDataWrapper.JSONReader(c.signjwtrequest) + if err != nil { + return nil, err + } + reqHeaders.Set("Content-Type", "application/json") + c.urlParams_.Set("alt", alt) + c.urlParams_.Set("prettyPrint", "false") + urls := googleapi.ResolveRelative(c.s.BasePath, "v1/{+name}:signJwt") + urls += "?" + c.urlParams_.Encode() + req, err := http.NewRequest("POST", urls, body) + if err != nil { + return nil, err + } + req.Header = reqHeaders + googleapi.Expand(req.URL, map[string]string{ + "name": c.name, + }) + return gensupport.SendRequest(c.ctx_, c.s.client, req) +} + +// Do executes the "iamcredentials.projects.serviceAccounts.signJwt" call. +// Exactly one of *SignJwtResponse or error will be non-nil. Any non-2xx +// status code is an error. Response headers are in either +// *SignJwtResponse.ServerResponse.Header or (if a response was returned +// at all) in error.(*googleapi.Error).Header. Use +// googleapi.IsNotModified to check whether the returned error was +// because http.StatusNotModified was returned. +func (c *ProjectsServiceAccountsSignJwtCall) Do(opts ...googleapi.CallOption) (*SignJwtResponse, error) { + gensupport.SetOptions(c.urlParams_, opts...) + res, err := c.doRequest("json") + if res != nil && res.StatusCode == http.StatusNotModified { + if res.Body != nil { + res.Body.Close() + } + return nil, &googleapi.Error{ + Code: res.StatusCode, + Header: res.Header, + } + } + if err != nil { + return nil, err + } + defer googleapi.CloseBody(res) + if err := googleapi.CheckResponse(res); err != nil { + return nil, err + } + ret := &SignJwtResponse{ + ServerResponse: googleapi.ServerResponse{ + Header: res.Header, + HTTPStatusCode: res.StatusCode, + }, + } + target := &ret + if err := gensupport.DecodeResponse(target, res); err != nil { + return nil, err + } + return ret, nil + // { + // "description": "Signs a JWT using a service account's system-managed private key.", + // "flatPath": "v1/projects/{projectsId}/serviceAccounts/{serviceAccountsId}:signJwt", + // "httpMethod": "POST", + // "id": "iamcredentials.projects.serviceAccounts.signJwt", + // "parameterOrder": [ + // "name" + // ], + // "parameters": { + // "name": { + // "description": "The resource name of the service account for which the credentials\nare requested, in the following format:\n`projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`. The `-` wildcard\ncharacter is required; replacing it with a project ID is invalid.", + // "location": "path", + // "pattern": "^projects/[^/]+/serviceAccounts/[^/]+$", + // "required": true, + // "type": "string" + // } + // }, + // "path": "v1/{+name}:signJwt", + // "request": { + // "$ref": "SignJwtRequest" + // }, + // "response": { + // "$ref": "SignJwtResponse" + // }, + // "scopes": [ + // "https://www.googleapis.com/auth/cloud-platform" + // ] + // } + +} diff --git a/website/docs/d/google_impersonated_credential.html.markdown b/website/docs/d/google_impersonated_credential.html.markdown index 677364d3910..3d551dacaa3 100644 --- a/website/docs/d/google_impersonated_credential.html.markdown +++ b/website/docs/d/google_impersonated_credential.html.markdown @@ -26,7 +26,7 @@ $ cat service_policy.json "bindings": [ { "members": [ - "service_A@projectA.iam.gserviceaccount.com " + "serviceAccount:service_A@projectA.iam.gserviceaccount.com" ], "role": "roles/iam.serviceAccountTokenCreator", } @@ -50,7 +50,7 @@ data "google_client_config" "default" { data "google_impersonated_credential" "default" { provider = "google" target_service_account = "service_B@projectB.iam.gserviceaccount.com" - scopes = ["devstorage.read_only", "cloud-platform"] + scopes = ["userinfo-email", "cloud-platform"] lifetime = "300s" } @@ -63,6 +63,18 @@ data "google_project" "project" { provider = "google.impersonated" project_id = "target-project" } + +data "google_client_openid_userinfo" "me" { + provider = "google.impersonated" +} + +output "project_number" { + value = "${data.google_project.project.number}" +} + +output "target-email" { + value = "${data.google_client_openid_userinfo.me.email}" +} ``` > *Note*: the generated token is non-refreshable and can have a maximum `lifetime` of `3600` seconds. @@ -72,7 +84,7 @@ data "google_project" "project" { The following arguments are supported: * `target_service_account` (Required) - The service account _to_ impersonate (e.g. `service_B@your-project-id.iam.gserviceaccount.com`) -* `scopes` (Required) - The scopes the new credential should have (e.g. `["devstorage.read_only", "cloud-platform"]`) +* `scopes` (Required) - The scopes the new credential should have (e.g. `["storage-ro", "cloud-platform"]`) * `delegates` (Optional) - Deegate chain of approvals needed to perform full impersonation. Specify the fully qualified service account name. (e.g. `["projects/-/serviceAccounts/delegate-svc-account@project-id.iam.gserviceaccount.com"]`) * `lifetime` (Optional) Lifetime of the impersonated token (defaults to its max: `3600s`). * `source_access_token` (Optional) - The source token to bootstrap this module. From 407e2bbfd5427f54da0bfc5155fd4222cb1b7d2f Mon Sep 17 00:00:00 2001 From: salmaan rashid Date: Tue, 19 Mar 2019 11:24:01 -0700 Subject: [PATCH 5/9] update iamclientservice init --- google/data_source_google_impersonated_credential.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/google/data_source_google_impersonated_credential.go b/google/data_source_google_impersonated_credential.go index 45cca211de6..a6768cea484 100644 --- a/google/data_source_google_impersonated_credential.go +++ b/google/data_source_google_impersonated_credential.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log" - "net/http" "strings" "time" @@ -12,6 +11,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "golang.org/x/oauth2" iamcredentials "google.golang.org/api/iamcredentials/v1" + "google.golang.org/api/option" ) func dataSourceGoogleImpersonatedCredential() *schema.Resource { @@ -67,20 +67,20 @@ func dataSourceImpersonatedCredentialRead(d *schema.ResourceData, meta interface log.Printf("[INFO] Acquire Impersonated credentials for %s", d.Get("target_service_account").(string)) d.SetId(time.Now().UTC().String()) - var client *http.Client + var tokenSource oauth2.TokenSource if d.Get("source_access_token") != "" { - rootTokenSource := oauth2.StaticTokenSource(&oauth2.Token{ + tokenSource = oauth2.StaticTokenSource(&oauth2.Token{ AccessToken: d.Get("source_access_token").(string), }) - client = oauth2.NewClient(context.TODO(), rootTokenSource) } else { - client = config.client + tokenSource = config.tokenSource } - service, err := iamcredentials.New(client) + service, err := iamcredentials.NewService(context.Background(), option.WithTokenSource(tokenSource)) if err != nil { return err } + name := fmt.Sprintf("projects/-/serviceAccounts/%s", d.Get("target_service_account").(string)) tokenRequest := &iamcredentials.GenerateAccessTokenRequest{ Lifetime: d.Get("lifetime").(string), From 4c6f07e036cf0841180e77d16789e4d8b468c5ee Mon Sep 17 00:00:00 2001 From: salmaan rashid Date: Tue, 19 Mar 2019 13:19:23 -0700 Subject: [PATCH 6/9] address comments; revert iamcredential vendord version --- google/config.go | 9 +++ ...a_source_google_impersonated_credential.go | 20 +++---- .../iamcredentials/v1/iamcredentials-gen.go | 56 +------------------ ...ogle_impersonated_credential.html.markdown | 34 +++-------- 4 files changed, 31 insertions(+), 88 deletions(-) diff --git a/google/config.go b/google/config.go index 78a62681dc2..e927b570b11 100644 --- a/google/config.go +++ b/google/config.go @@ -34,6 +34,7 @@ import ( dnsBeta "google.golang.org/api/dns/v1beta2" file "google.golang.org/api/file/v1beta1" "google.golang.org/api/iam/v1" + iamcredentials "google.golang.org/api/iamcredentials/v1" cloudlogging "google.golang.org/api/logging/v2" "google.golang.org/api/pubsub/v1" runtimeconfig "google.golang.org/api/runtimeconfig/v1beta1" @@ -73,6 +74,7 @@ type Config struct { clientDns *dns.Service clientDnsBeta *dnsBeta.Service clientFilestore *file.Service + clientIamCredentials *iamcredentials.Service clientKms *cloudkms.Service clientLogging *cloudlogging.Service clientPubsub *pubsub.Service @@ -240,6 +242,13 @@ func (c *Config) LoadAndValidate() error { } c.clientIAM.UserAgent = userAgent + log.Printf("[INFO] Instantiating Google Cloud IAMCredentials Client...") + c.clientIamCredentials, err = iamcredentials.New(client) + if err != nil { + return err + } + c.clientIamCredentials.UserAgent = userAgent + log.Printf("[INFO] Instantiating Google Cloud Service Management Client...") c.clientServiceMan, err = servicemanagement.New(client) if err != nil { diff --git a/google/data_source_google_impersonated_credential.go b/google/data_source_google_impersonated_credential.go index a6768cea484..88833bb8811 100644 --- a/google/data_source_google_impersonated_credential.go +++ b/google/data_source_google_impersonated_credential.go @@ -11,7 +11,6 @@ import ( "github.com/hashicorp/terraform/helper/schema" "golang.org/x/oauth2" iamcredentials "google.golang.org/api/iamcredentials/v1" - "google.golang.org/api/option" ) func dataSourceGoogleImpersonatedCredential() *schema.Resource { @@ -66,19 +65,19 @@ func dataSourceImpersonatedCredentialRead(d *schema.ResourceData, meta interface config := meta.(*Config) log.Printf("[INFO] Acquire Impersonated credentials for %s", d.Get("target_service_account").(string)) - d.SetId(time.Now().UTC().String()) - var tokenSource oauth2.TokenSource + var service *iamcredentials.Service + var err error if d.Get("source_access_token") != "" { - tokenSource = oauth2.StaticTokenSource(&oauth2.Token{ + tokenSource := oauth2.StaticTokenSource(&oauth2.Token{ AccessToken: d.Get("source_access_token").(string), }) + client := oauth2.NewClient(context.Background(), tokenSource) + service, err = iamcredentials.New(client) + if err != nil { + return err + } } else { - tokenSource = config.tokenSource - } - - service, err := iamcredentials.NewService(context.Background(), option.WithTokenSource(tokenSource)) - if err != nil { - return err + service = config.clientIamCredentials } name := fmt.Sprintf("projects/-/serviceAccounts/%s", d.Get("target_service_account").(string)) @@ -92,6 +91,7 @@ func dataSourceImpersonatedCredentialRead(d *schema.ResourceData, meta interface return err } + d.SetId(time.Now().UTC().String()) d.Set("access_token", at.AccessToken) return nil diff --git a/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-gen.go b/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-gen.go index 3c3e0c86036..098e583eb0f 100644 --- a/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-gen.go +++ b/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-gen.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC. +// Copyright 2019 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -6,35 +6,13 @@ // Package iamcredentials provides access to the IAM Service Account Credentials API. // -// For product documentation, see: https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials -// -// Creating a client +// See https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials // // Usage example: // // import "google.golang.org/api/iamcredentials/v1" // ... -// ctx := context.Background() -// iamcredentialsService, err := iamcredentials.NewService(ctx) -// -// In this example, Google Application Default Credentials are used for authentication. -// -// For information on how to create and obtain Application Default Credentials, see https://developers.google.com/identity/protocols/application-default-credentials. -// -// Other authentication options -// -// To use an API key for authentication (note: some APIs do not support API keys), use option.WithAPIKey: -// -// iamcredentialsService, err := iamcredentials.NewService(ctx, option.WithAPIKey("AIza...")) -// -// To use an OAuth token (e.g., a user token obtained via a three-legged OAuth flow), use option.WithTokenSource: -// -// config := &oauth2.Config{...} -// // ... -// token, err := config.Exchange(ctx, ...) -// iamcredentialsService, err := iamcredentials.NewService(ctx, option.WithTokenSource(config.TokenSource(ctx, token))) -// -// See https://godoc.org/google.golang.org/api/option/ for details on options. +// iamcredentialsService, err := iamcredentials.New(oauthHttpClient) package iamcredentials // import "google.golang.org/api/iamcredentials/v1" import ( @@ -51,8 +29,6 @@ import ( gensupport "google.golang.org/api/gensupport" googleapi "google.golang.org/api/googleapi" - option "google.golang.org/api/option" - htransport "google.golang.org/api/transport/http" ) // Always reference these packages, just in case the auto-generated code @@ -80,32 +56,6 @@ const ( CloudPlatformScope = "https://www.googleapis.com/auth/cloud-platform" ) -// NewService creates a new Service. -func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) { - scopesOption := option.WithScopes( - "https://www.googleapis.com/auth/cloud-platform", - ) - // NOTE: prepend, so we don't override user-specified scopes. - opts = append([]option.ClientOption{scopesOption}, opts...) - client, endpoint, err := htransport.NewClient(ctx, opts...) - if err != nil { - return nil, err - } - s, err := New(client) - if err != nil { - return nil, err - } - if endpoint != "" { - s.BasePath = endpoint - } - return s, nil -} - -// New creates a new Service. It uses the provided http.Client for requests. -// -// Deprecated: please use NewService instead. -// To provide a custom HTTP client, use option.WithHTTPClient. -// If you are using google.golang.org/api/googleapis/transport.APIKey, use option.WithAPIKey with NewService instead. func New(client *http.Client) (*Service, error) { if client == nil { return nil, errors.New("client is nil") diff --git a/website/docs/d/google_impersonated_credential.html.markdown b/website/docs/d/google_impersonated_credential.html.markdown index 3d551dacaa3..c6ef228449f 100644 --- a/website/docs/d/google_impersonated_credential.html.markdown +++ b/website/docs/d/google_impersonated_credential.html.markdown @@ -8,8 +8,7 @@ description: |- # google\_impersonated\_credential -This data source provides a google `oauth2` `access_token` for a different service account than the one initially running the script. You can -then use this new token to access resources the original caller would not have permissions on otherwise. +This data source provides a google `oauth2` `access_token` for a different service account than the one initially running the script. For more information see [the official documentation](https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials) as well as [iamcredentials.generateAccessToken()](https://cloud.google.com/iam/credentials/reference/rest/v1/projects.serviceAccounts/generateAccessToken) @@ -20,23 +19,17 @@ To allow `service_A` to impersonate `service_B`, grant the [Service Account Toke In the IAM policy below, `service_A` is given the Token Creator role impersonate `service_B` -```hcl -$ cat service_policy.json -{ -"bindings": [ - { - "members": [ - "serviceAccount:service_A@projectA.iam.gserviceaccount.com" - ], - "role": "roles/iam.serviceAccountTokenCreator", - } - ] +```sh +resource "google_service_account_iam_binding" "token-creator-iam" { + service_account_id = "projects/projectB/serviceAccounts/service_B@projectB.iam.gserviceaccount.com" + role = "roles/iam.serviceAccountTokenCreator" + members = [ + "serviceAccount:service_A@projectA.iam.gserviceaccount.com", + ] } - -$ gcloud iam service-accounts set-iam-policy service_B@projectB.iam.gserviceaccount.com service_policy.json ``` -Once the IAM permissions are set, you can apply the new token to a provider bootstrapped with it. Any resources that references the new provider will run as the new identity. +Once the IAM permissions are set, you can apply the new token to a provider bootstrapped with it. Any resources that references the aliased provider will run as the new identity. In the example below, `google_project` will run as `service_B`. @@ -59,19 +52,10 @@ provider "google" { access_token = "${data.google_impersonated_credential.default.access_token}" } -data "google_project" "project" { - provider = "google.impersonated" - project_id = "target-project" -} - data "google_client_openid_userinfo" "me" { provider = "google.impersonated" } -output "project_number" { - value = "${data.google_project.project.number}" -} - output "target-email" { value = "${data.google_client_openid_userinfo.me.email}" } From 15e5c31ec2dc932ac6e1c622bc49db8b37891fc2 Mon Sep 17 00:00:00 2001 From: salmaan rashid Date: Fri, 22 Mar 2019 16:40:29 -0700 Subject: [PATCH 7/9] use google_service_account_access_token --- ...rce_google_impersonated_credential_test.go | 87 ------------------- ...ce_google_service_account_access_token.go} | 27 +----- ...oogle_service_account_access_token_test.go | 66 ++++++++++++++ google/provider.go | 2 +- google/validation.go | 9 +- ...ervice_account_access_token.html.markdown} | 13 ++- 6 files changed, 82 insertions(+), 122 deletions(-) delete mode 100644 google/data_source_google_impersonated_credential_test.go rename google/{data_source_google_impersonated_credential.go => data_source_google_service_account_access_token.go} (71%) create mode 100644 google/data_source_google_service_account_access_token_test.go rename website/docs/d/{google_impersonated_credential.html.markdown => datasource_google_service_account_access_token.html.markdown} (85%) diff --git a/google/data_source_google_impersonated_credential_test.go b/google/data_source_google_impersonated_credential_test.go deleted file mode 100644 index 28c8ca9d226..00000000000 --- a/google/data_source_google_impersonated_credential_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package google - -import ( - "testing" - - "fmt" - - "github.com/hashicorp/terraform/helper/acctest" - "github.com/hashicorp/terraform/helper/resource" -) - -func TestAccDataSourceGoogleImpersonatedCredential_basic(t *testing.T) { - t.Parallel() - - resourceName := "data.google_impersonated_credential.default" - - sourceServiceAccountEmail := getTestServiceAccountFromEnv(t) - targetServiceAccountID := acctest.RandomWithPrefix("tf-test") - targetServiceAccountEmail := fmt.Sprintf( - "%s@%s.iam.gserviceaccount.com", - targetServiceAccountID, - getTestProjectFromEnv(), - ) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: testAccCheckGoogleImpersonatedCredential_datasource(sourceServiceAccountEmail, targetServiceAccountID), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "target_service_account", targetServiceAccountEmail), - resource.TestCheckOutput("target-email", targetServiceAccountEmail), - ), - }, - }, - }) -} - -func testAccCheckGoogleImpersonatedCredential_datasource(sourceServiceAccountEmail string, targetServiceAccountID string) string { - - return fmt.Sprintf(` - - provider "google" { - scopes = [ - "https://www.googleapis.com/auth/cloud-platform", - ] - } - - resource "google_service_account" "targetSA" { - account_id = "%s" - } - - resource "google_service_account_iam_binding" "token-creator-iam" { - service_account_id = "${google_service_account.targetSA.name}" - role = "roles/iam.serviceAccountTokenCreator" - members = [ - "serviceAccount:%s", - ] - } - - data "google_impersonated_credential" "default" { - target_service_account = "${google_service_account.targetSA.email}" - scopes = ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/cloud-platform"] - lifetime = "60s" - - depends_on = ["google_service_account_iam_binding.token-creator-iam"] - } - - provider "google" { - alias = "impersonated" - scopes = [ - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/userinfo.email", - ] - access_token = "${data.google_impersonated_credential.default.access_token}" - } - - data "google_client_openid_userinfo" "me" { - provider = "google.impersonated" - } - - output "target-email" { - value = "${data.google_client_openid_userinfo.me.email}" - } - `, targetServiceAccountID, sourceServiceAccountEmail) -} diff --git a/google/data_source_google_impersonated_credential.go b/google/data_source_google_service_account_access_token.go similarity index 71% rename from google/data_source_google_impersonated_credential.go rename to google/data_source_google_service_account_access_token.go index 88833bb8811..53ea7a698ac 100644 --- a/google/data_source_google_impersonated_credential.go +++ b/google/data_source_google_service_account_access_token.go @@ -1,7 +1,6 @@ package google import ( - "context" "fmt" "log" @@ -9,19 +8,14 @@ import ( "time" "github.com/hashicorp/terraform/helper/schema" - "golang.org/x/oauth2" iamcredentials "google.golang.org/api/iamcredentials/v1" ) func dataSourceGoogleImpersonatedCredential() *schema.Resource { return &schema.Resource{ - Read: dataSourceImpersonatedCredentialRead, + Read: dataSourceGoogleServiceAccountAccessTokenRead, Schema: map[string]*schema.Schema{ - "source_access_token": { - Type: schema.TypeString, - Optional: true, - }, "target_service_account": { Type: schema.TypeString, Required: true, @@ -61,24 +55,11 @@ func dataSourceGoogleImpersonatedCredential() *schema.Resource { } } -func dataSourceImpersonatedCredentialRead(d *schema.ResourceData, meta interface{}) error { +func dataSourceGoogleServiceAccountAccessTokenRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - log.Printf("[INFO] Acquire Impersonated credentials for %s", d.Get("target_service_account").(string)) + log.Printf("[INFO] Acquire Service Account AccessToken for %s", d.Get("target_service_account").(string)) - var service *iamcredentials.Service - var err error - if d.Get("source_access_token") != "" { - tokenSource := oauth2.StaticTokenSource(&oauth2.Token{ - AccessToken: d.Get("source_access_token").(string), - }) - client := oauth2.NewClient(context.Background(), tokenSource) - service, err = iamcredentials.New(client) - if err != nil { - return err - } - } else { - service = config.clientIamCredentials - } + service := config.clientIamCredentials name := fmt.Sprintf("projects/-/serviceAccounts/%s", d.Get("target_service_account").(string)) tokenRequest := &iamcredentials.GenerateAccessTokenRequest{ diff --git a/google/data_source_google_service_account_access_token_test.go b/google/data_source_google_service_account_access_token_test.go new file mode 100644 index 00000000000..27e2ba45995 --- /dev/null +++ b/google/data_source_google_service_account_access_token_test.go @@ -0,0 +1,66 @@ +package google + +import ( + "testing" + + "fmt" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func testAccCheckServiceAccountAccessTokenValue(name, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ms := s.RootModule() + rs, ok := ms.Outputs[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + // TODO: validate the token belongs to the service account + if rs.Value == "" { + return fmt.Errorf("%s Cannot be empty", name) + } + + return nil + } +} + +func TestAccDataSourceGoogleServiceAccountAccessToken_basic(t *testing.T) { + t.Parallel() + + resourceName := "data.google_service_account_access_token.default" + + targetServiceAccountEmail := getTestServiceAccountFromEnv(t) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleServiceAccountAccessToken_datasource(targetServiceAccountEmail), + Destroy: true, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "target_service_account", targetServiceAccountEmail), + testAccCheckServiceAccountAccessTokenValue("access_token", targetServiceAccountEmail), + ), + }, + }, + }) +} + +func testAccCheckGoogleServiceAccountAccessToken_datasource(targetServiceAccountID string) string { + + return fmt.Sprintf(` + + data "google_service_account_access_token" "default" { + target_service_account = "%s" + scopes = ["userinfo-email", "https://www.googleapis.com/auth/cloud-platform"] + lifetime = "30s" + } + + output "access_token" { + value = "${data.google_service_account_access_token.default.access_token}" + } + `, targetServiceAccountID) +} diff --git a/google/provider.go b/google/provider.go index b7af13fb0e8..9a2539eb180 100644 --- a/google/provider.go +++ b/google/provider.go @@ -105,7 +105,7 @@ func Provider() terraform.ResourceProvider { "google_container_registry_image": dataSourceGoogleContainerImage(), "google_iam_policy": dataSourceGoogleIamPolicy(), "google_iam_role": dataSourceGoogleIamRole(), - "google_impersonated_credential": dataSourceGoogleImpersonatedCredential(), + "google_service_account_access_token": dataSourceGoogleImpersonatedCredential(), "google_kms_secret": dataSourceGoogleKmsSecret(), "google_kms_key_ring": dataSourceGoogleKmsKeyRing(), "google_kms_crypto_key": dataSourceGoogleKmsCryptoKey(), diff --git a/google/validation.go b/google/validation.go index ef717cfcbd0..5c800476e88 100644 --- a/google/validation.go +++ b/google/validation.go @@ -14,9 +14,10 @@ import ( const ( // Copied from the official Google Cloud auto-generated client. - ProjectRegex = "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?)|-)" - RegionRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?" - SubnetworkRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?" + ProjectRegex = "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?))" + ProjectRegexWildCard = "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?)|-)" + RegionRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?" + SubnetworkRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?" SubnetworkLinkRegex = "projects/(" + ProjectRegex + ")/regions/(" + RegionRegex + ")/subnetworks/(" + SubnetworkRegex + ")$" @@ -35,7 +36,7 @@ var ( // 4 and 28 since the first and last character are excluded. ServiceAccountNameRegex = fmt.Sprintf(RFC1035NameTemplate, 4, 28) - ServiceAccountLinkRegexPrefix = "projects/" + ProjectRegex + "/serviceAccounts/" + ServiceAccountLinkRegexPrefix = "projects/" + ProjectRegexWildCard + "/serviceAccounts/" PossibleServiceAccountNames = []string{ AppEngineServiceAccountNameRegex, ComputeServiceAccountNameRegex, diff --git a/website/docs/d/google_impersonated_credential.html.markdown b/website/docs/d/datasource_google_service_account_access_token.html.markdown similarity index 85% rename from website/docs/d/google_impersonated_credential.html.markdown rename to website/docs/d/datasource_google_service_account_access_token.html.markdown index c6ef228449f..4d064856133 100644 --- a/website/docs/d/google_impersonated_credential.html.markdown +++ b/website/docs/d/datasource_google_service_account_access_token.html.markdown @@ -1,12 +1,12 @@ --- layout: "google" -page_title: "Google: google_impersonated_credential" -sidebar_current: "docs-google-impersonated-credential" +page_title: "Google: google_service_account_access_token" +sidebar_current: "docs-google-service-account-access-token" description: |- Produces access_token for impersonated service accounts --- -# google\_impersonated\_credential +# google\_service\_account\_access\_token This data source provides a google `oauth2` `access_token` for a different service account than the one initially running the script. @@ -21,7 +21,7 @@ In the IAM policy below, `service_A` is given the Token Creator role impersonate ```sh resource "google_service_account_iam_binding" "token-creator-iam" { - service_account_id = "projects/projectB/serviceAccounts/service_B@projectB.iam.gserviceaccount.com" + service_account_id = "projects/-/serviceAccounts/service_B@projectB.iam.gserviceaccount.com" role = "roles/iam.serviceAccountTokenCreator" members = [ "serviceAccount:service_A@projectA.iam.gserviceaccount.com", @@ -40,7 +40,7 @@ data "google_client_config" "default" { provider = "google" } -data "google_impersonated_credential" "default" { +data "google_service_account_access_token" "default" { provider = "google" target_service_account = "service_B@projectB.iam.gserviceaccount.com" scopes = ["userinfo-email", "cloud-platform"] @@ -49,7 +49,7 @@ data "google_impersonated_credential" "default" { provider "google" { alias = "impersonated" - access_token = "${data.google_impersonated_credential.default.access_token}" + access_token = "${data.google_service_account_access_token.default.access_token}" } data "google_client_openid_userinfo" "me" { @@ -71,7 +71,6 @@ The following arguments are supported: * `scopes` (Required) - The scopes the new credential should have (e.g. `["storage-ro", "cloud-platform"]`) * `delegates` (Optional) - Deegate chain of approvals needed to perform full impersonation. Specify the fully qualified service account name. (e.g. `["projects/-/serviceAccounts/delegate-svc-account@project-id.iam.gserviceaccount.com"]`) * `lifetime` (Optional) Lifetime of the impersonated token (defaults to its max: `3600s`). -* `source_access_token` (Optional) - The source token to bootstrap this module. ## Attributes Reference From 58c83e4536619ad0f56824dfd93b145c453da145 Mon Sep 17 00:00:00 2001 From: salmaan rashid Date: Fri, 22 Mar 2019 16:50:09 -0700 Subject: [PATCH 8/9] update reference name --- google/data_source_google_service_account_access_token.go | 2 +- google/provider.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/google/data_source_google_service_account_access_token.go b/google/data_source_google_service_account_access_token.go index 53ea7a698ac..f59d7d02905 100644 --- a/google/data_source_google_service_account_access_token.go +++ b/google/data_source_google_service_account_access_token.go @@ -11,7 +11,7 @@ import ( iamcredentials "google.golang.org/api/iamcredentials/v1" ) -func dataSourceGoogleImpersonatedCredential() *schema.Resource { +func dataSourceGoogleServiceAccountAccessToken() *schema.Resource { return &schema.Resource{ Read: dataSourceGoogleServiceAccountAccessTokenRead, diff --git a/google/provider.go b/google/provider.go index 9a2539eb180..17fb471aca8 100644 --- a/google/provider.go +++ b/google/provider.go @@ -105,7 +105,7 @@ func Provider() terraform.ResourceProvider { "google_container_registry_image": dataSourceGoogleContainerImage(), "google_iam_policy": dataSourceGoogleIamPolicy(), "google_iam_role": dataSourceGoogleIamRole(), - "google_service_account_access_token": dataSourceGoogleImpersonatedCredential(), + "google_service_account_access_token": dataSourceGoogleServiceAccountAccessToken(), "google_kms_secret": dataSourceGoogleKmsSecret(), "google_kms_key_ring": dataSourceGoogleKmsKeyRing(), "google_kms_crypto_key": dataSourceGoogleKmsCryptoKey(), From 5ff1d1d4f2077d6a7bd8abb015fee139e9fa09fe Mon Sep 17 00:00:00 2001 From: salmaan rashid Date: Fri, 29 Mar 2019 15:15:24 -0700 Subject: [PATCH 9/9] sh->hcl --- ...datasource_google_service_account_access_token.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/d/datasource_google_service_account_access_token.html.markdown b/website/docs/d/datasource_google_service_account_access_token.html.markdown index 4d064856133..8b567346b30 100644 --- a/website/docs/d/datasource_google_service_account_access_token.html.markdown +++ b/website/docs/d/datasource_google_service_account_access_token.html.markdown @@ -19,7 +19,7 @@ To allow `service_A` to impersonate `service_B`, grant the [Service Account Toke In the IAM policy below, `service_A` is given the Token Creator role impersonate `service_B` -```sh +```hcl resource "google_service_account_iam_binding" "token-creator-iam" { service_account_id = "projects/-/serviceAccounts/service_B@projectB.iam.gserviceaccount.com" role = "roles/iam.serviceAccountTokenCreator"