@@ -9,15 +9,19 @@ import (
99
1010 tfe "github.com/hashicorp/go-tfe"
1111 "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
12+ "github.com/hashicorp/terraform-plugin-framework/path"
1213 "github.com/hashicorp/terraform-plugin-framework/resource"
1314 "github.com/hashicorp/terraform-plugin-framework/resource/schema"
1415 "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
1516 "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
17+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
1618 "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
1719 "github.com/hashicorp/terraform-plugin-framework/schema/validator"
1820 "github.com/hashicorp/terraform-plugin-framework/types"
1921 "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
2022 "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"
2125)
2226
2327const (
@@ -30,13 +34,38 @@ const (
3034 samlDefaultSSOAPITokenSessionTimeoutSeconds int64 = 1209600 // 14 days
3135)
3236
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+
3362// resourceTFESAMLSettings implements the tfe_saml_settings resource type
3463type resourceTFESAMLSettings struct {
3564 client * tfe.Client
3665}
3766
3867// 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 {
4069 m := modelTFESAMLSettings {
4170 ID : types .StringValue (v .ID ),
4271 Enabled : types .BoolValue (v .Enabled ),
@@ -60,9 +89,16 @@ func modelFromTFEAdminSAMLSettings(v tfe.AdminSAMLSetting, privateKey types.Stri
6089 SignatureSigningMethod : types .StringValue (v .SignatureSigningMethod ),
6190 SignatureDigestMethod : types .StringValue (v .SignatureDigestMethod ),
6291 }
92+
6393 if len (privateKey .String ()) > 0 {
6494 m .PrivateKey = privateKey
6595 }
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+
66102 return m
67103}
68104
@@ -187,6 +223,21 @@ func (r *resourceTFESAMLSettings) Schema(ctx context.Context, req resource.Schem
187223 Optional : true ,
188224 Computed : true ,
189225 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+ },
190241 },
191242 "signature_signing_method" : schema.StringAttribute {
192243 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
225276 return
226277 }
227278
279+ tflog .Debug (ctx , "Reading SAML Settings" )
280+
228281 samlSettings , err := r .client .Admin .Settings .SAML .Read (ctx )
229282 if err != nil {
230283 resp .Diagnostics .AddError ("Error reading SAML Settings" , "Could not read SAML Settings, unexpected error: " + err .Error ())
231284 return
232285 }
233286
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 )
235295 diags = resp .State .Set (ctx , & result )
236296 resp .Diagnostics .Append (diags ... )
237297}
@@ -245,14 +305,28 @@ func (r *resourceTFESAMLSettings) Create(ctx context.Context, req resource.Creat
245305 return
246306 }
247307
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+
248319 tflog .Debug (ctx , "Create SAML Settings" )
249320 samlSettings , err := r .updateSAMLSettings (ctx , m )
250321 if err != nil {
251322 resp .Diagnostics .AddError ("Error creating SAML Settings" , "Could not set SAML Settings, unexpected error: " + err .Error ())
252323 return
253324 }
254325
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 )... )
256330 diags = resp .State .Set (ctx , & result )
257331 resp .Diagnostics .Append (diags ... )
258332}
@@ -266,14 +340,33 @@ func (r *resourceTFESAMLSettings) Update(ctx context.Context, req resource.Updat
266340 return
267341 }
268342
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+
269354 tflog .Debug (ctx , "Update SAML Settings" )
270355 samlSettings , err := r .updateSAMLSettings (ctx , m )
271356 if err != nil {
272357 resp .Diagnostics .AddError ("Error updating SAML Settings" , "Could not set SAML Settings, unexpected error: " + err .Error ())
273358 return
274359 }
275360
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
277370 diags = resp .State .Set (ctx , & result )
278371 resp .Diagnostics .Append (diags ... )
279372}
@@ -321,7 +414,7 @@ func (r *resourceTFESAMLSettings) ImportState(ctx context.Context, req resource.
321414 return
322415 }
323416
324- result := modelFromTFEAdminSAMLSettings (* samlSettings , types .StringValue ("" ))
417+ result := modelFromTFEAdminSAMLSettings (* samlSettings , types .StringValue ("" ), false )
325418 diags := resp .State .Set (ctx , & result )
326419 resp .Diagnostics .Append (diags ... )
327420}
@@ -363,3 +456,7 @@ func (r *resourceTFESAMLSettings) updateSAMLSettings(ctx context.Context, m mode
363456 }
364457 return s , nil
365458}
459+
460+ func (r * resourceTFESAMLSettings ) writeOnlyValueStore (private helpers.PrivateState ) * helpers.WriteOnlyValueStore {
461+ return helpers .NewWriteOnlyValueStore (private , "private_key_wo" )
462+ }
0 commit comments