diff --git a/mmv1/products/iamworkforcepool/WorkforcePoolProvider.yaml b/mmv1/products/iamworkforcepool/WorkforcePoolProvider.yaml index 13e19af8fd78..221019f14a47 100644 --- a/mmv1/products/iamworkforcepool/WorkforcePoolProvider.yaml +++ b/mmv1/products/iamworkforcepool/WorkforcePoolProvider.yaml @@ -58,6 +58,8 @@ examples: provider_id: 'example-prvdr' test_env_vars: org_id: :ORG_ID + ignore_read_extra: + - 'oidc.0.client_secret.0.value.0.plain_text' - !ruby/object:Provider::Terraform::Examples name: 'iam_workforce_pool_provider_oidc_full' primary_resource_id: 'example' @@ -66,9 +68,13 @@ examples: provider_id: 'example-prvdr' test_env_vars: org_id: :ORG_ID + ignore_read_extra: + - 'oidc.0.client_secret.0.value.0.plain_text' custom_code: !ruby/object:Provider::Terraform::CustomCode constants: templates/terraform/constants/iam_workforce_pool_provider.go.erb decoder: templates/terraform/decoders/treat_deleted_state_as_gone.go.erb + post_create: templates/terraform/post_create/iam_workforce_pool_provider.go.erb + post_update: templates/terraform/post_update/iam_workforce_pool_provider.go.erb properties: - !ruby/object:Api::Type::String name: 'location' @@ -252,6 +258,32 @@ properties: The client ID. Must match the audience claim of the JWT issued by the identity provider. required: true + - !ruby/object:Api::Type::NestedObject + name: 'clientSecret' + description: | + The optional client secret. Required to enable Authorization Code flow for web sign-in. + properties: + - !ruby/object:Api::Type::NestedObject + name: 'value' + exactly_one_of: + - oidc.0.client_secret.0.value + description: | + The value of the client secret. + custom_flatten: templates/terraform/custom_flatten/iam_workforce_pool_provider_oidc_client_secret_value.go.erb + properties: + - !ruby/object:Api::Type::String + name: 'plainText' + description: | + The plain text of the client secret value. + sensitive: true + required: true + validation: !ruby/object:Provider::Terraform::Validation + function: 'validation.StringIsNotEmpty' + - !ruby/object:Api::Type::String + name: 'thumbprint' + description: | + A thumbprint to represent the current client secret value. + output: true - !ruby/object:Api::Type::NestedObject name: 'webSsoConfig' description: | @@ -262,15 +294,21 @@ properties: name: 'responseType' description: | The Response Type to request for in the OIDC Authorization Request for web sign-in. + + The `CODE` Response Type is recommended to avoid the Implicit Flow, for security reasons. + * CODE: The `response_type=code` selection uses the Authorization Code Flow for web sign-in. Requires a configured client secret. * ID_TOKEN: The `response_type=id_token` selection uses the Implicit Flow for web sign-in. required: true values: + - :CODE - :ID_TOKEN - !ruby/object:Api::Type::Enum name: assertionClaimsBehavior description: | The behavior for how OIDC Claims are included in the `assertion` object used for attribute mapping and attribute condition. + * MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS: Merge the UserInfo Endpoint Claims with ID Token Claims, preferring UserInfo Claim Values for the same Claim Name. This option is available only for the Authorization Code Flow. * ONLY_ID_TOKEN_CLAIMS: Only include ID Token Claims. required: true values: + - :MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS - :ONLY_ID_TOKEN_CLAIMS diff --git a/mmv1/templates/terraform/custom_flatten/iam_workforce_pool_provider_oidc_client_secret_value.go.erb b/mmv1/templates/terraform/custom_flatten/iam_workforce_pool_provider_oidc_client_secret_value.go.erb new file mode 100644 index 000000000000..23cb2279fa8d --- /dev/null +++ b/mmv1/templates/terraform/custom_flatten/iam_workforce_pool_provider_oidc_client_secret_value.go.erb @@ -0,0 +1,31 @@ +<%# The license inside this block applies to this file. + # Copyright 2023 Google Inc. + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. +-%> +func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["thumbprint"] = original["thumbprint"] + // Trigger a diff based on the plain_text if there is no change in the thumbprint, + // otherwise leave plain_text empty to always trigger a diff. + if original["thumbprint"].(string) == d.Get("oidc.0.client_secret.0.value.0.thumbprint").(string) { + transformed["plain_text"] = d.Get("oidc.0.client_secret.0.value.0.plain_text") + } + return []interface{}{transformed} +} diff --git a/mmv1/templates/terraform/examples/iam_workforce_pool_provider_oidc_basic.tf.erb b/mmv1/templates/terraform/examples/iam_workforce_pool_provider_oidc_basic.tf.erb index 8683d745880f..a5adf114df0f 100644 --- a/mmv1/templates/terraform/examples/iam_workforce_pool_provider_oidc_basic.tf.erb +++ b/mmv1/templates/terraform/examples/iam_workforce_pool_provider_oidc_basic.tf.erb @@ -14,9 +14,14 @@ resource "google_iam_workforce_pool_provider" "<%= ctx[:primary_resource_id] %>" oidc { issuer_uri = "https://accounts.thirdparty.com" client_id = "client-id" + client_secret { + value { + plain_text = "client-secret" + } + } web_sso_config { - response_type = "ID_TOKEN" - assertion_claims_behavior = "ONLY_ID_TOKEN_CLAIMS" + response_type = "CODE" + assertion_claims_behavior = "MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS" } } } diff --git a/mmv1/templates/terraform/examples/iam_workforce_pool_provider_oidc_full.tf.erb b/mmv1/templates/terraform/examples/iam_workforce_pool_provider_oidc_full.tf.erb index 760cf4521230..a3d99681b821 100644 --- a/mmv1/templates/terraform/examples/iam_workforce_pool_provider_oidc_full.tf.erb +++ b/mmv1/templates/terraform/examples/iam_workforce_pool_provider_oidc_full.tf.erb @@ -14,9 +14,14 @@ resource "google_iam_workforce_pool_provider" "<%= ctx[:primary_resource_id] %>" oidc { issuer_uri = "https://accounts.thirdparty.com" client_id = "client-id" + client_secret { + value { + plain_text = "client-secret" + } + } web_sso_config { - response_type = "ID_TOKEN" - assertion_claims_behavior = "ONLY_ID_TOKEN_CLAIMS" + response_type = "CODE" + assertion_claims_behavior = "MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS" } } display_name = "Display name" diff --git a/mmv1/templates/terraform/post_create/iam_workforce_pool_provider.go.erb b/mmv1/templates/terraform/post_create/iam_workforce_pool_provider.go.erb new file mode 100644 index 000000000000..25aaf9eb73b5 --- /dev/null +++ b/mmv1/templates/terraform/post_create/iam_workforce_pool_provider.go.erb @@ -0,0 +1,17 @@ +createdClientSecret := d.Get("oidc.0.client_secret.0.value.0.plain_text") +if createdClientSecret != nil && createdClientSecret != "" { + // After the create, reading from the API returns a new thumbprint + // for the client secret value, which clears the plain_text. We set the plain_text since + // this case should not warrant a diff. + if err := resourceIAMWorkforcePoolWorkforcePoolProviderRead(d, meta); err != nil { + return err + } + oidc := d.Get("oidc") + clientSecret := oidc.([]interface{})[0].(map[string]interface{})["client_secret"] + clientSecretValue := clientSecret.([]interface{})[0].(map[string]interface{})["value"] + clientSecretValue.([]interface{})[0].(map[string]interface{})["plain_text"] = createdClientSecret + if err := d.Set("oidc", oidc); err != nil { + return err + } + return nil +} diff --git a/mmv1/templates/terraform/post_update/iam_workforce_pool_provider.go.erb b/mmv1/templates/terraform/post_update/iam_workforce_pool_provider.go.erb new file mode 100644 index 000000000000..f82d1d27a253 --- /dev/null +++ b/mmv1/templates/terraform/post_update/iam_workforce_pool_provider.go.erb @@ -0,0 +1,19 @@ +if d.HasChange("oidc") { + updatedClientSecret := d.Get("oidc.0.client_secret.0.value.0.plain_text") + if updatedClientSecret != nil && updatedClientSecret != "" { + // After the update, reading from the API returns a different thumbprint + // for the client secret value, which clears the plain_text. We set the plain_text since + // this case should not warrant a diff. + if err := resourceIAMWorkforcePoolWorkforcePoolProviderRead(d, meta); err != nil { + return err + } + oidc := d.Get("oidc") + clientSecret := oidc.([]interface{})[0].(map[string]interface{})["client_secret"] + clientSecretValue := clientSecret.([]interface{})[0].(map[string]interface{})["value"] + clientSecretValue.([]interface{})[0].(map[string]interface{})["plain_text"] = updatedClientSecret + if err := d.Set("oidc", oidc); err != nil { + return err + } + return nil + } +} diff --git a/mmv1/third_party/terraform/tests/resource_iam_workforce_pool_workforce_pool_provider_test.go.erb b/mmv1/third_party/terraform/tests/resource_iam_workforce_pool_workforce_pool_provider_test.go.erb index 4bcb3d0cf0d1..1a1153dc7728 100644 --- a/mmv1/third_party/terraform/tests/resource_iam_workforce_pool_workforce_pool_provider_test.go.erb +++ b/mmv1/third_party/terraform/tests/resource_iam_workforce_pool_workforce_pool_provider_test.go.erb @@ -35,10 +35,20 @@ func TestAccIAMWorkforcePoolWorkforcePoolProvider_oidc(t *testing.T) { ResourceName: "google_iam_workforce_pool_provider.my_provider", ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"oidc.0.client_secret.0.value.0.plain_text"}, }, { Config: testAccIAMWorkforcePoolWorkforcePoolProvider_oidc_update(context), }, + { + ResourceName: "google_iam_workforce_pool_provider.my_provider", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"oidc.0.client_secret.0.value.0.plain_text"}, + }, + { + Config: testAccIAMWorkforcePoolWorkforcePoolProvider_oidc_update_clearClientSecret(context), + }, { ResourceName: "google_iam_workforce_pool_provider.my_provider", ImportState: true, @@ -51,6 +61,7 @@ func TestAccIAMWorkforcePoolWorkforcePoolProvider_oidc(t *testing.T) { ResourceName: "google_iam_workforce_pool_provider.my_provider", ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"oidc.0.client_secret.0.value.0.plain_text"}, }, { Config: testAccIAMWorkforcePoolWorkforcePoolProvider_destroy(context), @@ -161,9 +172,14 @@ resource "google_iam_workforce_pool_provider" "my_provider" { oidc { issuer_uri = "https://accounts.thirdparty.com" client_id = "client-id" + client_secret { + value { + plain_text = "client-secret" + } + } web_sso_config { - response_type = "ID_TOKEN" - assertion_claims_behavior = "ONLY_ID_TOKEN_CLAIMS" + response_type = "CODE" + assertion_claims_behavior = "MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS" } } display_name = "Display name" @@ -182,6 +198,42 @@ resource "google_iam_workforce_pool" "my_pool" { location = "global" } +resource "google_iam_workforce_pool_provider" "my_provider" { + workforce_pool_id = google_iam_workforce_pool.my_pool.workforce_pool_id + location = google_iam_workforce_pool.my_pool.location + provider_id = "my-provider-%{random_suffix}" + attribute_mapping = { + "google.subject" = "false" + } + oidc { + issuer_uri = "https://test.thirdparty.com" + client_id = "new-client-id" + client_secret { + value { + plain_text = "new-client-secret" + } + } + web_sso_config { + response_type = "ID_TOKEN" + assertion_claims_behavior = "ONLY_ID_TOKEN_CLAIMS" + } + } + display_name = "New Display name" + description = "A sample OIDC workforce pool provider with updated description." + disabled = true + attribute_condition = "false" +} +`, context) +} + +func testAccIAMWorkforcePoolWorkforcePoolProvider_oidc_update_clearClientSecret(context map[string]interface{}) string { + return Nprintf(` +resource "google_iam_workforce_pool" "my_pool" { + workforce_pool_id = "my-pool-%{random_suffix}" + parent = "organizations/%{org_id}" + location = "global" +} + resource "google_iam_workforce_pool_provider" "my_provider" { workforce_pool_id = google_iam_workforce_pool.my_pool.workforce_pool_id location = google_iam_workforce_pool.my_pool.location @@ -223,9 +275,14 @@ resource "google_iam_workforce_pool_provider" "my_provider" { oidc { issuer_uri = "https://accounts.thirdparty.com" client_id = "client-id" + client_secret { + value { + plain_text = "client-secret" + } + } web_sso_config { - response_type = "ID_TOKEN" - assertion_claims_behavior = "ONLY_ID_TOKEN_CLAIMS" + response_type = "CODE" + assertion_claims_behavior = "MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS" } } }