From 62356bdaf0e9f7a9e520784fc7c54fc7aed309ef Mon Sep 17 00:00:00 2001 From: Francis Chuang Date: Tue, 28 May 2024 16:38:12 +1000 Subject: [PATCH 1/5] Force Cloudflare Access Application to be recreated if SaaS app auth_type is changed. --- .changelog/3332.txt | 3 +++ ...urce_cloudflare_access_application_test.go | 26 +++++++++++++++++++ .../schema_cloudflare_access_application.go | 1 + 3 files changed, 30 insertions(+) create mode 100644 .changelog/3332.txt diff --git a/.changelog/3332.txt b/.changelog/3332.txt new file mode 100644 index 0000000000..cb73f3c6c1 --- /dev/null +++ b/.changelog/3332.txt @@ -0,0 +1,3 @@ +```release-note:bug +Force Cloudflare Access Application to be recreated if SaaS app auth_type is changed +``` \ No newline at end of file diff --git a/internal/sdkv2provider/resource_cloudflare_access_application_test.go b/internal/sdkv2provider/resource_cloudflare_access_application_test.go index 571a43e1e1..7183ace461 100644 --- a/internal/sdkv2provider/resource_cloudflare_access_application_test.go +++ b/internal/sdkv2provider/resource_cloudflare_access_application_test.go @@ -1027,6 +1027,32 @@ func TestAccCloudflareAccessApplication_WithAppLauncherCustomization(t *testing. }) } +func TestAccCloudflareAccessApplication_AuthTypeForcesNewResource(t *testing.T) { + rnd := generateRandomResourceName() + name := fmt.Sprintf("cloudflare_access_application.%s", rnd) + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckAccount(t) + }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckCloudflareAccessApplicationDestroy, + Steps: []resource.TestStep{ + {Config: testAccCloudflareAccessApplicationConfigWithSAMLSaas(rnd, accountID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "saas_app.auth_type", "saml"), + ), + }, + {Config: testAccCloudflareAccessApplicationConfigWithOIDCSaas(rnd, accountID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "saas_app.auth_type", "oidc"), + ), + }, + }, + }) +} + func testAccCloudflareAccessApplicationConfigBasic(rnd string, domain string, identifier *cloudflare.ResourceContainer) string { return fmt.Sprintf(` resource "cloudflare_access_application" "%[1]s" { diff --git a/internal/sdkv2provider/schema_cloudflare_access_application.go b/internal/sdkv2provider/schema_cloudflare_access_application.go index d0629f4d45..996a4766b5 100644 --- a/internal/sdkv2provider/schema_cloudflare_access_application.go +++ b/internal/sdkv2provider/schema_cloudflare_access_application.go @@ -167,6 +167,7 @@ func resourceCloudflareAccessApplicationSchema() map[string]*schema.Schema { Optional: true, ValidateFunc: validation.StringInSlice([]string{"oidc", "saml"}, false), Description: "", + ForceNew: true, }, "public_key": { Type: schema.TypeString, From f833df76039d6fd73ff9a9e3eaf11fd9a9c8aaf9 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 29 May 2024 10:23:19 +1000 Subject: [PATCH 2/5] fix changelog entry --- .changelog/3332.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changelog/3332.txt b/.changelog/3332.txt index cb73f3c6c1..664089cec3 100644 --- a/.changelog/3332.txt +++ b/.changelog/3332.txt @@ -1,3 +1,3 @@ ```release-note:bug -Force Cloudflare Access Application to be recreated if SaaS app auth_type is changed -``` \ No newline at end of file +resource/cloudflare_access_application: force recreation if SaaS app `auth_type` is changed +``` From 23de9448513f956667f2aa4f0930bbead4be30b9 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 29 May 2024 10:25:04 +1000 Subject: [PATCH 3/5] `make docs` --- docs/resources/access_application.md | 43 +++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/resources/access_application.md b/docs/resources/access_application.md index de74ff02ce..d7dd4aecca 100644 --- a/docs/resources/access_application.md +++ b/docs/resources/access_application.md @@ -130,16 +130,19 @@ Optional: Optional: +- `allow_pkce_without_client_secret` (Boolean) Allow PKCE flow without a client secret. - `app_launcher_url` (String) The URL where this applications tile redirects users. -- `auth_type` (String) +- `auth_type` (String) **Modifying this attribute will force creation of a new resource.** - `consumer_service_url` (String) The service provider's endpoint that is responsible for receiving and parsing a SAML assertion. - `custom_attribute` (Block List) Custom attribute mapped from IDPs. (see [below for nested schema](#nestedblock--saas_app--custom_attribute)) +- `custom_claim` (Block List) Custom claim mapped from IDPs. (see [below for nested schema](#nestedblock--saas_app--custom_claim)) - `default_relay_state` (String) The relay state used if not provided by the identity provider. - `grant_types` (Set of String) The OIDC flows supported by this application. - `group_filter_regex` (String) A regex to filter Cloudflare groups returned in ID token and userinfo endpoint. - `name_id_format` (String) The format of the name identifier sent to the SaaS application. - `name_id_transform_jsonata` (String) A [JSONata](https://jsonata.org/) expression that transforms an application's user identities into a NameID value for its SAML assertion. This expression should evaluate to a singular string. The output of this expression can override the `name_id_format` setting. - `redirect_uris` (Set of String) The permitted URL's for Cloudflare to return Authorization codes and Access/ID tokens. +- `refresh_token_options` (Block List) Refresh token grant options. (see [below for nested schema](#nestedblock--saas_app--refresh_token_options)) - `saml_attribute_transform_jsonata` (String) A [JSONata](https://jsonata.org/) expression that transforms an application's user identities into attribute assertions in the SAML response. The expression can transform id, email, name, and groups values. It can also transform fields listed in the saml_attributes or oidc_fields of the identity provider used to authenticate. The output of this expression must be a JSON object. - `scopes` (Set of String) Define the user information shared with access. - `sp_entity_id` (String) A globally unique name for an identity or service provider. @@ -173,6 +176,44 @@ Required: - `name` (String) The name of the attribute as provided by the IDP. +Optional: + +- `name_by_idp` (Map of String) A mapping from IdP ID to claim name. + + + + +### Nested Schema for `saas_app.custom_claim` + +Required: + +- `source` (Block List, Min: 1, Max: 1) (see [below for nested schema](#nestedblock--saas_app--custom_claim--source)) + +Optional: + +- `name` (String) The name of the attribute as provided to the SaaS app. +- `required` (Boolean) True if the attribute must be always present. +- `scope` (String) The scope of the claim. + + +### Nested Schema for `saas_app.custom_claim.source` + +Required: + +- `name` (String) The name of the attribute as provided by the IDP. + +Optional: + +- `name_by_idp` (Map of String) A mapping from IdP ID to claim name. + + + + +### Nested Schema for `saas_app.refresh_token_options` + +Optional: + +- `lifetime` (String) How long a refresh token will be valid for after creation. Valid units are m,h,d. Must be longer than 1m. From b51212bc54f49853e33b0a2319ebd7f5f1d6328c Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 29 May 2024 10:26:23 +1000 Subject: [PATCH 4/5] fix lint --- ...urce_cloudflare_access_application_test.go | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/internal/sdkv2provider/resource_cloudflare_access_application_test.go b/internal/sdkv2provider/resource_cloudflare_access_application_test.go index 7183ace461..bc3e17cb1b 100644 --- a/internal/sdkv2provider/resource_cloudflare_access_application_test.go +++ b/internal/sdkv2provider/resource_cloudflare_access_application_test.go @@ -1039,12 +1039,14 @@ func TestAccCloudflareAccessApplication_AuthTypeForcesNewResource(t *testing.T) ProviderFactories: providerFactories, CheckDestroy: testAccCheckCloudflareAccessApplicationDestroy, Steps: []resource.TestStep{ - {Config: testAccCloudflareAccessApplicationConfigWithSAMLSaas(rnd, accountID), + { + Config: testAccCloudflareAccessApplicationConfigWithSAMLSaas(rnd, accountID), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(name, "saas_app.auth_type", "saml"), ), }, - {Config: testAccCloudflareAccessApplicationConfigWithOIDCSaas(rnd, accountID), + { + Config: testAccCloudflareAccessApplicationConfigWithOIDCSaas(rnd, accountID), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(name, "saas_app.auth_type", "oidc"), ), @@ -1670,7 +1672,7 @@ resource "cloudflare_access_identity_provider" "%[1]s" { user_deprovision = true } } - + resource "cloudflare_access_application" "%[1]s" { account_id = "%[2]s" name = "%[1]s" @@ -1722,7 +1724,7 @@ resource "cloudflare_access_identity_provider" "%[1]s" { user_deprovision = true } } - + resource "cloudflare_access_application" "%[1]s" { account_id = "%[2]s" name = "%[1]s" @@ -1762,7 +1764,7 @@ resource "cloudflare_access_identity_provider" "%[1]s" { user_deprovision = true } } - + resource "cloudflare_access_application" "%[1]s" { account_id = "%[2]s" name = "%[1]s" @@ -1813,7 +1815,7 @@ resource "cloudflare_access_identity_provider" "%[1]s" { user_deprovision = true } } - + resource "cloudflare_access_application" "%[1]s" { account_id = "%[2]s" name = "%[1]s" @@ -1868,7 +1870,7 @@ resource "cloudflare_access_identity_provider" "%[1]s" { user_deprovision = true } } - + resource "cloudflare_access_application" "%[1]s" { account_id = "%[2]s" name = "%[1]s" @@ -1922,7 +1924,7 @@ resource "cloudflare_access_identity_provider" "%[1]s" { user_deprovision = true } } - + resource "cloudflare_access_application" "%[1]s" { account_id = "%[2]s" name = "%[1]s" @@ -1980,7 +1982,7 @@ resource "cloudflare_access_identity_provider" "%[1]s" { user_deprovision = true } } - + resource "cloudflare_access_application" "%[1]s" { account_id = "%[2]s" name = "%[1]s" @@ -2030,7 +2032,7 @@ resource "cloudflare_access_identity_provider" "%[1]s" { user_deprovision = true } } - + resource "cloudflare_access_application" "%[1]s" { account_id = "%[2]s" name = "%[1]s" @@ -2068,7 +2070,7 @@ func testAccCloudflareAccessApplicationConfigWithReusablePolicies(rnd, domain st resource "cloudflare_access_policy" "%[1]s_p1" { account_id = "%[3]s" name = "%[1]s" - decision = "allow" + decision = "allow" include { email = ["a@example.com"] } @@ -2077,7 +2079,7 @@ resource "cloudflare_access_policy" "%[1]s_p1" { resource "cloudflare_access_policy" "%[1]s_p2" { account_id = "%[3]s" name = "%[1]s" - decision = "non_identity" + decision = "non_identity" include { ip = ["127.0.0.1/32"] } From b120016b892678456d8b32ddb5ccb491794c04dc Mon Sep 17 00:00:00 2001 From: Francis Chuang Date: Wed, 29 May 2024 11:16:02 +1000 Subject: [PATCH 5/5] Fix acceptance test --- .../resource_cloudflare_access_application_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/sdkv2provider/resource_cloudflare_access_application_test.go b/internal/sdkv2provider/resource_cloudflare_access_application_test.go index bc3e17cb1b..4f6d1c7a45 100644 --- a/internal/sdkv2provider/resource_cloudflare_access_application_test.go +++ b/internal/sdkv2provider/resource_cloudflare_access_application_test.go @@ -1042,13 +1042,13 @@ func TestAccCloudflareAccessApplication_AuthTypeForcesNewResource(t *testing.T) { Config: testAccCloudflareAccessApplicationConfigWithSAMLSaas(rnd, accountID), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(name, "saas_app.auth_type", "saml"), + resource.TestCheckResourceAttr(name, "saas_app.0.auth_type", ""), ), }, { Config: testAccCloudflareAccessApplicationConfigWithOIDCSaas(rnd, accountID), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(name, "saas_app.auth_type", "oidc"), + resource.TestCheckResourceAttr(name, "saas_app.0.auth_type", "oidc"), ), }, },