@@ -9,15 +9,19 @@ import (
9
9
10
10
tfe "github.com/hashicorp/go-tfe"
11
11
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
12
+ "github.com/hashicorp/terraform-plugin-framework/path"
12
13
"github.com/hashicorp/terraform-plugin-framework/resource"
13
14
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
14
15
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
15
16
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
17
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
16
18
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
17
19
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
18
20
"github.com/hashicorp/terraform-plugin-framework/types"
19
21
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
20
22
"github.com/hashicorp/terraform-plugin-log/tflog"
23
+ "github.com/hashicorp/terraform-provider-tfe/internal/provider/helpers"
24
+ "github.com/hashicorp/terraform-provider-tfe/internal/provider/planmodifiers"
21
25
)
22
26
23
27
const (
@@ -30,13 +34,38 @@ const (
30
34
samlDefaultSSOAPITokenSessionTimeoutSeconds int64 = 1209600 // 14 days
31
35
)
32
36
37
+ type modelTFESAMLSettings struct {
38
+ ID types.String `tfsdk:"id"`
39
+ Enabled types.Bool `tfsdk:"enabled"`
40
+ Debug types.Bool `tfsdk:"debug"`
41
+ TeamManagementEnabled types.Bool `tfsdk:"team_management_enabled"`
42
+ AuthnRequestsSigned types.Bool `tfsdk:"authn_requests_signed"`
43
+ WantAssertionsSigned types.Bool `tfsdk:"want_assertions_signed"`
44
+ IDPCert types.String `tfsdk:"idp_cert"`
45
+ OldIDPCert types.String `tfsdk:"old_idp_cert"`
46
+ SLOEndpointURL types.String `tfsdk:"slo_endpoint_url"`
47
+ SSOEndpointURL types.String `tfsdk:"sso_endpoint_url"`
48
+ AttrUsername types.String `tfsdk:"attr_username"`
49
+ AttrGroups types.String `tfsdk:"attr_groups"`
50
+ AttrSiteAdmin types.String `tfsdk:"attr_site_admin"`
51
+ SiteAdminRole types.String `tfsdk:"site_admin_role"`
52
+ SSOAPITokenSessionTimeout types.Int64 `tfsdk:"sso_api_token_session_timeout"`
53
+ ACSConsumerURL types.String `tfsdk:"acs_consumer_url"`
54
+ MetadataURL types.String `tfsdk:"metadata_url"`
55
+ Certificate types.String `tfsdk:"certificate"`
56
+ PrivateKey types.String `tfsdk:"private_key"`
57
+ PrivateKeyWO types.String `tfsdk:"private_key_wo"`
58
+ SignatureSigningMethod types.String `tfsdk:"signature_signing_method"`
59
+ SignatureDigestMethod types.String `tfsdk:"signature_digest_method"`
60
+ }
61
+
33
62
// resourceTFESAMLSettings implements the tfe_saml_settings resource type
34
63
type resourceTFESAMLSettings struct {
35
64
client * tfe.Client
36
65
}
37
66
38
67
// modelFromTFEAdminSAMLSettings builds a modelTFESAMLSettings struct from a tfe.AdminSAMLSetting value
39
- func modelFromTFEAdminSAMLSettings (v tfe.AdminSAMLSetting , privateKey types.String ) modelTFESAMLSettings {
68
+ func modelFromTFEAdminSAMLSettings (v tfe.AdminSAMLSetting , privateKey types.String , isWriteOnly bool ) modelTFESAMLSettings {
40
69
m := modelTFESAMLSettings {
41
70
ID : types .StringValue (v .ID ),
42
71
Enabled : types .BoolValue (v .Enabled ),
@@ -60,9 +89,16 @@ func modelFromTFEAdminSAMLSettings(v tfe.AdminSAMLSetting, privateKey types.Stri
60
89
SignatureSigningMethod : types .StringValue (v .SignatureSigningMethod ),
61
90
SignatureDigestMethod : types .StringValue (v .SignatureDigestMethod ),
62
91
}
92
+
63
93
if len (privateKey .String ()) > 0 {
64
94
m .PrivateKey = privateKey
65
95
}
96
+
97
+ // Don't retrieve values if write-only is being used. Unset the private key field before updating the state.
98
+ if isWriteOnly {
99
+ m .PrivateKey = types .StringValue ("" )
100
+ }
101
+
66
102
return m
67
103
}
68
104
@@ -187,6 +223,21 @@ func (r *resourceTFESAMLSettings) Schema(ctx context.Context, req resource.Schem
187
223
Optional : true ,
188
224
Computed : true ,
189
225
Sensitive : true ,
226
+ Validators : []validator.String {
227
+ stringvalidator .ConflictsWith (path .MatchRoot ("private_key_wo" )),
228
+ },
229
+ },
230
+ "private_key_wo" : schema.StringAttribute {
231
+ Description : "The private key in write-only mode used for request and assertion signing" ,
232
+ Optional : true ,
233
+ Sensitive : true ,
234
+ WriteOnly : true ,
235
+ Validators : []validator.String {
236
+ stringvalidator .ConflictsWith (path .MatchRoot ("private_key" )),
237
+ },
238
+ PlanModifiers : []planmodifier.String {
239
+ planmodifiers .NewReplaceForWriteOnlyStringValue ("private_key_wo" ),
240
+ },
190
241
},
191
242
"signature_signing_method" : schema.StringAttribute {
192
243
Description : fmt .Sprintf ("Signature Signing Method. Must be either `%s` or `%s`. Defaults to `%s`" , samlSignatureMethodSHA1 , samlSignatureMethodSHA256 , samlSignatureMethodSHA256 ),
@@ -225,13 +276,22 @@ func (r *resourceTFESAMLSettings) Read(ctx context.Context, req resource.ReadReq
225
276
return
226
277
}
227
278
279
+ tflog .Debug (ctx , "Reading SAML Settings" )
280
+
228
281
samlSettings , err := r .client .Admin .Settings .SAML .Read (ctx )
229
282
if err != nil {
230
283
resp .Diagnostics .AddError ("Error reading SAML Settings" , "Could not read SAML Settings, unexpected error: " + err .Error ())
231
284
return
232
285
}
233
286
234
- result := modelFromTFEAdminSAMLSettings (* samlSettings , m .PrivateKey )
287
+ isWriteOnly , diags := r .writeOnlyValueStore (resp .Private ).PriorValueExists (ctx )
288
+ resp .Diagnostics .Append (diags ... )
289
+ if diags .HasError () {
290
+ return
291
+ }
292
+
293
+ // update state
294
+ result := modelFromTFEAdminSAMLSettings (* samlSettings , m .PrivateKey , isWriteOnly )
235
295
diags = resp .State .Set (ctx , & result )
236
296
resp .Diagnostics .Append (diags ... )
237
297
}
@@ -245,14 +305,28 @@ func (r *resourceTFESAMLSettings) Create(ctx context.Context, req resource.Creat
245
305
return
246
306
}
247
307
308
+ var config modelTFESAMLSettings
309
+ diags = req .Config .Get (ctx , & config )
310
+ resp .Diagnostics .Append (diags ... )
311
+ if resp .Diagnostics .HasError () {
312
+ return
313
+ }
314
+
315
+ if ! config .PrivateKeyWO .IsNull () {
316
+ m .PrivateKey = config .PrivateKeyWO
317
+ }
318
+
248
319
tflog .Debug (ctx , "Create SAML Settings" )
249
320
samlSettings , err := r .updateSAMLSettings (ctx , m )
250
321
if err != nil {
251
322
resp .Diagnostics .AddError ("Error creating SAML Settings" , "Could not set SAML Settings, unexpected error: " + err .Error ())
252
323
return
253
324
}
254
325
255
- result := modelFromTFEAdminSAMLSettings (* samlSettings , m .PrivateKey )
326
+ result := modelFromTFEAdminSAMLSettings (* samlSettings , m .PrivateKey , ! config .PrivateKeyWO .IsNull ())
327
+ // Store the hashed write-only value in the private state
328
+ store := r .writeOnlyValueStore (resp .Private )
329
+ resp .Diagnostics .Append (store .SetPriorValue (ctx , config .PrivateKeyWO )... )
256
330
diags = resp .State .Set (ctx , & result )
257
331
resp .Diagnostics .Append (diags ... )
258
332
}
@@ -266,14 +340,33 @@ func (r *resourceTFESAMLSettings) Update(ctx context.Context, req resource.Updat
266
340
return
267
341
}
268
342
343
+ var config modelTFESAMLSettings
344
+ diags = req .Config .Get (ctx , & config )
345
+ resp .Diagnostics .Append (diags ... )
346
+ if resp .Diagnostics .HasError () {
347
+ return
348
+ }
349
+
350
+ if ! config .PrivateKeyWO .IsNull () {
351
+ m .PrivateKey = config .PrivateKeyWO
352
+ }
353
+
269
354
tflog .Debug (ctx , "Update SAML Settings" )
270
355
samlSettings , err := r .updateSAMLSettings (ctx , m )
271
356
if err != nil {
272
357
resp .Diagnostics .AddError ("Error updating SAML Settings" , "Could not set SAML Settings, unexpected error: " + err .Error ())
273
358
return
274
359
}
275
360
276
- result := modelFromTFEAdminSAMLSettings (* samlSettings , m .PrivateKey )
361
+ // Store the hashed write-only value in the private state
362
+ store := r .writeOnlyValueStore (resp .Private )
363
+ resp .Diagnostics .Append (store .SetPriorValue (ctx , config .PrivateKeyWO )... )
364
+ if resp .Diagnostics .HasError () {
365
+ return
366
+ }
367
+
368
+ result := modelFromTFEAdminSAMLSettings (* samlSettings , m .PrivateKey , ! config .PrivateKeyWO .IsNull ())
369
+ // Save data into Terraform state
277
370
diags = resp .State .Set (ctx , & result )
278
371
resp .Diagnostics .Append (diags ... )
279
372
}
@@ -321,7 +414,7 @@ func (r *resourceTFESAMLSettings) ImportState(ctx context.Context, req resource.
321
414
return
322
415
}
323
416
324
- result := modelFromTFEAdminSAMLSettings (* samlSettings , types .StringValue ("" ))
417
+ result := modelFromTFEAdminSAMLSettings (* samlSettings , types .StringValue ("" ), false )
325
418
diags := resp .State .Set (ctx , & result )
326
419
resp .Diagnostics .Append (diags ... )
327
420
}
@@ -363,3 +456,7 @@ func (r *resourceTFESAMLSettings) updateSAMLSettings(ctx context.Context, m mode
363
456
}
364
457
return s , nil
365
458
}
459
+
460
+ func (r * resourceTFESAMLSettings ) writeOnlyValueStore (private helpers.PrivateState ) * helpers.WriteOnlyValueStore {
461
+ return helpers .NewWriteOnlyValueStore (private , "private_key_wo" )
462
+ }
0 commit comments