Skip to content

Commit 7774583

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

7 files changed

Lines changed: 968 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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
### Creating a Rancher OIDC Client.
11+
12+
```hcl
13+
resource "rancher2_oidc_client" "oidc-test-client" {
14+
description = "Access for Test Client"
15+
token_expiration_seconds = 600 # expiration of the id_token and access_token
16+
refresh_token_expiration_seconds = 7200 # expiration of the refresh_token
17+
redirect_uris = [
18+
"http://127.0.0.1:5556/auth/rancher/callback",
19+
"http://127.0.0.1:33418/",
20+
"https://vscode.dev/redirect"
21+
]
22+
}
23+
```
24+
25+
## Argument Reference
26+
27+
The following arguments are supported:
28+
29+
* `description` - used as a human-readable name for the created OIDC Client.
30+
* `token_expiration_seconds` - ID Token and Access Token will only be valid for this many seconds.
31+
* `refresh_token_expiration_seconds` - How long can the refresh token be used for?
32+
* `redirect_uris` - Provides a list of allowed redirect URIs for this OIDC Client.
33+
34+
## Attributes Reference
35+
36+
The following attributes are exported:
37+
38+
* `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: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
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+
Optional: true,
65+
Computed: true,
66+
Description: "The Client ID for OIDC Access.",
67+
},
68+
}
69+
70+
maps.Copy(s, commonAnnotationLabelFields())
71+
72+
return s
73+
}
74+
75+
func resourceRancher2OIDCClientCreate(d *schema.ResourceData, meta any) error {
76+
log.Printf("[INFO] Creating OIDCClient")
77+
oidcClient, err := expandOIDCClient(d)
78+
if err != nil {
79+
return err
80+
}
81+
82+
client, err := meta.(managementClientGetter).ManagementClient()
83+
if err != nil {
84+
log.Printf("[ERROR] getting a client when creating OIDC Client: %s", err)
85+
return fmt.Errorf("getting a client when creating OIDC Client: %w", err)
86+
}
87+
88+
updatedClient, err := client.OIDCClient.Create(oidcClient)
89+
if err != nil {
90+
log.Printf("[ERROR] creating OIDCClient: %s", err)
91+
return fmt.Errorf("creating OIDCClient: %w", err)
92+
}
93+
94+
d.SetId(updatedClient.ID)
95+
96+
return resourceRancher2OIDCClientRead(d, meta)
97+
}
98+
99+
func resourceRancher2OIDCClientRead(d *schema.ResourceData, meta any) error {
100+
log.Printf("[INFO] Refreshing OIDCClient ID %s", d.Id())
101+
102+
client, err := meta.(managementClientGetter).ManagementClient()
103+
if err != nil {
104+
log.Printf("[ERROR] getting a client when reading OIDC Client: %s", err)
105+
return fmt.Errorf("getting a client when reading OIDC Client: %w", err)
106+
}
107+
oidcClient, err := client.OIDCClient.ByID(d.Id())
108+
if err != nil {
109+
if IsNotFound(err) || IsForbidden(err) || IsNotAccessibleByID(err) {
110+
log.Printf("[INFO] OIDC Client %s not found", d.Id())
111+
d.SetId("")
112+
return nil
113+
}
114+
log.Printf("[ERROR] reading OIDC Client %s: %s", d.Id(), err)
115+
return fmt.Errorf("reading OIDC Client %s: %w", d.Id(), err)
116+
}
117+
118+
return flattenOIDCClient(d, oidcClient)
119+
}
120+
121+
func resourceRancher2OIDCClientUpdate(d *schema.ResourceData, meta any) error {
122+
log.Printf("[INFO] Updating OIDCClient ID %s", d.Id())
123+
124+
client, err := meta.(managementClientGetter).ManagementClient()
125+
if err != nil {
126+
log.Printf("[ERROR] getting a client when updating OIDC Client: %s", err)
127+
return fmt.Errorf("getting a client when updating OIDC Client: %w", err)
128+
}
129+
130+
oidcClient, err := client.OIDCClient.ByID(d.Id())
131+
if err != nil {
132+
log.Printf("[ERROR] getting OIDC Client %s for update: %s", d.Id(), err)
133+
return fmt.Errorf("getting OIDC Client %s for update: %w", d.Id(), err)
134+
}
135+
136+
update := map[string]any{
137+
"labels": toMapString(d.Get("labels").(map[string]any)),
138+
"annotations": toMapString(d.Get("annotations").(map[string]any)),
139+
"description": d.Get("description"),
140+
"redirectURIs": d.Get("redirect_uris"),
141+
"tokenExpirationSeconds": d.Get("token_expiration_seconds"),
142+
"refreshTokenExpirationSeconds": d.Get("refresh_token_expiration_seconds"),
143+
}
144+
145+
_, err = client.OIDCClient.Update(oidcClient, update)
146+
if err != nil {
147+
log.Printf("[ERROR] updating OIDC Client %s: %s", d.Id(), err)
148+
return fmt.Errorf("updating OIDC Client %s: %w", d.Id(), err)
149+
}
150+
151+
return resourceRancher2OIDCClientRead(d, meta)
152+
}
153+
154+
func resourceRancher2OIDCClientDelete(d *schema.ResourceData, meta any) error {
155+
log.Printf("[INFO] Deleting OIDCClient ID %s", d.Id())
156+
157+
client, err := meta.(managementClientGetter).ManagementClient()
158+
if err != nil {
159+
log.Printf("[ERROR] getting a client when deleting OIDC Client: %s", err)
160+
return fmt.Errorf("getting a client when deleting OIDC Client: %w", err)
161+
}
162+
163+
oidcClient, err := client.OIDCClient.ByID(d.Id())
164+
if err != nil {
165+
if IsNotFound(err) || IsForbidden(err) || IsNotAccessibleByID(err) {
166+
log.Printf("[INFO] OIDC Client ID %s not found", d.Id())
167+
d.SetId("")
168+
return nil
169+
}
170+
return fmt.Errorf("getting OIDC Client %s for deletion: %w", d.Id(), err)
171+
}
172+
173+
err = client.OIDCClient.Delete(oidcClient)
174+
if err != nil {
175+
if !IsNotFound(err) {
176+
log.Printf("[ERROR] deleting OIDC Client %s: %s", d.Id(), err)
177+
return fmt.Errorf("deleting OIDC Client %s: %w", d.Id(), err)
178+
}
179+
}
180+
181+
d.SetId("")
182+
return nil
183+
}
184+
185+
func resourceRancher2OIDCClientImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
186+
err := resourceRancher2OIDCClientRead(d, meta)
187+
if err != nil || d.Id() == "" {
188+
return []*schema.ResourceData{}, err
189+
}
190+
191+
return []*schema.ResourceData{d}, nil
192+
}
193+
194+
type managementClientGetter interface {
195+
ManagementClient() (*managementClient.Client, error)
196+
}

0 commit comments

Comments
 (0)