Skip to content

Commit 2c5d726

Browse files
committed
feat: Add OIDC client resource.
This adds support for the OIDC Provider OIDCClient resource.
1 parent fb997de commit 2c5d726

7 files changed

Lines changed: 976 additions & 0 deletions

aspell_custom.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ destructuring
4343
yaml
4444
ttl
4545
pkce
46+
oidcclients

docs/resources/oidc_client.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
page_title: "rancher2_oidc_client Resource"
3+
---
4+
5+
# rancher2\_oidc_client Resource
6+
7+
Provides a Rancher OIDC Client. This can be used to configure the OIDC Clients
8+
available for the Rancher OIDC Provider.
9+
10+
## Example Usage
11+
12+
### Creating a Rancher OIDC Client.
13+
14+
```hcl
15+
resource "rancher2_oidc_client" "oidc-test-client" {
16+
description = "Access for Test Client"
17+
token_expiration_seconds = 600 # expiration of the id_token and access_token
18+
refresh_token_expiration_seconds = 7200 # expiration of the refresh_token
19+
redirect_uris = [
20+
"http://127.0.0.1:5556/auth/rancher/callback",
21+
"http://127.0.0.1:33418/",
22+
"https://vscode.dev/redirect"
23+
]
24+
}
25+
```
26+
27+
## Argument Reference
28+
29+
The following arguments are supported:
30+
31+
* `description` - A human-readable description for the OIDC Client.
32+
* `token_expiration_seconds` - ID Token and Access Token will only be valid for this many seconds.
33+
* `refresh_token_expiration_seconds` - How long can the refresh token be used for?
34+
* `redirect_uris` - Provides a list of allowed redirect URIs for this OIDC Client.
35+
* `annotations` - (Optional/Computed) Annotations for OIDC Client object (map)
36+
* `labels` - (Optional/Computed) Labels for OIDC Client object (map)
37+
38+
## Attributes Reference
39+
40+
The following attributes are exported:
41+
42+
* `id` - (Computed) The ID of the resource (string)
43+
* `client_id` - (Computed) The ID to be used when authenticating as this OIDC Client.

rancher2/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ func Provider() terraform.ResourceProvider {
140140
"rancher2_namespace": resourceRancher2Namespace(),
141141
"rancher2_node_driver": resourceRancher2NodeDriver(),
142142
"rancher2_node_pool": resourceRancher2NodePool(),
143+
"rancher2_oidc_client": resourceRancher2OIDCClient(),
143144
"rancher2_pod_security_admission_configuration_template": resourceRancher2PodSecurityAdmissionConfigurationTemplate(),
144145
"rancher2_project": resourceRancher2Project(),
145146
"rancher2_project_role_template_binding": resourceRancher2ProjectRoleTemplateBinding(),
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package rancher2
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"maps"
7+
"time"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
10+
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
11+
managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
12+
)
13+
14+
func resourceRancher2OIDCClient() *schema.Resource {
15+
return &schema.Resource{
16+
Create: resourceRancher2OIDCClientCreate,
17+
Read: resourceRancher2OIDCClientRead,
18+
Update: resourceRancher2OIDCClientUpdate,
19+
Delete: resourceRancher2OIDCClientDelete,
20+
Importer: &schema.ResourceImporter{
21+
State: resourceRancher2OIDCClientImport,
22+
},
23+
24+
Schema: oidcClientFields(),
25+
Timeouts: &schema.ResourceTimeout{
26+
Create: schema.DefaultTimeout(5 * time.Minute),
27+
Update: schema.DefaultTimeout(5 * time.Minute),
28+
Delete: schema.DefaultTimeout(5 * time.Minute),
29+
},
30+
}
31+
}
32+
33+
func oidcClientFields() map[string]*schema.Schema {
34+
s := map[string]*schema.Schema{
35+
"token_expiration_seconds": {
36+
Type: schema.TypeInt,
37+
Optional: true,
38+
Computed: true,
39+
Description: "The duration (in seconds) before an access token and ID token expire.",
40+
ValidateFunc: validation.IntAtLeast(1),
41+
},
42+
"refresh_token_expiration_seconds": {
43+
Type: schema.TypeInt,
44+
Optional: true,
45+
Computed: true,
46+
Description: "The duration (in seconds) a refresh token remains valid before it expires.",
47+
ValidateFunc: validation.IntAtLeast(1),
48+
},
49+
"redirect_uris": {
50+
Description: "List of allowed redirect_uris for this client.",
51+
Required: true,
52+
Type: schema.TypeList,
53+
Elem: &schema.Schema{
54+
Type: schema.TypeString,
55+
},
56+
},
57+
"description": {
58+
Type: schema.TypeString,
59+
Optional: true,
60+
Description: "OIDCClient description",
61+
},
62+
"client_id": {
63+
Type: schema.TypeString,
64+
Computed: true,
65+
Description: "The Client ID for OIDC Access.",
66+
},
67+
}
68+
69+
maps.Copy(s, commonAnnotationLabelFields())
70+
71+
return s
72+
}
73+
74+
func resourceRancher2OIDCClientCreate(d *schema.ResourceData, meta any) error {
75+
log.Printf("[INFO] Creating OIDCClient")
76+
oidcClient, err := expandOIDCClient(d)
77+
if err != nil {
78+
return err
79+
}
80+
81+
client, err := meta.(managementClientGetter).ManagementClient()
82+
if err != nil {
83+
log.Printf("[ERROR] getting a client when creating OIDC Client: %s", err)
84+
return fmt.Errorf("getting a client when creating OIDC Client: %w", err)
85+
}
86+
87+
createdClient, err := client.OIDCClient.Create(oidcClient)
88+
if err != nil {
89+
log.Printf("[ERROR] creating OIDCClient: %s", err)
90+
return fmt.Errorf("creating OIDCClient: %w", err)
91+
}
92+
93+
d.SetId(createdClient.ID)
94+
95+
return resourceRancher2OIDCClientRead(d, meta)
96+
}
97+
98+
func resourceRancher2OIDCClientRead(d *schema.ResourceData, meta any) error {
99+
log.Printf("[INFO] Refreshing OIDCClient ID %s", d.Id())
100+
101+
client, err := meta.(managementClientGetter).ManagementClient()
102+
if err != nil {
103+
log.Printf("[ERROR] getting a client when reading OIDC Client: %s", err)
104+
return fmt.Errorf("getting a client when reading OIDC Client: %w", err)
105+
}
106+
oidcClient, err := client.OIDCClient.ByID(d.Id())
107+
if err != nil {
108+
if IsNotFound(err) || IsForbidden(err) || IsNotAccessibleByID(err) {
109+
log.Printf("[INFO] OIDC Client %s not found", d.Id())
110+
d.SetId("")
111+
return nil
112+
}
113+
log.Printf("[ERROR] reading OIDC Client %s: %s", d.Id(), err)
114+
return fmt.Errorf("reading OIDC Client %s: %w", d.Id(), err)
115+
}
116+
117+
return flattenOIDCClient(d, oidcClient)
118+
}
119+
120+
func resourceRancher2OIDCClientUpdate(d *schema.ResourceData, meta any) error {
121+
log.Printf("[INFO] Updating OIDCClient ID %s", d.Id())
122+
123+
client, err := meta.(managementClientGetter).ManagementClient()
124+
if err != nil {
125+
log.Printf("[ERROR] getting a client when updating OIDC Client: %s", err)
126+
return fmt.Errorf("getting a client when updating OIDC Client: %w", err)
127+
}
128+
129+
oidcClient, err := client.OIDCClient.ByID(d.Id())
130+
if err != nil {
131+
log.Printf("[ERROR] getting OIDC Client %s for update: %s", d.Id(), err)
132+
return fmt.Errorf("getting OIDC Client %s for update: %w", d.Id(), err)
133+
}
134+
135+
update := map[string]any{
136+
"labels": toMapString(d.Get("labels").(map[string]any)),
137+
"annotations": toMapString(d.Get("annotations").(map[string]any)),
138+
"description": d.Get("description"),
139+
"redirectURIs": toArrayString(d.Get("redirect_uris").([]any)),
140+
"tokenExpirationSeconds": d.Get("token_expiration_seconds"),
141+
"refreshTokenExpirationSeconds": d.Get("refresh_token_expiration_seconds"),
142+
}
143+
144+
_, err = client.OIDCClient.Update(oidcClient, update)
145+
if err != nil {
146+
log.Printf("[ERROR] updating OIDC Client %s: %s", d.Id(), err)
147+
return fmt.Errorf("updating OIDC Client %s: %w", d.Id(), err)
148+
}
149+
150+
return resourceRancher2OIDCClientRead(d, meta)
151+
}
152+
153+
func resourceRancher2OIDCClientDelete(d *schema.ResourceData, meta any) error {
154+
log.Printf("[INFO] Deleting OIDCClient ID %s", d.Id())
155+
156+
client, err := meta.(managementClientGetter).ManagementClient()
157+
if err != nil {
158+
log.Printf("[ERROR] getting a client when deleting OIDC Client: %s", err)
159+
return fmt.Errorf("getting a client when deleting OIDC Client: %w", err)
160+
}
161+
162+
oidcClient, err := client.OIDCClient.ByID(d.Id())
163+
if err != nil {
164+
if IsNotFound(err) || IsForbidden(err) || IsNotAccessibleByID(err) {
165+
log.Printf("[INFO] OIDC Client ID %s not found", d.Id())
166+
d.SetId("")
167+
return nil
168+
}
169+
return fmt.Errorf("getting OIDC Client %s for deletion: %w", d.Id(), err)
170+
}
171+
172+
err = client.OIDCClient.Delete(oidcClient)
173+
if err != nil {
174+
if !IsNotFound(err) {
175+
log.Printf("[ERROR] deleting OIDC Client %s: %s", d.Id(), err)
176+
return fmt.Errorf("deleting OIDC Client %s: %w", d.Id(), err)
177+
}
178+
}
179+
180+
d.SetId("")
181+
return nil
182+
}
183+
184+
func resourceRancher2OIDCClientImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
185+
err := resourceRancher2OIDCClientRead(d, meta)
186+
if err != nil || d.Id() == "" {
187+
return []*schema.ResourceData{}, err
188+
}
189+
190+
return []*schema.ResourceData{d}, nil
191+
}
192+
193+
type managementClientGetter interface {
194+
ManagementClient() (*managementClient.Client, error)
195+
}

0 commit comments

Comments
 (0)