diff --git a/.vscode/launch.json b/.vscode/launch.json index 1326815cd..5e4b431f6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,10 @@ "env": {}, "args": [ "-debug", + ], + "dlvFlags": [ + "--check-go-version=false" ] } ] -} +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a57ca291..6e1ecdbf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,10 @@ * `location` property in `resourceLink` object types for all Resources and DataSources. This property will be removed in a future release. ([#195](https://github.com/pingidentity/terraform-provider-pingfederate/pull/195)) ### Resources +* **New Resource:** `pingfederate_authentication_selector ` ([#199](https://github.com/pingidentity/terraform-provider-pingfederate/pull/199)) +* **New Resource:** `pingfederate_incoming_proxy_settings` ([#190](https://github.com/pingidentity/terraform-provider-pingfederate/pull/190)) * **New Resource:** `pingfederate_notification_publishers_settings` ([#187](https://github.com/pingidentity/terraform-provider-pingfederate/pull/187)) * **New Resource:** `pingfederate_oauth_access_token_mapping` ([#195](https://github.com/pingidentity/terraform-provider-pingfederate/pull/195)) -* **New Resource:** `pingfederate_incoming_proxy_settings` ([#190](https://github.com/pingidentity/terraform-provider-pingfederate/pull/190)) * **New Resource:** `pingfederate_open_id_connect_settings` ([#196](https://github.com/pingidentity/terraform-provider-pingfederate/pull/196)) # v0.6.0 February 9th, 2024 diff --git a/docs/resources/authentication_selector.md b/docs/resources/authentication_selector.md new file mode 100644 index 000000000..0f2c45a04 --- /dev/null +++ b/docs/resources/authentication_selector.md @@ -0,0 +1,210 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "pingfederate_authentication_selector Resource - terraform-provider-pingfederate" +subcategory: "" +description: |- + Manages Authentication Selectors +--- + +# pingfederate_authentication_selector (Resource) + +Manages Authentication Selectors + +## Example Usage + +```terraform +resource "pingfederate_authentication_selector" "samlAuthnContextExample" { + selector_id = "samlAuthnContextExample" + name = "samlAuthnContextExample" + plugin_descriptor_ref = { + id = "com.pingidentity.pf.selectors.saml.SamlAuthnContextAdapterSelector" + } + configuration = { + tables = [] + fields = [ + { + name = "Add or Update AuthN Context Attribute" + value = "true" + }, + { + name = "Override AuthN Context for Flow" + value = "true" + }, + { + name = "Enable 'No Match' Result Value" + value = "false" + }, + { + name = "Enable 'Not in Request' Result Value" + value = "false" + } + ] + } + attribute_contract = { + extended_attributes = [ + { + name = "result_value2" + } + ] + } +} +``` + + +## Schema + +### Required + +- `configuration` (Attributes) Plugin instance configuration. (see [below for nested schema](#nestedatt--configuration)) +- `name` (String) The plugin instance name. The name can be modified once the instance is created. +- `plugin_descriptor_ref` (Attributes) Reference to the plugin descriptor for this instance. The plugin descriptor cannot be modified once the instance is created. (see [below for nested schema](#nestedatt--plugin_descriptor_ref)) +- `selector_id` (String) The ID of the plugin instance. The ID cannot be modified once the instance is created. + +### Optional + +- `attribute_contract` (Attributes) The list of attributes that the Authentication Selector provides. (see [below for nested schema](#nestedatt--attribute_contract)) + +### Read-Only + +- `id` (String) The ID of this resource. + + +### Nested Schema for `configuration` + +Optional: + +- `fields` (Attributes List) List of configuration fields. (see [below for nested schema](#nestedatt--configuration--fields)) +- `tables` (Attributes List) List of configuration tables. (see [below for nested schema](#nestedatt--configuration--tables)) + +Read-Only: + +- `fields_all` (Attributes List) List of configuration fields. This attribute will include any values set by default by PingFederate. (see [below for nested schema](#nestedatt--configuration--fields_all)) +- `tables_all` (Attributes List) List of configuration tables. This attribute will include any values set by default by PingFederate. (see [below for nested schema](#nestedatt--configuration--tables_all)) + + +### Nested Schema for `configuration.fields` + +Required: + +- `name` (String) The name of the configuration field. +- `value` (String) The value for the configuration field. For encrypted or hashed fields, GETs will not return this attribute. To update an encrypted or hashed field, specify the new value in this attribute. + +Optional: + +- `inherited` (Boolean, Deprecated) Whether this field is inherited from its parent instance. If true, the value/encrypted value properties become read-only. The default value is false. + + + +### Nested Schema for `configuration.tables` + +Required: + +- `name` (String) The name of the table. + +Optional: + +- `inherited` (Boolean, Deprecated) Whether this table is inherited from its parent instance. If true, the rows become read-only. The default value is false. +- `rows` (Attributes List) List of table rows. (see [below for nested schema](#nestedatt--configuration--tables--rows)) + + +### Nested Schema for `configuration.tables.rows` + +Optional: + +- `default_row` (Boolean) Whether this row is the default. +- `fields` (Attributes List) The configuration fields in the row. (see [below for nested schema](#nestedatt--configuration--tables--rows--fields)) + + +### Nested Schema for `configuration.tables.rows.fields` + +Required: + +- `name` (String) The name of the configuration field. +- `value` (String) The value for the configuration field. For encrypted or hashed fields, GETs will not return this attribute. To update an encrypted or hashed field, specify the new value in this attribute. + +Optional: + +- `inherited` (Boolean, Deprecated) Whether this field is inherited from its parent instance. If true, the value/encrypted value properties become read-only. The default value is false. + + + + + +### Nested Schema for `configuration.fields_all` + +Required: + +- `name` (String) The name of the configuration field. +- `value` (String) The value for the configuration field. For encrypted or hashed fields, GETs will not return this attribute. To update an encrypted or hashed field, specify the new value in this attribute. + +Optional: + +- `inherited` (Boolean, Deprecated) Whether this field is inherited from its parent instance. If true, the value/encrypted value properties become read-only. The default value is false. + + + +### Nested Schema for `configuration.tables_all` + +Required: + +- `name` (String) The name of the table. + +Optional: + +- `inherited` (Boolean, Deprecated) Whether this table is inherited from its parent instance. If true, the rows become read-only. The default value is false. +- `rows` (Attributes List) List of table rows. (see [below for nested schema](#nestedatt--configuration--tables_all--rows)) + + +### Nested Schema for `configuration.tables_all.rows` + +Optional: + +- `default_row` (Boolean) Whether this row is the default. +- `fields` (Attributes List) The configuration fields in the row. (see [below for nested schema](#nestedatt--configuration--tables_all--rows--fields)) + + +### Nested Schema for `configuration.tables_all.rows.fields` + +Required: + +- `name` (String) The name of the configuration field. +- `value` (String) The value for the configuration field. For encrypted or hashed fields, GETs will not return this attribute. To update an encrypted or hashed field, specify the new value in this attribute. + +Optional: + +- `inherited` (Boolean, Deprecated) Whether this field is inherited from its parent instance. If true, the value/encrypted value properties become read-only. The default value is false. + + + + + + +### Nested Schema for `plugin_descriptor_ref` + +Required: + +- `id` (String) The ID of the resource. + + + +### Nested Schema for `attribute_contract` + +Optional: + +- `extended_attributes` (Attributes List) A list of additional attributes that can be returned by the Authentication Selector. The extended attributes are only used if the Authentication Selector supports them. (see [below for nested schema](#nestedatt--attribute_contract--extended_attributes)) + + +### Nested Schema for `attribute_contract.extended_attributes` + +Required: + +- `name` (String) An attribute for the Authentication Selector attribute contract. + +## Import + +Import is supported using the following syntax: + +```shell +# "authenticationSelectorId" should be the id of the Authentication Selector to be imported +terraform import pingfederate_authentication_selector.authenticationSelector authenticationSelectorId +``` diff --git a/examples/resources/pingfederate_authentication_selector/import.sh b/examples/resources/pingfederate_authentication_selector/import.sh new file mode 100644 index 000000000..08afc25ca --- /dev/null +++ b/examples/resources/pingfederate_authentication_selector/import.sh @@ -0,0 +1,2 @@ +# "authenticationSelectorId" should be the id of the Authentication Selector to be imported +terraform import pingfederate_authentication_selector.authenticationSelector authenticationSelectorId \ No newline at end of file diff --git a/examples/resources/pingfederate_authentication_selector/resource.tf b/examples/resources/pingfederate_authentication_selector/resource.tf new file mode 100644 index 000000000..947731b93 --- /dev/null +++ b/examples/resources/pingfederate_authentication_selector/resource.tf @@ -0,0 +1,36 @@ +resource "pingfederate_authentication_selector" "samlAuthnContextExample" { + selector_id = "samlAuthnContextExample" + name = "samlAuthnContextExample" + plugin_descriptor_ref = { + id = "com.pingidentity.pf.selectors.saml.SamlAuthnContextAdapterSelector" + } + configuration = { + tables = [] + fields = [ + { + name = "Add or Update AuthN Context Attribute" + value = "true" + }, + { + name = "Override AuthN Context for Flow" + value = "true" + }, + { + name = "Enable 'No Match' Result Value" + value = "false" + }, + { + name = "Enable 'Not in Request' Result Value" + value = "false" + } + ] + } + attribute_contract = { + extended_attributes = [ + { + name = "result_value2" + } + ] + } +} + diff --git a/internal/acctest/config/authenticationselector/authentication_selector_test.go b/internal/acctest/config/authenticationselector/authentication_selector_test.go new file mode 100644 index 000000000..484089d29 --- /dev/null +++ b/internal/acctest/config/authenticationselector/authentication_selector_test.go @@ -0,0 +1,238 @@ +package authenticationselector_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + client "github.com/pingidentity/pingfederate-go-client/v1200/configurationapi" + "github.com/pingidentity/terraform-provider-pingfederate/internal/acctest" + "github.com/pingidentity/terraform-provider-pingfederate/internal/acctest/common/pointers" + "github.com/pingidentity/terraform-provider-pingfederate/internal/provider" +) + +const authenticationSelectorsId = "selectorTest" + +// Attributes to test with. Add optional properties to test here if desired. +type authenticationSelectorsResourceModel struct { + attributeContract *client.AuthenticationSelectorAttributeContract + pluginDescriptorRef client.ResourceLink + addOrUpdateAuthNContextAttribute string + overrideAuthNContextForFlow string + enableNoMatchResultValue string + enableNotInRequestResultValue string +} + +func TestAccAuthenticationSelector(t *testing.T) { + resourceName := "myAuthenticationSelector" + + initialResourceModel := authenticationSelectorsResourceModel{ + pluginDescriptorRef: client.ResourceLink{ + Id: "com.pingidentity.pf.selectors.saml.SamlAuthnContextAdapterSelector", + }, + addOrUpdateAuthNContextAttribute: "true", + overrideAuthNContextForFlow: "true", + enableNoMatchResultValue: "false", + enableNotInRequestResultValue: "false", + attributeContract: &client.AuthenticationSelectorAttributeContract{ + ExtendedAttributes: []client.AuthenticationSelectorAttribute{ + { + Name: "result_value", + }, + }, + }, + } + + updatedResourceModel := authenticationSelectorsResourceModel{ + pluginDescriptorRef: client.ResourceLink{ + Id: "com.pingidentity.pf.selectors.saml.SamlAuthnContextAdapterSelector", + }, + addOrUpdateAuthNContextAttribute: "false", + overrideAuthNContextForFlow: "false", + enableNoMatchResultValue: "true", + enableNotInRequestResultValue: "true", + attributeContract: &client.AuthenticationSelectorAttributeContract{ + ExtendedAttributes: []client.AuthenticationSelectorAttribute{ + { + Name: "result_value2", + }, + }, + }, + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.ConfigurationPreCheck(t) }, + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "pingfederate": providerserver.NewProtocol6WithError(provider.NewTestProvider()), + }, + CheckDestroy: testAccCheckAuthenticationSelectorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAuthenticationSelector(resourceName, initialResourceModel), + Check: testAccCheckExpectedAuthenticationSelectorAttributes(initialResourceModel), + }, + { + // Test updating some fields + Config: testAccAuthenticationSelector(resourceName, updatedResourceModel), + Check: testAccCheckExpectedAuthenticationSelectorAttributes(updatedResourceModel), + }, + { + // Test importing the resource + Config: testAccAuthenticationSelector(resourceName, updatedResourceModel), + ResourceName: "pingfederate_authentication_selector." + resourceName, + ImportStateId: authenticationSelectorsId, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"configuration.fields"}, + }, + { + Config: testAccAuthenticationSelector(resourceName, initialResourceModel), + Check: testAccCheckExpectedAuthenticationSelectorAttributes(initialResourceModel), + }, + { + PreConfig: func() { + testClient := acctest.TestClient() + ctx := acctest.TestBasicAuthContext() + _, err := testClient.AuthenticationSelectorsAPI.DeleteAuthenticationSelector(ctx, authenticationSelectorsId).Execute() + if err != nil { + t.Fatalf("Failed to delete config: %v", err) + } + }, + RefreshState: true, + ExpectNonEmptyPlan: true, + }, + { + Config: testAccAuthenticationSelector(resourceName, initialResourceModel), + Check: testAccCheckExpectedAuthenticationSelectorAttributes(initialResourceModel), + }, + }, + }) +} + +func testAccAuthenticationSelector(resourceName string, resourceModel authenticationSelectorsResourceModel) string { + return fmt.Sprintf(` +resource "pingfederate_authentication_selector" "%[1]s" { + selector_id = "%[2]s" + name = "%[2]s" + plugin_descriptor_ref = { + id = "%[3]s" + } + configuration = { + tables = [] + fields = [ + { + name = "Add or Update AuthN Context Attribute" + value = "%[4]s" + }, + { + name = "Override AuthN Context for Flow" + value = "%[5]s" + }, + { + name = "Enable 'No Match' Result Value" + value = "%[6]s" + }, + { + name = "Enable 'Not in Request' Result Value" + value = "%[7]s" + } + ] + } + attribute_contract = { + extended_attributes = [ + { + name = "%s" + } + ] + } +}`, resourceName, + authenticationSelectorsId, + resourceModel.pluginDescriptorRef.Id, + resourceModel.addOrUpdateAuthNContextAttribute, + resourceModel.overrideAuthNContextForFlow, + resourceModel.enableNoMatchResultValue, + resourceModel.enableNotInRequestResultValue, + resourceModel.attributeContract.ExtendedAttributes[0].Name, + ) +} + +// Test that the expected attributes are set on the PingFederate server +func testAccCheckExpectedAuthenticationSelectorAttributes(config authenticationSelectorsResourceModel) resource.TestCheckFunc { + return func(s *terraform.State) error { + resourceType := "AuthenticationSelector" + testClient := acctest.TestClient() + ctx := acctest.TestBasicAuthContext() + response, _, err := testClient.AuthenticationSelectorsAPI.GetAuthenticationSelector(ctx, authenticationSelectorsId).Execute() + + if err != nil { + return err + } + + // Verify that attributes have expected values + err = acctest.TestAttributesMatchString(resourceType, pointers.String(authenticationSelectorsId), "selector_id", authenticationSelectorsId, response.Id) + if err != nil { + return err + } + + err = acctest.TestAttributesMatchString(resourceType, pointers.String(authenticationSelectorsId), "name", authenticationSelectorsId, response.Name) + if err != nil { + return err + } + + configFields := response.Configuration.Fields + for _, field := range configFields { + + if field.Name == "Add or Update AuthN Context Attribute" { + err = acctest.TestAttributesMatchString(resourceType, pointers.String(authenticationSelectorsId), "value", config.addOrUpdateAuthNContextAttribute, *field.Value) + if err != nil { + return err + } + } + + if field.Name == "Override AuthN Context For Flow" { + err = acctest.TestAttributesMatchString(resourceType, pointers.String(authenticationSelectorsId), "value", config.overrideAuthNContextForFlow, *field.Value) + if err != nil { + return err + } + } + + if field.Name == "Enable No Match Result Value" { + err = acctest.TestAttributesMatchString(resourceType, pointers.String(authenticationSelectorsId), "value", config.enableNoMatchResultValue, *field.Value) + if err != nil { + return err + } + } + + if field.Name == "Enable Not In Request Result Value" { + err = acctest.TestAttributesMatchString(resourceType, pointers.String(authenticationSelectorsId), "value", config.enableNotInRequestResultValue, *field.Value) + if err != nil { + return err + } + } + + } + + if config.attributeContract != nil { + err = acctest.TestAttributesMatchString(resourceType, pointers.String(authenticationSelectorsId), "name", config.attributeContract.ExtendedAttributes[0].Name, response.AttributeContract.ExtendedAttributes[0].Name) + if err != nil { + return err + } + } + + return nil + } +} + +// Test that any objects created by the test are destroyed +func testAccCheckAuthenticationSelectorDestroy(s *terraform.State) error { + testClient := acctest.TestClient() + ctx := acctest.TestBasicAuthContext() + _, err := testClient.AuthenticationSelectorsAPI.DeleteAuthenticationSelector(ctx, authenticationSelectorsId).Execute() + if err == nil { + return acctest.ExpectedDestroyError("AuthenticationSelector", authenticationSelectorsId) + } + return nil +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index efda7c93a..4b12317c6 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -29,6 +29,7 @@ import ( authenticationpoliciesfragments "github.com/pingidentity/terraform-provider-pingfederate/internal/resource/config/authenticationpolicies/fragments" authenticationpoliciessettings "github.com/pingidentity/terraform-provider-pingfederate/internal/resource/config/authenticationpolicies/settings" "github.com/pingidentity/terraform-provider-pingfederate/internal/resource/config/authenticationpolicycontract" + "github.com/pingidentity/terraform-provider-pingfederate/internal/resource/config/authenticationselector" certificate "github.com/pingidentity/terraform-provider-pingfederate/internal/resource/config/certificate/ca" datastore "github.com/pingidentity/terraform-provider-pingfederate/internal/resource/config/datastore" extendedproperties "github.com/pingidentity/terraform-provider-pingfederate/internal/resource/config/extendedproperties" @@ -709,6 +710,7 @@ func (p *pingfederateProvider) Resources(_ context.Context) []func() resource.Re authenticationpoliciesfragments.AuthenticationPoliciesFragmentResource, authenticationpoliciessettings.AuthenticationPoliciesSettingsResource, authenticationpolicycontract.AuthenticationPolicyContractResource, + authenticationselector.AuthenticationSelectorsResource, certificate.CertificateCAResource, extendedproperties.ExtendedPropertiesResource, idpadapter.IdpAdapterResource, diff --git a/internal/resource/config/authenticationselector/authentication_selector_resource.go b/internal/resource/config/authenticationselector/authentication_selector_resource.go new file mode 100644 index 000000000..9921c8327 --- /dev/null +++ b/internal/resource/config/authenticationselector/authentication_selector_resource.go @@ -0,0 +1,324 @@ +package authenticationselector + +import ( + "context" + "encoding/json" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + client "github.com/pingidentity/pingfederate-go-client/v1200/configurationapi" + internaljson "github.com/pingidentity/terraform-provider-pingfederate/internal/json" + "github.com/pingidentity/terraform-provider-pingfederate/internal/resource/common/id" + "github.com/pingidentity/terraform-provider-pingfederate/internal/resource/common/pluginconfiguration" + "github.com/pingidentity/terraform-provider-pingfederate/internal/resource/common/resourcelink" + "github.com/pingidentity/terraform-provider-pingfederate/internal/resource/config" + internaltypes "github.com/pingidentity/terraform-provider-pingfederate/internal/types" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &authenticationSelectorResource{} + _ resource.ResourceWithConfigure = &authenticationSelectorResource{} + _ resource.ResourceWithImportState = &authenticationSelectorResource{} + + attributeContractAttrType = map[string]attr.Type{ + "extended_attributes": types.ListType{ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "name": types.StringType, + }}}, + } +) + +// AuthenticationSelectorsResource is a helper function to simplify the provider implementation. +func AuthenticationSelectorsResource() resource.Resource { + return &authenticationSelectorResource{} +} + +// authenticationSelectorResource is the resource implementation. +type authenticationSelectorResource struct { + providerConfig internaltypes.ProviderConfiguration + apiClient *client.APIClient +} + +type authenticationSelectorResourceModel struct { + AttributeContract types.Object `tfsdk:"attribute_contract"` + SelectorId types.String `tfsdk:"selector_id"` + Id types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + PluginDescriptorRef types.Object `tfsdk:"plugin_descriptor_ref"` + Configuration types.Object `tfsdk:"configuration"` +} + +// GetSchema defines the schema for the resource. +func (r *authenticationSelectorResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + schema := schema.Schema{ + Description: "Manages Authentication Selectors", + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Description: "The plugin instance name. The name can be modified once the instance is created.", + Required: true, + }, + "plugin_descriptor_ref": schema.SingleNestedAttribute{ + Required: true, + Description: "Reference to the plugin descriptor for this instance. The plugin descriptor cannot be modified once the instance is created.", + Attributes: resourcelink.ToSchemaNoLocation(), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.RequiresReplace(), + }, + }, + "configuration": pluginconfiguration.ToSchema(), + "attribute_contract": schema.SingleNestedAttribute{ + Description: "The list of attributes that the Authentication Selector provides.", + Optional: true, + Attributes: map[string]schema.Attribute{ + "extended_attributes": schema.ListNestedAttribute{ + Description: "A list of additional attributes that can be returned by the Authentication Selector. The extended attributes are only used if the Authentication Selector supports them.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Description: "An attribute for the Authentication Selector attribute contract.", + Required: true, + }, + }, + }, + }, + }, + }, + }, + } + id.ToSchema(&schema) + id.ToSchemaCustomId(&schema, "selector_id", true, + "The ID of the plugin instance. The ID cannot be modified once the instance is created.") + resp.Schema = schema +} + +func addOptionalAuthenticationSelectorsFields(ctx context.Context, addRequest *client.AuthenticationSelector, plan authenticationSelectorResourceModel) error { + + if internaltypes.IsDefined(plan.AttributeContract) { + addRequest.AttributeContract = &client.AuthenticationSelectorAttributeContract{} + err := json.Unmarshal([]byte(internaljson.FromValue(plan.AttributeContract, true)), addRequest.AttributeContract) + if err != nil { + return err + } + } + + return nil + +} + +// Metadata returns the resource type name. +func (r *authenticationSelectorResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_authentication_selector" +} + +func (r *authenticationSelectorResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + providerCfg := req.ProviderData.(internaltypes.ResourceConfiguration) + r.providerConfig = providerCfg.ProviderConfig + r.apiClient = providerCfg.ApiClient + +} + +func (r *authenticationSelectorResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + var plan, state *authenticationSelectorResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + var respDiags diag.Diagnostics + + if plan == nil || state == nil { + return + } + + plan.Configuration, respDiags = pluginconfiguration.MarkComputedAttrsUnknownOnChange(plan.Configuration, state.Configuration) + resp.Diagnostics.Append(respDiags...) + + resp.Plan.Set(ctx, plan) +} + +func readAuthenticationSelectorsResponse(ctx context.Context, r *client.AuthenticationSelector, state *authenticationSelectorResourceModel, configurationFromPlan types.Object) diag.Diagnostics { + var diags, objDiags diag.Diagnostics + + state.AttributeContract, objDiags = types.ObjectValueFrom(ctx, attributeContractAttrType, r.AttributeContract) + diags = append(diags, objDiags...) + state.SelectorId = types.StringValue(r.Id) + state.Id = types.StringValue(r.Id) + state.Name = types.StringValue(r.Name) + state.PluginDescriptorRef, objDiags = resourcelink.ToStateNoLocation(&r.PluginDescriptorRef) + diags = append(diags, objDiags...) + diags = append(diags, objDiags...) + state.Configuration, objDiags = pluginconfiguration.ToState(configurationFromPlan, &r.Configuration) + diags = append(diags, objDiags...) + + // make sure all object type building appends diags + return diags +} + +func (r *authenticationSelectorResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan authenticationSelectorResourceModel + + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + var hasObjectErrMap = make(map[error]bool) + pluginDescriptorRef, err := resourcelink.ClientStruct(plan.PluginDescriptorRef) + if err != nil { + hasObjectErrMap[err] = true + } + + var configuration client.PluginConfiguration + err = json.Unmarshal([]byte(internaljson.FromValue(plan.Configuration, false)), &configuration) + if err != nil { + hasObjectErrMap[err] = true + } + + for err, hasErr := range hasObjectErrMap { + if hasErr { + resp.Diagnostics.AddError("Failed to create an Authentication Selector due to dependent object", err.Error()) + } + } + + if resp.Diagnostics.HasError() { + return + } + + createAuthenticationSelectors := client.NewAuthenticationSelector(plan.SelectorId.ValueString(), plan.Name.ValueString(), *pluginDescriptorRef, configuration) + err = addOptionalAuthenticationSelectorsFields(ctx, createAuthenticationSelectors, plan) + if err != nil { + resp.Diagnostics.AddError("Failed to add optional properties to add request for an Authentication Selector", err.Error()) + return + } + + apiCreateAuthenticationSelectors := r.apiClient.AuthenticationSelectorsAPI.CreateAuthenticationSelector(config.ProviderBasicAuthContext(ctx, r.providerConfig)) + apiCreateAuthenticationSelectors = apiCreateAuthenticationSelectors.Body(*createAuthenticationSelectors) + authenticationSelectorResponse, httpResp, err := r.apiClient.AuthenticationSelectorsAPI.CreateAuthenticationSelectorExecute(apiCreateAuthenticationSelectors) + if err != nil { + config.ReportHttpError(ctx, &resp.Diagnostics, "An error occurred while creating the an Authentication Selector", err, httpResp) + return + } + + // Read the response into the state + var state authenticationSelectorResourceModel + + diags = readAuthenticationSelectorsResponse(ctx, authenticationSelectorResponse, &state, plan.Configuration) + resp.Diagnostics.Append(diags...) + diags = resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) +} + +func (r *authenticationSelectorResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state authenticationSelectorResourceModel + + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + apiReadAuthenticationSelectors, httpResp, err := r.apiClient.AuthenticationSelectorsAPI.GetAuthenticationSelector(config.ProviderBasicAuthContext(ctx, r.providerConfig), state.SelectorId.ValueString()).Execute() + + if err != nil { + if httpResp != nil && httpResp.StatusCode == 404 { + config.ReportHttpErrorAsWarning(ctx, &resp.Diagnostics, "An error occurred while getting the an Authentication Selector", err, httpResp) + resp.State.RemoveResource(ctx) + } else { + config.ReportHttpError(ctx, &resp.Diagnostics, "An error occurred while getting the an Authentication Selector", err, httpResp) + } + return + } + + // Read the response into the state + diags = readAuthenticationSelectorsResponse(ctx, apiReadAuthenticationSelectors, &state, state.Configuration) + resp.Diagnostics.Append(diags...) + + // Set refreshed state + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) +} + +// Update updates the resource and sets the updated Terraform state on success. +func (r *authenticationSelectorResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + + var plan authenticationSelectorResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + var hasObjectErrMap = make(map[error]bool) + pluginDescriptorRef, err := resourcelink.ClientStruct(plan.PluginDescriptorRef) + if err != nil { + hasObjectErrMap[err] = true + } + + var configuration client.PluginConfiguration + err = json.Unmarshal([]byte(internaljson.FromValue(plan.Configuration, false)), &configuration) + if err != nil { + hasObjectErrMap[err] = true + } + + for err, hasErr := range hasObjectErrMap { + if hasErr { + resp.Diagnostics.AddError("Failed to create an Authentication Selector due to dependent object", err.Error()) + } + } + + if resp.Diagnostics.HasError() { + return + } + + updateAuthenticationSelectors := r.apiClient.AuthenticationSelectorsAPI.UpdateAuthenticationSelector(config.ProviderBasicAuthContext(ctx, r.providerConfig), plan.SelectorId.ValueString()) + createUpdateRequest := client.NewAuthenticationSelector(plan.SelectorId.ValueString(), plan.Name.ValueString(), *pluginDescriptorRef, configuration) + err = addOptionalAuthenticationSelectorsFields(ctx, createUpdateRequest, plan) + if err != nil { + resp.Diagnostics.AddError("Failed to add optional properties to add request for an Authentication Selector", err.Error()) + return + } + + updateAuthenticationSelectors = updateAuthenticationSelectors.Body(*createUpdateRequest) + updateAuthenticationSelectorsResponse, httpResp, err := r.apiClient.AuthenticationSelectorsAPI.UpdateAuthenticationSelectorExecute(updateAuthenticationSelectors) + if err != nil { + config.ReportHttpError(ctx, &resp.Diagnostics, "An error occurred while updating an Authentication Selector", err, httpResp) + return + } + + // Read the response + var state authenticationSelectorResourceModel + diags = readAuthenticationSelectorsResponse(ctx, updateAuthenticationSelectorsResponse, &state, plan.Configuration) + resp.Diagnostics.Append(diags...) + + // Update computed values + diags = resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) +} + +func (r *authenticationSelectorResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // Retrieve values from state + var state authenticationSelectorResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + httpResp, err := r.apiClient.AuthenticationSelectorsAPI.DeleteAuthenticationSelector(config.AuthContext(ctx, r.providerConfig), state.SelectorId.ValueString()).Execute() + if err != nil && (httpResp == nil || httpResp.StatusCode != 404) { + config.ReportHttpError(ctx, &resp.Diagnostics, "An error occurred while deleting an Authentication Selector", err, httpResp) + } +} + +func (r *authenticationSelectorResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + // Retrieve import ID and save to id attribute + resource.ImportStatePassthroughID(ctx, path.Root("selector_id"), req, resp) +}