Skip to content

Commit 56db509

Browse files
plus-1sDawid212
authored andcommitted
Add new resource IAM OAuth Client for Workforce Identity Federation. (GoogleCloudPlatform#13209)
1 parent 6a27e13 commit 56db509

File tree

7 files changed

+354
-6
lines changed

7 files changed

+354
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# Copyright 2024 Google Inc.
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
---
15+
name: OauthClient
16+
description: |
17+
Represents an OAuth Client. Used to access Google Cloud resources on behalf of a
18+
Workforce Identity Federation user by using OAuth 2.0 Protocol to obtain an access
19+
token from Google Cloud.
20+
references:
21+
guides:
22+
"Managing OAuth clients": "https://cloud.google.com/iam/docs/workforce-manage-oauth-app#manage-clients"
23+
api: "https://cloud.google.com/iam/docs/reference/rest/v1/projects.locations.oauthClients"
24+
base_url: projects/{{project}}/locations/{{location}}/oauthClients
25+
update_mask: true
26+
self_link: projects/{{project}}/locations/{{location}}/oauthClients/{{oauth_client_id}}
27+
create_url: projects/{{project}}/locations/{{location}}/oauthClients?oauthClientId={{oauth_client_id}}
28+
update_verb: PATCH
29+
id_format: projects/{{project}}/locations/{{location}}/oauthClients/{{oauth_client_id}}
30+
import_format:
31+
- projects/{{project}}/locations/{{location}}/oauthClients/{{oauth_client_id}}
32+
timeouts:
33+
insert_minutes: 20
34+
update_minutes: 20
35+
delete_minutes: 20
36+
custom_code:
37+
constants: "templates/terraform/constants/iam_oauth_client.go.tmpl"
38+
decoder: "templates/terraform/decoders/treat_deleted_state_as_gone.go.tmpl"
39+
test_check_destroy: "templates/terraform/custom_check_destroy/iam_oauth_client.go.tmpl"
40+
post_create: "templates/terraform/post_create/sleep.go.tmpl"
41+
post_update: "templates/terraform/post_create/sleep.go.tmpl"
42+
post_delete: "templates/terraform/post_create/sleep.go.tmpl"
43+
exclude_sweeper: true
44+
examples:
45+
- name: "iam_oauth_client_basic"
46+
primary_resource_id: "example"
47+
vars:
48+
oauth_client_id: "example-client-id"
49+
- name: "iam_oauth_client_full"
50+
primary_resource_id: "example"
51+
vars:
52+
oauth_client_id: "example-client-id"
53+
parameters:
54+
- name: location
55+
type: String
56+
description: Resource ID segment making up resource `name`. It identifies the resource within its parent collection as described in https://google.aip.dev/122.
57+
immutable: true
58+
url_param_only: true
59+
required: true
60+
- name: oauthClientId
61+
type: String
62+
description: |-
63+
Required. The ID to use for the OauthClient, which becomes the final component of
64+
the resource name. This value should be a string of 6 to 63 lowercase
65+
letters, digits, or hyphens. It must start with a letter, and cannot have a
66+
trailing hyphen. The prefix `gcp-` is reserved for use by Google, and may
67+
not be specified.
68+
immutable: true
69+
url_param_only: true
70+
required: true
71+
properties:
72+
- name: allowedScopes
73+
type: Array
74+
description: |-
75+
Required. The list of scopes that the OauthClient is allowed to request during
76+
OAuth flows.
77+
78+
The following scopes are supported:
79+
80+
* `https://www.googleapis.com/auth/cloud-platform`: See, edit, configure,
81+
and delete your Google Cloud data and see the email address for your Google
82+
Account.
83+
* `openid`: The OAuth client can associate you with your personal
84+
information on Google Cloud.
85+
* `email`: The OAuth client can read a federated identity's email address.
86+
* `groups`: The OAuth client can read a federated identity's groups.
87+
required: true
88+
item_type:
89+
type: String
90+
- name: name
91+
type: String
92+
description: |-
93+
Immutable. Identifier. The resource name of the OauthClient.
94+
95+
Format:`projects/{project}/locations/{location}/oauthClients/{oauth_client}`.
96+
output: true
97+
immutable: true
98+
- name: state
99+
type: String
100+
description: |-
101+
Output only. The state of the OauthClient.
102+
Possible values:
103+
STATE_UNSPECIFIED
104+
ACTIVE
105+
DELETED
106+
output: true
107+
- name: disabled
108+
type: Boolean
109+
description: |-
110+
Optional. Whether the OauthClient is disabled. You cannot use a disabled OAuth
111+
client.
112+
- name: clientId
113+
type: String
114+
description: Output only. The system-generated OauthClient id.
115+
output: true
116+
- name: displayName
117+
type: String
118+
description: |-
119+
Optional. A user-specified display name of the OauthClient.
120+
121+
Cannot exceed 32 characters.
122+
- name: description
123+
type: String
124+
description: |-
125+
Optional. A user-specified description of the OauthClient.
126+
127+
Cannot exceed 256 characters.
128+
- name: allowedGrantTypes
129+
type: Array
130+
description: Required. The list of OAuth grant types is allowed for the OauthClient.
131+
required: true
132+
item_type:
133+
type: String
134+
- name: expireTime
135+
type: String
136+
description: |-
137+
Output only. Time after which the OauthClient will be permanently purged and cannot
138+
be recovered.
139+
output: true
140+
- name: clientType
141+
type: String
142+
description: |-
143+
Immutable. The type of OauthClient. Either public or private.
144+
For private clients, the client secret can be managed using the dedicated
145+
OauthClientCredential resource.
146+
Possible values:
147+
CLIENT_TYPE_UNSPECIFIED
148+
PUBLIC_CLIENT
149+
CONFIDENTIAL_CLIENT
150+
immutable: true
151+
- name: allowedRedirectUris
152+
type: Array
153+
description: |-
154+
Required. The list of redirect uris that is allowed to redirect back
155+
when authorization process is completed.
156+
required: true
157+
item_type:
158+
type: String

mmv1/products/iamworkforcepool/product.yaml

-6
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,3 @@ versions:
2222
base_url: 'https://iam.googleapis.com/v1/'
2323
scopes:
2424
- 'https://www.googleapis.com/auth/iam'
25-
async:
26-
type: "OpAsync"
27-
operation:
28-
base_url: '{{op_id}}'
29-
result:
30-
resource_inside_response: false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const oauthClientIdRegexp = `^[a-z][a-z0-9-]{4,61}[a-z0-9]$`
2+
3+
func ValidateOauthClientId(v interface{}, k string) (ws []string, errors []error) {
4+
value := v.(string)
5+
6+
if strings.HasPrefix(value, "gcp-") {
7+
errors = append(errors, fmt.Errorf(
8+
"%q (%q) can not start with \"gcp-\". " +
9+
"The prefix `gcp-` is reserved for use by Google, and may not be specified.", k, value))
10+
}
11+
12+
if !regexp.MustCompile(oauthClientIdRegexp).MatchString(value) {
13+
errors = append(errors, fmt.Errorf(
14+
"%q (%q) must contain only lowercase letters [a-z], digits [0-9], and hyphens " +
15+
"[-]. The OauthClient ID must be between 6 and 63 characters, begin " +
16+
"with a letter, and cannot have a trailing hyphen.", k, value))
17+
}
18+
19+
return
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
config := acctest.GoogleProviderConfig(t)
2+
3+
url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{"{{"}}IAMWorkforcePoolBasePath{{"}}"}}projects/{{"{{"}}project{{"}}"}}/locations/global/oauthClients/{{"{{"}}oauth_client_id{{"}}"}}")
4+
if err != nil {
5+
return err
6+
}
7+
8+
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
9+
Config: config,
10+
Method: "GET",
11+
RawURL: url,
12+
UserAgent: config.UserAgent,
13+
})
14+
if err != nil {
15+
return nil
16+
}
17+
18+
if v := res["state"]; v == "DELETED" {
19+
return nil
20+
}
21+
22+
return fmt.Errorf("IAMOAuthCLient still exists at %s", url)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
resource "google_iam_oauth_client" "{{$.PrimaryResourceId}}" {
2+
oauth_client_id = "{{index $.Vars "oauth_client_id"}}"
3+
location = "global"
4+
allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"]
5+
allowed_redirect_uris = ["https://www.example.com"]
6+
allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
7+
client_type = "CONFIDENTIAL_CLIENT"
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
resource "google_iam_oauth_client" "{{$.PrimaryResourceId}}" {
2+
oauth_client_id = "{{index $.Vars "oauth_client_id"}}"
3+
display_name = "Display Name of OAuth client"
4+
description = "A sample OAuth client"
5+
location = "global"
6+
disabled = false
7+
allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"]
8+
allowed_redirect_uris = ["https://www.example.com"]
9+
allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
10+
client_type = "CONFIDENTIAL_CLIENT"
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package iamworkforcepool_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
7+
8+
"github.com/hashicorp/terraform-provider-google/google/acctest"
9+
)
10+
11+
func TestAccIAMWorkforcePoolOauthClient_basic(t *testing.T) {
12+
t.Parallel()
13+
14+
context := map[string]interface{}{
15+
"random_suffix": acctest.RandString(t, 10),
16+
}
17+
18+
acctest.VcrTest(t, resource.TestCase{
19+
PreCheck: func() { acctest.AccTestPreCheck(t) },
20+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
21+
CheckDestroy: testAccCheckIAMWorkforcePoolOauthClientDestroyProducer(t),
22+
Steps: []resource.TestStep{
23+
{
24+
Config: testAccIAMWorkforcePoolOauthClient_basic(context),
25+
},
26+
{
27+
ResourceName: "google_iam_oauth_client.example",
28+
ImportState: true,
29+
ImportStateVerify: true,
30+
ImportStateVerifyIgnore: []string{"location", "oauth_client_id"},
31+
},
32+
{
33+
Config: testAccIAMWorkforcePoolOauthClient_basic_update(context),
34+
},
35+
{
36+
ResourceName: "google_iam_oauth_client.example",
37+
ImportState: true,
38+
ImportStateVerify: true,
39+
ImportStateVerifyIgnore: []string{"location", "oauth_client_id"},
40+
},
41+
},
42+
})
43+
}
44+
45+
func testAccIAMWorkforcePoolOauthClient_basic(context map[string]interface{}) string {
46+
return acctest.Nprintf(`
47+
resource "google_iam_oauth_client" "example" {
48+
oauth_client_id = "tf-test-example-client-id%{random_suffix}"
49+
location = "global"
50+
allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"]
51+
allowed_redirect_uris = ["https://www.example.com"]
52+
allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
53+
client_type = "CONFIDENTIAL_CLIENT"
54+
}
55+
`, context)
56+
}
57+
58+
func testAccIAMWorkforcePoolOauthClient_basic_update(context map[string]interface{}) string {
59+
return acctest.Nprintf(`
60+
resource "google_iam_oauth_client" "example" {
61+
oauth_client_id = "tf-test-example-client-id%{random_suffix}"
62+
location = "global"
63+
allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"]
64+
allowed_redirect_uris = ["https://www.update.com"]
65+
allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform", "openid"]
66+
client_type = "CONFIDENTIAL_CLIENT"
67+
}
68+
`, context)
69+
}
70+
71+
func TestAccIAMWorkforcePoolOauthClient_full(t *testing.T) {
72+
t.Parallel()
73+
74+
context := map[string]interface{}{
75+
"random_suffix": acctest.RandString(t, 10),
76+
}
77+
78+
acctest.VcrTest(t, resource.TestCase{
79+
PreCheck: func() { acctest.AccTestPreCheck(t) },
80+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
81+
CheckDestroy: testAccCheckIAMWorkforcePoolOauthClientDestroyProducer(t),
82+
Steps: []resource.TestStep{
83+
{
84+
Config: testAccIAMWorkforcePoolOauthClient_full(context),
85+
},
86+
{
87+
ResourceName: "google_iam_oauth_client.example",
88+
ImportState: true,
89+
ImportStateVerify: true,
90+
ImportStateVerifyIgnore: []string{"location", "oauth_client_id"},
91+
},
92+
{
93+
Config: testAccIAMWorkforcePoolOauthClient_full_update(context),
94+
},
95+
{
96+
ResourceName: "google_iam_oauth_client.example",
97+
ImportState: true,
98+
ImportStateVerify: true,
99+
ImportStateVerifyIgnore: []string{"location", "oauth_client_id"},
100+
},
101+
},
102+
})
103+
}
104+
105+
func testAccIAMWorkforcePoolOauthClient_full(context map[string]interface{}) string {
106+
return acctest.Nprintf(`
107+
resource "google_iam_oauth_client" "example" {
108+
oauth_client_id = "tf-test-example-client-id%{random_suffix}"
109+
display_name = "Display Name of OAuth client"
110+
description = "A sample OAuth client"
111+
location = "global"
112+
disabled = false
113+
allowed_grant_types = ["AUTHORIZATION_CODE_GRANT"]
114+
allowed_redirect_uris = ["https://www.example.com"]
115+
allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
116+
client_type = "CONFIDENTIAL_CLIENT"
117+
}
118+
`, context)
119+
}
120+
121+
func testAccIAMWorkforcePoolOauthClient_full_update(context map[string]interface{}) string {
122+
return acctest.Nprintf(`
123+
resource "google_iam_oauth_client" "example" {
124+
oauth_client_id = "tf-test-example-client-id%{random_suffix}"
125+
display_name = "Updated displayName"
126+
description = "Updated description"
127+
location = "global"
128+
disabled = true
129+
allowed_grant_types = ["AUTHORIZATION_CODE_GRANT", ]
130+
allowed_redirect_uris = ["https://www.update.com"]
131+
allowed_scopes = ["https://www.googleapis.com/auth/cloud-platform", "openid"]
132+
client_type = "CONFIDENTIAL_CLIENT"
133+
}
134+
`, context)
135+
}

0 commit comments

Comments
 (0)