Skip to content

Commit 2a7b93c

Browse files
authored
Merge pull request #3 from aidy/issuance
Add support for jwks service accounts
2 parents 35ee153 + 5d8941d commit 2a7b93c

File tree

3 files changed

+144
-42
lines changed

3 files changed

+144
-42
lines changed

docs/resources/service_account.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,20 @@ description: |-
1717

1818
### Required
1919

20-
- `credential_lifetime` (Number)
2120
- `name` (String)
2221
- `owner` (String)
23-
- `public_key` (String)
2422
- `scopes` (Set of String)
2523

24+
### Optional
25+
26+
- `applications` (Set of String)
27+
- `audience` (String)
28+
- `credential_lifetime` (Number)
29+
- `issuer_url` (String)
30+
- `jwks_uri` (String)
31+
- `public_key` (String)
32+
- `subject` (String)
33+
2634
### Read-Only
2735

2836
- `id` (String) The ID of this resource.

internal/provider/service_account_resource.go

Lines changed: 116 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,29 @@ func (r *serviceAccountResource) Schema(_ context.Context, _ resource.SchemaRequ
4949
Required: true,
5050
ElementType: types.StringType,
5151
},
52+
// Agent service account
5253
"public_key": schema.StringAttribute{
53-
Required: true,
54+
Optional: true,
5455
},
55-
/*
56-
"privateKey": schema.StringAttribute{
57-
Required: true,
58-
},
59-
*/
6056
"credential_lifetime": schema.Int32Attribute{
61-
Required: true,
57+
Optional: true,
58+
},
59+
// Issuer service account (jwks)
60+
"jwks_uri": schema.StringAttribute{
61+
Optional: true,
62+
},
63+
"issuer_url": schema.StringAttribute{
64+
Optional: true,
65+
},
66+
"audience": schema.StringAttribute{
67+
Optional: true,
68+
},
69+
"subject": schema.StringAttribute{
70+
Optional: true,
71+
},
72+
"applications": schema.SetAttribute{
73+
Optional: true,
74+
ElementType: types.StringType,
6275
},
6376
},
6477
}
@@ -84,13 +97,17 @@ func (r *serviceAccountResource) Configure(_ context.Context, req resource.Confi
8497
}
8598

8699
type serviceAccountResourceModel struct {
87-
ID types.String `tfsdk:"id"`
88-
Name types.String `tfsdk:"name"`
89-
Owner types.String `tfsdk:"owner"`
90-
Scopes []types.String `tfsdk:"scopes"`
91-
PublicKey types.String `tfsdk:"public_key"`
92-
//PrivateKey types.String `tfsdk:"privateKey"`
93-
CredentialLifetime types.Int32 `tfsdk:"credential_lifetime"`
100+
ID types.String `tfsdk:"id"`
101+
Name types.String `tfsdk:"name"`
102+
Owner types.String `tfsdk:"owner"`
103+
Scopes []types.String `tfsdk:"scopes"`
104+
PublicKey types.String `tfsdk:"public_key"`
105+
CredentialLifetime types.Int32 `tfsdk:"credential_lifetime"`
106+
JwksURI types.String `tfsdk:"jwks_uri"`
107+
IssuerURL types.String `tfsdk:"issuer_url"`
108+
Audience types.String `tfsdk:"audience"`
109+
Subject types.String `tfsdk:"subject"`
110+
Applications []types.String `tfsdk:"applications"`
94111
}
95112

96113
func (r *serviceAccountResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
@@ -106,12 +123,48 @@ func (r *serviceAccountResource) Create(ctx context.Context, req resource.Create
106123
}
107124

108125
serviceAccount := tlspc.ServiceAccount{
109-
Name: plan.Name.ValueString(),
110-
Owner: plan.Owner.ValueString(),
111-
Scopes: scopes,
112-
PublicKey: plan.PublicKey.ValueString(),
113-
CredentialLifetime: plan.CredentialLifetime.ValueInt32(),
114-
AuthenticationType: "rsaKey",
126+
Name: plan.Name.ValueString(),
127+
Owner: plan.Owner.ValueString(),
128+
Scopes: scopes,
129+
}
130+
131+
configured := false
132+
// Agent type
133+
if plan.PublicKey.ValueString() != "" || plan.CredentialLifetime.ValueInt32() > 0 {
134+
serviceAccount.PublicKey = plan.PublicKey.ValueString()
135+
serviceAccount.CredentialLifetime = plan.CredentialLifetime.ValueInt32()
136+
serviceAccount.AuthenticationType = "rsaKey"
137+
configured = true
138+
}
139+
140+
// Issuer type
141+
if plan.JwksURI.ValueString() != "" || plan.IssuerURL.ValueString() != "" || plan.Audience.ValueString() != "" || plan.Subject.ValueString() != "" || len(plan.Applications) > 0 {
142+
if serviceAccount.AuthenticationType == "rsaKey" {
143+
resp.Diagnostics.AddError(
144+
"Error creating serviceAccount",
145+
"Could not create serviceAccount, invalid configuration (both public_key and jwks fields present)",
146+
)
147+
return
148+
}
149+
serviceAccount.JwksURI = plan.JwksURI.ValueString()
150+
serviceAccount.IssuerURL = plan.IssuerURL.ValueString()
151+
serviceAccount.Audience = plan.Audience.ValueString()
152+
serviceAccount.Subject = plan.Subject.ValueString()
153+
serviceAccount.AuthenticationType = "rsaKeyFederated"
154+
155+
apps := []string{}
156+
for _, v := range plan.Applications {
157+
apps = append(apps, v.ValueString())
158+
}
159+
serviceAccount.Applications = apps
160+
configured = true
161+
}
162+
if !configured {
163+
resp.Diagnostics.AddError(
164+
"Error creating serviceAccount",
165+
"Could not create serviceAccount, invalid configuration (neither public_key or jwks fields present)",
166+
)
167+
return
115168
}
116169

117170
created, err := r.client.CreateServiceAccount(serviceAccount)
@@ -180,13 +233,49 @@ func (r *serviceAccountResource) Update(ctx context.Context, req resource.Update
180233
}
181234

182235
serviceAccount := tlspc.ServiceAccount{
183-
ID: state.ID.ValueString(),
184-
Name: plan.Name.ValueString(),
185-
Owner: plan.Owner.ValueString(),
186-
Scopes: scopes,
187-
PublicKey: plan.PublicKey.ValueString(),
188-
CredentialLifetime: plan.CredentialLifetime.ValueInt32(),
189-
AuthenticationType: "rsaKey",
236+
ID: state.ID.ValueString(),
237+
Name: plan.Name.ValueString(),
238+
Owner: plan.Owner.ValueString(),
239+
Scopes: scopes,
240+
}
241+
242+
configured := false
243+
// Agent type
244+
if plan.PublicKey.ValueString() != "" || plan.CredentialLifetime.ValueInt32() > 0 {
245+
serviceAccount.PublicKey = plan.PublicKey.ValueString()
246+
serviceAccount.CredentialLifetime = plan.CredentialLifetime.ValueInt32()
247+
serviceAccount.AuthenticationType = "rsaKey"
248+
configured = true
249+
}
250+
251+
// Issuer type
252+
if plan.JwksURI.ValueString() != "" || plan.IssuerURL.ValueString() != "" || plan.Audience.ValueString() != "" || plan.Subject.ValueString() != "" || len(plan.Applications) > 0 {
253+
if serviceAccount.AuthenticationType == "rsaKey" {
254+
resp.Diagnostics.AddError(
255+
"Error creating serviceAccount",
256+
"Could not create serviceAccount, invalid configuration (both public_key and jwks fields present)",
257+
)
258+
return
259+
}
260+
serviceAccount.JwksURI = plan.JwksURI.ValueString()
261+
serviceAccount.IssuerURL = plan.IssuerURL.ValueString()
262+
serviceAccount.Audience = plan.Audience.ValueString()
263+
serviceAccount.Subject = plan.Subject.ValueString()
264+
serviceAccount.AuthenticationType = "rsaKeyFederated"
265+
266+
apps := []string{}
267+
for _, v := range plan.Applications {
268+
apps = append(apps, v.ValueString())
269+
}
270+
serviceAccount.Applications = apps
271+
configured = true
272+
}
273+
if !configured {
274+
resp.Diagnostics.AddError(
275+
"Error creating serviceAccount",
276+
"Could not create serviceAccount, invalid configuration (neither public_key or jwks fields present)",
277+
)
278+
return
190279
}
191280

192281
err := r.client.UpdateServiceAccount(serviceAccount)

internal/tlspc/tlspc.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func (c *Client) GetUser(email string) (*User, error) {
8585
var users Users
8686
err = json.Unmarshal(body, &users)
8787
if err != nil {
88-
return nil, fmt.Errorf("Error decoding response: %s", err)
88+
return nil, fmt.Errorf("Error decoding response: %s", string(body))
8989
}
9090
if len(users.Users) != 1 {
9191
return nil, fmt.Errorf("Unexpected number of users returned (%d)", len(users.Users))
@@ -122,7 +122,7 @@ func (c *Client) CreateTeam(team Team) (*Team, error) {
122122
var created Team
123123
err = json.Unmarshal(respBody, &created)
124124
if err != nil {
125-
return nil, fmt.Errorf("Error decoding response: %s", err)
125+
return nil, fmt.Errorf("Error decoding response: %s", string(respBody))
126126
}
127127
if created.ID == "" {
128128
return nil, fmt.Errorf("Didn't create a team; response was: %s", string(respBody))
@@ -146,7 +146,7 @@ func (c *Client) GetTeam(id string) (*Team, error) {
146146
var team Team
147147
err = json.Unmarshal(respBody, &team)
148148
if err != nil {
149-
return nil, fmt.Errorf("Error decoding response: %s", err)
149+
return nil, fmt.Errorf("Error decoding response: %s", string(respBody))
150150
}
151151
if team.ID == "" {
152152
return nil, fmt.Errorf("Didn't find a Team; response was: %s", string(respBody))
@@ -191,7 +191,7 @@ func (c *Client) UpdateTeam(team Team) (*Team, error) {
191191
var updated Team
192192
err = json.Unmarshal(respBody, &updated)
193193
if err != nil {
194-
return nil, fmt.Errorf("Error decoding response: %s", err)
194+
return nil, fmt.Errorf("Error decoding response: %s", string(respBody))
195195
}
196196
if updated.ID == "" {
197197
return nil, fmt.Errorf("Didn't get a Team ID; response was: %s", string(respBody))
@@ -227,7 +227,7 @@ func (c *Client) AddTeamOwners(id string, owners []string) (*Team, error) {
227227
var updated Team
228228
err = json.Unmarshal(respBody, &updated)
229229
if err != nil {
230-
return nil, fmt.Errorf("Error decoding response: %s", err)
230+
return nil, fmt.Errorf("Error decoding response: %s", string(respBody))
231231
}
232232
if updated.ID == "" {
233233
return nil, fmt.Errorf("Didn't get a Team ID; response was: %s", string(respBody))
@@ -259,7 +259,7 @@ func (c *Client) RemoveTeamOwners(id string, owners []string) (*Team, error) {
259259
var updated Team
260260
err = json.Unmarshal(respBody, &updated)
261261
if err != nil {
262-
return nil, fmt.Errorf("Error decoding response: %s", err)
262+
return nil, fmt.Errorf("Error decoding response: %s", string(respBody))
263263
}
264264
if updated.ID == "" {
265265
return nil, fmt.Errorf("Didn't get a Team ID; response was: %s", string(respBody))
@@ -291,11 +291,16 @@ type ServiceAccount struct {
291291
Name string `json:"name"`
292292
Owner string `json:"owner"`
293293
Scopes []string `json:"scopes"`
294-
CredentialLifetime int32 `json:"credentialLifetime"`
295-
PublicKey string `json:"publicKey"`
296-
AuthenticationType string `json:"authenticationType"`
297-
OciAccountName string `json:"ociAccountName"`
298-
OciRegistryToken string `json:"ociRegistryToken"`
294+
CredentialLifetime int32 `json:"credentialLifetime,omitempty"`
295+
PublicKey string `json:"publicKey,omitempty"`
296+
AuthenticationType string `json:"authenticationType,omitempty"`
297+
OciAccountName string `json:"ociAccountName,omitempty"`
298+
OciRegistryToken string `json:"ociRegistryToken,omitempty"`
299+
JwksURI string `json:"jwksURI,omitempty"`
300+
IssuerURL string `json:"issuerURL,omitempty"`
301+
Audience string `json:"audience,omitempty"`
302+
Subject string `json:"subject,omitempty"`
303+
Applications []string `json:"applications,omitempty"`
299304
}
300305

301306
func (c *Client) CreateServiceAccount(sa ServiceAccount) (*ServiceAccount, error) {
@@ -318,7 +323,7 @@ func (c *Client) CreateServiceAccount(sa ServiceAccount) (*ServiceAccount, error
318323
var created ServiceAccount
319324
err = json.Unmarshal(respBody, &created)
320325
if err != nil {
321-
return nil, fmt.Errorf("Error decoding response: %s", err)
326+
return nil, fmt.Errorf("Error decoding response: %s", string(respBody))
322327
}
323328
if created.ID == "" {
324329
return nil, fmt.Errorf("Didn't create a service account; response was: %s", string(respBody))
@@ -342,7 +347,7 @@ func (c *Client) GetServiceAccount(id string) (*ServiceAccount, error) {
342347
var sa ServiceAccount
343348
err = json.Unmarshal(respBody, &sa)
344349
if err != nil {
345-
return nil, fmt.Errorf("Error decoding response: %s", err)
350+
return nil, fmt.Errorf("Error decoding response: %s", string(respBody))
346351
}
347352
if sa.ID == "" {
348353
return nil, fmt.Errorf("Didn't find a Service Account; response was: %s", string(respBody))

0 commit comments

Comments
 (0)