Skip to content

Commit 478a5ef

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

4 files changed

Lines changed: 797 additions & 0 deletions

File tree

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

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: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
package rancher2
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"log"
7+
"maps"
8+
"time"
9+
10+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
11+
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
12+
managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
13+
)
14+
15+
func resourceRancher2OIDCClient() *schema.Resource {
16+
return &schema.Resource{
17+
Create: resourceRancher2OIDCClientCreate,
18+
Read: resourceRancher2OIDCClientRead,
19+
Update: resourceRancher2OIDCClientUpdate,
20+
Delete: resourceRancher2OIDCClientDelete,
21+
22+
Schema: oidcClientFields(),
23+
Timeouts: &schema.ResourceTimeout{
24+
Create: schema.DefaultTimeout(5 * time.Minute),
25+
Update: schema.DefaultTimeout(5 * time.Minute),
26+
Delete: schema.DefaultTimeout(5 * time.Minute),
27+
},
28+
}
29+
}
30+
31+
func oidcClientFields() map[string]*schema.Schema {
32+
s := map[string]*schema.Schema{
33+
"token_expiration_seconds": {
34+
Type: schema.TypeInt,
35+
Optional: true,
36+
Computed: true,
37+
Description: "The duration (in seconds) before an access token and ID token expires.",
38+
ValidateFunc: validation.IntAtLeast(1),
39+
},
40+
"refresh_token_expiration_seconds": {
41+
Type: schema.TypeInt,
42+
Optional: true,
43+
Computed: true,
44+
Description: "The duration (in seconds) a refresh token remains valid before expiration.",
45+
ValidateFunc: validation.IntAtLeast(1),
46+
},
47+
"redirect_uris": {
48+
Description: "List of allowed redirect_uris for this client.",
49+
Required: true,
50+
Type: schema.TypeList,
51+
Elem: &schema.Schema{
52+
Type: schema.TypeString,
53+
},
54+
},
55+
"description": {
56+
Type: schema.TypeString,
57+
Optional: true,
58+
Description: "OIDCClient description",
59+
},
60+
}
61+
62+
maps.Copy(s, commonAnnotationLabelFields())
63+
64+
return s
65+
}
66+
67+
func resourceRancher2OIDCClientCreate(d *schema.ResourceData, meta any) error {
68+
log.Printf("[INFO] Creating OIDCClient")
69+
oidcClient, err := expandOIDCClient(d)
70+
if err != nil {
71+
return err
72+
}
73+
74+
client, err := meta.(managementClientGetter).ManagementClient()
75+
if err != nil {
76+
log.Printf("[ERROR] getting a client when creating OIDC Client: %s", err)
77+
return fmt.Errorf("getting a client when creating OIDC Client: %w", err)
78+
}
79+
80+
updatedClient, err := client.OIDCClient.Create(oidcClient)
81+
if err != nil {
82+
log.Printf("[ERROR] creating OIDCClient: %s", err)
83+
return fmt.Errorf("creating OIDCClient: %w", err)
84+
}
85+
86+
d.SetId(updatedClient.ID)
87+
88+
return resourceRancher2OIDCClientRead(d, meta)
89+
}
90+
91+
func resourceRancher2OIDCClientRead(d *schema.ResourceData, meta any) error {
92+
log.Printf("[INFO] Refreshing OIDCClient ID %s", d.Id())
93+
94+
client, err := meta.(managementClientGetter).ManagementClient()
95+
if err != nil {
96+
log.Printf("[ERROR] getting a client when reading OIDC Client: %s", err)
97+
return fmt.Errorf("getting a client when reading OIDC Client: %w", err)
98+
}
99+
oidcClient, err := client.OIDCClient.ByID(d.Id())
100+
if err != nil {
101+
if IsNotFound(err) || IsForbidden(err) || IsNotAccessibleByID(err) {
102+
log.Printf("[INFO] OIDC Client %s not found", d.Id())
103+
d.SetId("")
104+
return nil
105+
}
106+
log.Printf("[ERROR] reading OIDC Client %s: %s", d.Id(), err)
107+
return fmt.Errorf("reading OIDC Client %s: %w", d.Id(), err)
108+
}
109+
110+
return flattenOIDCClient(d, oidcClient)
111+
}
112+
113+
func resourceRancher2OIDCClientUpdate(d *schema.ResourceData, meta any) error {
114+
log.Printf("[INFO] Updating OIDCClient ID %s", d.Id())
115+
116+
client, err := meta.(managementClientGetter).ManagementClient()
117+
if err != nil {
118+
log.Printf("[ERROR] getting a client when updating OIDC Client: %s", err)
119+
return fmt.Errorf("getting a client when updating OIDC Client: %w", err)
120+
}
121+
122+
oidcClient, err := client.OIDCClient.ByID(d.Id())
123+
if err != nil {
124+
log.Printf("[ERROR] getting OIDC Client %s for update: %s", d.Id(), err)
125+
return fmt.Errorf("getting OIDC Client %s for update: %w", d.Id(), err)
126+
}
127+
128+
update := map[string]any{
129+
"description": d.Get("description"),
130+
"redirectURIs": d.Get("redirect_uris"),
131+
"token_expiration_seconds": d.Get("token_expiration_seconds"),
132+
"refresh_token_expiration_seconds": d.Get("refresh_token_expiration_seconds"),
133+
}
134+
135+
_, err = client.OIDCClient.Update(oidcClient, update)
136+
if err != nil {
137+
log.Printf("[ERROR] updating OIDC Client %s: %s", d.Id(), err)
138+
return fmt.Errorf("updating OIDC Client %s: %w", d.Id(), err)
139+
}
140+
141+
return resourceRancher2OIDCClientRead(d, meta)
142+
}
143+
144+
func resourceRancher2OIDCClientDelete(d *schema.ResourceData, meta any) error {
145+
log.Printf("[INFO] Deleting OIDCClient ID %s", d.Id())
146+
147+
client, err := meta.(managementClientGetter).ManagementClient()
148+
if err != nil {
149+
log.Printf("[ERROR] getting a client when deleting OIDC Client: %s", err)
150+
return fmt.Errorf("getting a client when deleting OIDC Client: %w", err)
151+
}
152+
153+
oidcClient, err := client.OIDCClient.ByID(d.Id())
154+
if err != nil {
155+
if IsNotFound(err) || IsForbidden(err) {
156+
log.Printf("[INFO] OIDC Client ID %s not found", d.Id())
157+
d.SetId("")
158+
return nil
159+
}
160+
return fmt.Errorf("getting OIDC Client %s for deletion: %w", d.Id(), err)
161+
}
162+
163+
err = client.OIDCClient.Delete(oidcClient)
164+
if err != nil {
165+
if !IsNotFound(err) {
166+
log.Printf("[ERROR] deleting OIDC Client %s: %s", d.Id(), err)
167+
return fmt.Errorf("deleting OIDC Client %s: %w", d.Id(), err)
168+
}
169+
}
170+
171+
d.SetId("")
172+
return nil
173+
}
174+
175+
func expandOIDCClient(in *schema.ResourceData) (*managementClient.OIDCClient, error) {
176+
obj := &managementClient.OIDCClient{}
177+
if in == nil {
178+
return nil, errors.New("expanding OIDC Client: Input ResourceData is nil")
179+
}
180+
181+
if v := in.Id(); len(v) > 0 {
182+
obj.ID = v
183+
}
184+
185+
if v, ok := in.GetOk("description"); ok {
186+
obj.Description = v.(string)
187+
}
188+
189+
v := in.Get("redirect_uris")
190+
obj.RedirectURIs = toArrayString(v.([]any))
191+
192+
v, ok := in.GetOk("token_expiration_seconds")
193+
if ok {
194+
// This should be safe because the field is declared as an integer.
195+
tokenExpirationSeconds, _ := v.(int)
196+
obj.TokenExpirationSeconds = int64(tokenExpirationSeconds)
197+
} else {
198+
obj.TokenExpirationSeconds = 0
199+
}
200+
201+
v, ok = in.GetOk("refresh_token_expiration_seconds")
202+
if ok {
203+
// This should be safe because the field is declared as an integer.
204+
refreshTokenExpirationSeconds, _ := v.(int)
205+
obj.RefreshTokenExpirationSeconds = int64(refreshTokenExpirationSeconds)
206+
} else {
207+
obj.RefreshTokenExpirationSeconds = 0
208+
}
209+
210+
if v, ok := in.Get("annotations").(map[string]any); ok && len(v) > 0 {
211+
obj.Annotations = toMapString(v)
212+
}
213+
214+
if v, ok := in.Get("labels").(map[string]any); ok && len(v) > 0 {
215+
obj.Labels = toMapString(v)
216+
}
217+
218+
return obj, nil
219+
}
220+
221+
func flattenOIDCClient(d *schema.ResourceData, in *managementClient.OIDCClient) error {
222+
if in == nil {
223+
return errors.New("flattening OIDC Client: Input config is nil")
224+
}
225+
226+
if in.ID != "" {
227+
d.SetId(in.ID)
228+
}
229+
230+
return errors.Join(
231+
d.Set("description", in.Description),
232+
d.Set("redirect_uris", in.RedirectURIs),
233+
d.Set("token_expiration_seconds", int(in.TokenExpirationSeconds)),
234+
d.Set("refresh_token_expiration_seconds", int(in.RefreshTokenExpirationSeconds)),
235+
d.Set("annotations", toMapInterface(in.Annotations)),
236+
d.Set("labels", toMapInterface(in.Labels)),
237+
)
238+
}
239+
240+
type managementClientGetter interface {
241+
ManagementClient() (*managementClient.Client, error)
242+
}

0 commit comments

Comments
 (0)