Skip to content

Commit

Permalink
Add OIDC code flow fields to WorkforcePoolProvider. (GoogleCloudPlatf…
Browse files Browse the repository at this point in the history
…orm#8212)

* Add OIDC code flow fields to WorkforcePoolProvider.

* Add ignore_read_extra for client secret plaintext to bypass ImportStateVerify check.

* Use server-provided  to correct external drift for client secret.

* Handle nil return values from d.Get.

* Validate that client secret plain_text is non-empty to ensure that clearing plain_text on thumbprint change always triggers a diff.
  • Loading branch information
misterpoe authored and ericayyliu committed Jul 26, 2023
1 parent dc7f9c6 commit 0b4819d
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 8 deletions.
38 changes: 38 additions & 0 deletions mmv1/products/iamworkforcepool/WorkforcePoolProvider.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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'
Expand Down Expand Up @@ -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: |
Expand All @@ -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
Original file line number Diff line number Diff line change
@@ -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}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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),
Expand Down Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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"
}
}
}
Expand Down

0 comments on commit 0b4819d

Please sign in to comment.