Skip to content

Commit ed00cc1

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

4 files changed

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

0 commit comments

Comments
 (0)