From 95c1a4b63bec4416685774c0ec56f6f47bb198c5 Mon Sep 17 00:00:00 2001 From: Mike Mondragon Date: Mon, 3 Feb 2025 13:44:30 -0800 Subject: [PATCH] Resource `okta_app_saml`'s `acs_endpoints` is a list (with order) and not a set Closes #2173 --- okta/app.go | 2 +- okta/resource_okta_app_saml.go | 15 +++++-- okta/resource_okta_app_saml_test.go | 66 +++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/okta/app.go b/okta/app.go index 4079dbf9c..77e53c7a8 100644 --- a/okta/app.go +++ b/okta/app.go @@ -369,7 +369,7 @@ func setSamlSettings(d *schema.ResourceData, signOn *sdk.SamlApplicationSettings for i := range acsEndpointsObj { acsEndpoints[i] = acsEndpointsObj[i].Url } - _ = d.Set("acs_endpoints", convertStringSliceToSetNullable(acsEndpoints)) + _ = d.Set("acs_endpoints", acsEndpoints) } } else { _ = d.Set("acs_endpoints", nil) diff --git a/okta/resource_okta_app_saml.go b/okta/resource_okta_app_saml.go index 1e716491a..f791f794d 100644 --- a/okta/resource_okta_app_saml.go +++ b/okta/resource_okta_app_saml.go @@ -312,7 +312,7 @@ request feature flag 'ADVANCED_SSO' be applied to your org.`, Description: "Saml Inline Hook setting", }, "acs_endpoints": { - Type: schema.TypeSet, + Type: schema.TypeList, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, Description: "An array of ACS endpoints. You can configure a maximum of 100 endpoints.", @@ -521,6 +521,15 @@ func resourceAppSamlRead(ctx context.Context, d *schema.ResourceData, m interfac return diag.Errorf("failed to set Application Credential Key Values: %v", err) } + // acsEndpoints + if app.Settings.SignOn != nil && len(app.Settings.SignOn.AcsEndpoints) > 0 { + acsEndponts := make([]string, len(app.Settings.SignOn.AcsEndpoints)) + for _, ae := range app.Settings.SignOn.AcsEndpoints { + acsEndponts[ae.Index] = ae.Url + } + _ = d.Set("acs_endpoints", acsEndponts) + } + appRead(d, app.Name, app.Status, app.SignOnMode, app.Label, app.Accessibility, app.Visibility, app.Settings.Notes) if app.SignOnMode == "SAML_1_1" { _ = d.Set("saml_version", saml11) @@ -547,7 +556,7 @@ func resourceAppSamlUpdate(ctx context.Context, d *schema.ResourceData, m interf client := getOktaClientFromMetadata(m) app, err := buildSamlApp(d) if err != nil { - return diag.Errorf("failed to create SAML application: %v", err) + return diag.Errorf("failed to build SAML application: %v", err) } _, _, err = client.Application.UpdateApplication(ctx, d.Id(), app) if err != nil { @@ -657,7 +666,7 @@ func buildSamlApp(d *schema.ResourceData) (*sdk.SamlApplication, error) { } // Assumes that sso url is already part of the acs endpoints as part of the desired state. - acsEndpoints := convertInterfaceToStringSet(d.Get("acs_endpoints")) + acsEndpoints := convertInterfaceToStringArr(d.Get("acs_endpoints")) // If there are acs endpoints, implies this flag should be true. allowMultipleAcsEndpoints := false diff --git a/okta/resource_okta_app_saml_test.go b/okta/resource_okta_app_saml_test.go index 6430cd0ae..b715ec353 100644 --- a/okta/resource_okta_app_saml_test.go +++ b/okta/resource_okta_app_saml_test.go @@ -484,3 +484,69 @@ resource "okta_app_saml" "test" { }, }) } + +func TestAccResourceOktaAppSaml_Issue2171AcsEndpoints(t *testing.T) { + mgr := newFixtureManager("resources", appSaml, t.Name()) + resourceName := fmt.Sprintf("%s.test", appSaml) + config := ` +resource "okta_app_saml" "test" { + acs_endpoints = [%s] + + label = "testAcc_replace_with_uuid" + sso_url = "http://google.com" + recipient = "http://here.com" + destination = "http://its-about-the-journey.com" + audience = "http://audience.com" + subject_name_id_template = "$${source.login}" + subject_name_id_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" + response_signed = true + assertion_signed = true + signature_algorithm = "RSA_SHA1" + digest_algorithm = "SHA1" + honor_force_authn = true + authn_context_class_ref = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" +} +` + acsEndpoints1 := "\"https://example.com\",\"https://okta.com\"" + acsEndpoints2 := "\"https://okta.com\",\"https://example.com\"" + acsEndpoints3 := "\"https://okta.com\",\"https://middle.example.com\",\"https://example.com\"" + + oktaResourceTest(t, resource.TestCase{ + PreCheck: testAccPreCheck(t), + ErrorCheck: testAccErrorChecks(t), + ProviderFactories: testAccProvidersFactories, + CheckDestroy: checkResourceDestroy(appSaml, createDoesAppExist(sdk.NewSamlApplication())), + Steps: []resource.TestStep{ + { + Config: mgr.ConfigReplace(fmt.Sprintf(config, acsEndpoints1)), + Check: resource.ComposeTestCheckFunc( + ensureResourceExists(resourceName, createDoesAppExist(sdk.NewSamlApplication())), + resource.TestCheckResourceAttr(resourceName, "acs_endpoints.#", "2"), + resource.TestCheckResourceAttr(resourceName, "acs_endpoints.0", "https://example.com"), + resource.TestCheckResourceAttr(resourceName, "acs_endpoints.1", "https://okta.com"), + ), + }, + { + // demonstrate flipping order is respected + Config: mgr.ConfigReplace(fmt.Sprintf(config, acsEndpoints2)), + Check: resource.ComposeTestCheckFunc( + ensureResourceExists(resourceName, createDoesAppExist(sdk.NewSamlApplication())), + resource.TestCheckResourceAttr(resourceName, "acs_endpoints.#", "2"), + resource.TestCheckResourceAttr(resourceName, "acs_endpoints.0", "https://okta.com"), + resource.TestCheckResourceAttr(resourceName, "acs_endpoints.1", "https://example.com"), + ), + }, + { + // demonstrate inserting and order is respected + Config: mgr.ConfigReplace(fmt.Sprintf(config, acsEndpoints3)), + Check: resource.ComposeTestCheckFunc( + ensureResourceExists(resourceName, createDoesAppExist(sdk.NewSamlApplication())), + resource.TestCheckResourceAttr(resourceName, "acs_endpoints.#", "3"), + resource.TestCheckResourceAttr(resourceName, "acs_endpoints.0", "https://okta.com"), + resource.TestCheckResourceAttr(resourceName, "acs_endpoints.1", "https://middle.example.com"), + resource.TestCheckResourceAttr(resourceName, "acs_endpoints.2", "https://example.com"), + ), + }, + }, + }) +}