Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AUTH-6145 support multi-valued + service token authentication for SCIM provisioning to access applications #4743

Merged
merged 3 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/4743.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/access_application: support multi-valued + Access service token authentication for SCIM provisioning to Access applications
```
192 changes: 38 additions & 154 deletions internal/sdkv2provider/resource_cloudflare_access_application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,10 @@ func TestAccCloudflareAccessApplication_WithSCIMConfigInvalidMappingSchema(t *te
})
}

func TestAccCloudflareAccessApplication_WithSCIMConfigHttpBasicMissingRequired(t *testing.T) {
func TestAccCloudflareAccessApplication_WithSCIMConfigOAuthBearerToken(t *testing.T) {
rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_zero_trust_access_application.%s", rnd)
idpName := fmt.Sprintf("cloudflare_zero_trust_access_identity_provider.%s", rnd)

resource.Test(t, resource.TestCase{
PreCheck: func() {
Expand All @@ -266,14 +268,34 @@ func TestAccCloudflareAccessApplication_WithSCIMConfigHttpBasicMissingRequired(t
CheckDestroy: testAccCheckCloudflareAccessApplicationDestroy,
Steps: []resource.TestStep{
{
Config: testAccCloudflareAccessApplicationSCIMConfigHttpBasicMissingRequired(rnd, accountID, domain),
ExpectError: regexp.MustCompile(`.*all of\s*` + "`scim_config.0.authentication.0.password,scim_config.0.authentication.0.user`" + `\s*must be specified.*`),
Config: testAccCloudflareAccessApplicationSCIMConfigValidOAuthBearerToken(rnd, accountID, domain),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, consts.AccountIDSchemaKey, accountID),
resource.TestCheckResourceAttr(name, "name", rnd),
resource.TestCheckResourceAttr(name, "domain", fmt.Sprintf("%s.%s", rnd, domain)),
resource.TestCheckResourceAttr(name, "type", "self_hosted"),
resource.TestCheckResourceAttr(name, "session_duration", "24h"),
resource.TestCheckResourceAttr(name, "scim_config.#", "1"),
resource.TestCheckResourceAttr(name, "scim_config.0.enabled", "true"),
resource.TestCheckResourceAttr(name, "scim_config.0.remote_uri", "scim.com"),
resource.TestCheckResourceAttrPair(name, "scim_config.0.idp_uid", idpName, "id"),
resource.TestCheckResourceAttr(name, "scim_config.0.deactivate_on_delete", "true"),
resource.TestCheckResourceAttr(name, "scim_config.0.authentication.0.scheme", "oauthbearertoken"),
resource.TestCheckResourceAttrSet(name, "scim_config.0.authentication.0.token"),
resource.TestCheckResourceAttr(name, "scim_config.0.mappings.0.schema", "urn:ietf:params:scim:schemas:core:2.0:User"),
resource.TestCheckResourceAttr(name, "scim_config.0.mappings.0.enabled", "true"),
resource.TestCheckResourceAttr(name, "scim_config.0.mappings.0.filter", "title pr or userType eq \"Intern\""),
resource.TestCheckResourceAttr(name, "scim_config.0.mappings.0.transform_jsonata", "$merge([$, {'userName': $substringBefore($.userName, '@') & '+test@' & $substringAfter($.userName, '@')}])"),
resource.TestCheckResourceAttr(name, "scim_config.0.mappings.0.operations.0.create", "true"),
resource.TestCheckResourceAttr(name, "scim_config.0.mappings.0.operations.0.update", "true"),
resource.TestCheckResourceAttr(name, "scim_config.0.mappings.0.operations.0.delete", "true"),
),
},
},
})
}

func TestAccCloudflareAccessApplication_WithSCIMConfigOAuthBearerToken(t *testing.T) {
func TestAccCloudflareAccessApplication_WithSCIMConfigMultiAuth(t *testing.T) {
rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_zero_trust_access_application.%s", rnd)
idpName := fmt.Sprintf("cloudflare_zero_trust_access_identity_provider.%s", rnd)
Expand All @@ -286,7 +308,7 @@ func TestAccCloudflareAccessApplication_WithSCIMConfigOAuthBearerToken(t *testin
CheckDestroy: testAccCheckCloudflareAccessApplicationDestroy,
Steps: []resource.TestStep{
{
Config: testAccCloudflareAccessApplicationSCIMConfigValidOAuthBearerToken(rnd, accountID, domain),
Config: testAccCloudflareAccessApplicationSCIMConfigValidMulti(rnd, accountID, domain),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, consts.AccountIDSchemaKey, accountID),
resource.TestCheckResourceAttr(name, "name", rnd),
Expand All @@ -300,6 +322,9 @@ func TestAccCloudflareAccessApplication_WithSCIMConfigOAuthBearerToken(t *testin
resource.TestCheckResourceAttr(name, "scim_config.0.deactivate_on_delete", "true"),
resource.TestCheckResourceAttr(name, "scim_config.0.authentication.0.scheme", "oauthbearertoken"),
resource.TestCheckResourceAttrSet(name, "scim_config.0.authentication.0.token"),
resource.TestCheckResourceAttr(name, "scim_config.0.authentication.1.scheme", "access_service_token"),
resource.TestCheckResourceAttr(name, "scim_config.0.authentication.1.client_id", "1234"),
resource.TestCheckResourceAttrSet(name, "scim_config.0.authentication.1.client_secret"),
resource.TestCheckResourceAttr(name, "scim_config.0.mappings.0.schema", "urn:ietf:params:scim:schemas:core:2.0:User"),
resource.TestCheckResourceAttr(name, "scim_config.0.mappings.0.enabled", "true"),
resource.TestCheckResourceAttr(name, "scim_config.0.mappings.0.filter", "title pr or userType eq \"Intern\""),
Expand Down Expand Up @@ -358,42 +383,6 @@ func TestAccCloudflareAccessApplication_WithSCIMConfigOAuth2(t *testing.T) {
})
}

func TestAccCloudflareAccessApplication_WithSCIMConfigOAuth2MissingRequired(t *testing.T) {
rnd := generateRandomResourceName()

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
ProviderFactories: providerFactories,
CheckDestroy: testAccCheckCloudflareAccessApplicationDestroy,
Steps: []resource.TestStep{
{
Config: testAccCloudflareAccessApplicationSCIMConfigOAuth2MissingRequired(rnd, accountID, domain),
ExpectError: regexp.MustCompile(`.*all of\s*` + "`scim_config.0.authentication.0.authorization_url,scim_config.0.authentication.0.client_id,scim_config.0.authentication.0.client_secret,scim_config.0.authentication.0.token_url`" + `\s*must be specified.*`),
},
},
})
}

func TestAccCloudflareAccessApplication_WithSCIMConfigAuthenticationInvalid(t *testing.T) {
rnd := generateRandomResourceName()

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
ProviderFactories: providerFactories,
CheckDestroy: testAccCheckCloudflareAccessApplicationDestroy,
Steps: []resource.TestStep{
{
Config: testAccCloudflareAccessApplicationSCIMConfigAuthenticationInvalid(rnd, accountID, domain),
ExpectError: regexp.MustCompile(`Error: Conflicting configuration arguments`),
},
},
})
}

func TestAccCloudflareAccessApplication_WithCORS(t *testing.T) {
rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_zero_trust_access_application.%s", rnd)
Expand Down Expand Up @@ -1983,7 +1972,7 @@ resource "cloudflare_zero_trust_access_application" "%[1]s" {
`, rnd, accountID, domain)
}

func testAccCloudflareAccessApplicationSCIMConfigValidOAuth2(rnd, accountID, domain string) string {
func testAccCloudflareAccessApplicationSCIMConfigValidMulti(rnd, accountID, domain string) string {
return fmt.Sprintf(`
resource "cloudflare_zero_trust_access_identity_provider" "%[1]s" {
account_id = "%[2]s"
Expand Down Expand Up @@ -2015,66 +2004,13 @@ resource "cloudflare_zero_trust_access_application" "%[1]s" {
idp_uid = cloudflare_zero_trust_access_identity_provider.%[1]s.id
deactivate_on_delete = true
authentication {
scheme = "oauth2"
client_id = "beepboop"
client_secret = "bop"
authorization_url = "https://www.authorization.com"
token_url = "https://www.token.com"
scopes = ["read"]
}
mappings {
schema = "urn:ietf:params:scim:schemas:core:2.0:User"
enabled = true
filter = "title pr or userType eq \"Intern\""
transform_jsonata = "$merge([$, {'userName': $substringBefore($.userName, '@') & '+test@' & $substringAfter($.userName, '@')}])"
operations {
create = true
update = true
delete = true
}
}
}
}
`, rnd, accountID, domain)
}

func testAccCloudflareAccessApplicationSCIMConfigOAuth2MissingRequired(rnd, accountID, domain string) string {
return fmt.Sprintf(`
resource "cloudflare_zero_trust_access_identity_provider" "%[1]s" {
account_id = "%[2]s"
name = "%[1]s"
type = "azureAD"
config {
client_id = "test"
client_secret = "test"
directory_id = "directory"
support_groups = true
}
scim_config {
enabled = true
group_member_deprovision = true
seat_deprovision = true
user_deprovision = true
scheme = "oauthbearertoken"
token = "beepboop"
}
}

resource "cloudflare_zero_trust_access_application" "%[1]s" {
account_id = "%[2]s"
name = "%[1]s"
type = "self_hosted"
session_duration = "24h"
domain = "%[1]s.%[3]s"
scim_config {
enabled = true
remote_uri = "scim.com"
idp_uid = cloudflare_zero_trust_access_identity_provider.%[1]s.id
deactivate_on_delete = true
authentication {
scheme = "oauth2"
client_id = "beepboop"
client_secret = "bop"
authorization_url = "https://www.authorization.com"
scopes = ["read"]
scheme = "access_service_token"
client_id = "1234"
client_secret = "5678"
}
mappings {
schema = "urn:ietf:params:scim:schemas:core:2.0:User"
Expand All @@ -2086,13 +2022,14 @@ resource "cloudflare_zero_trust_access_application" "%[1]s" {
update = true
delete = true
}
strictness = "strict"
}
}
}
`, rnd, accountID, domain)
}

func testAccCloudflareAccessApplicationSCIMConfigAuthenticationInvalid(rnd, accountID, domain string) string {
func testAccCloudflareAccessApplicationSCIMConfigValidOAuth2(rnd, accountID, domain string) string {
return fmt.Sprintf(`
resource "cloudflare_zero_trust_access_identity_provider" "%[1]s" {
account_id = "%[2]s"
Expand Down Expand Up @@ -2129,9 +2066,6 @@ resource "cloudflare_zero_trust_access_application" "%[1]s" {
client_secret = "bop"
authorization_url = "https://www.authorization.com"
token_url = "https://www.token.com"
token = "1234"
user = "user"
password = "ahhh"
scopes = ["read"]
}
mappings {
Expand All @@ -2150,56 +2084,6 @@ resource "cloudflare_zero_trust_access_application" "%[1]s" {
`, rnd, accountID, domain)
}

func testAccCloudflareAccessApplicationSCIMConfigHttpBasicMissingRequired(rnd, accountID, domain string) string {
return fmt.Sprintf(`
resource "cloudflare_zero_trust_access_identity_provider" "%[1]s" {
account_id = "%[2]s"
name = "%[1]s"
type = "azureAD"
config {
client_id = "test"
client_secret = "test"
directory_id = "directory"
support_groups = true
}
scim_config {
enabled = true
group_member_deprovision = true
seat_deprovision = true
user_deprovision = true
}
}

resource "cloudflare_zero_trust_access_application" "%[1]s" {
account_id = "%[2]s"
name = "%[1]s"
type = "self_hosted"
session_duration = "24h"
domain = "%[1]s.%[3]s"
scim_config {
enabled = true
remote_uri = "scim.com"
idp_uid = cloudflare_zero_trust_access_identity_provider.%[1]s.id
deactivate_on_delete = true
authentication {
scheme = "httpbasic"
user = "test"
}
mappings {
schema = "urn:ietf:params:scim:schemas:core:2.0:User"
enabled = true
filter = "title pr or userType eq \"Intern\""
transform_jsonata = "$merge([$, {'userName': $substringBefore($.userName, '@') & '+test@' & $substringAfter($.userName, '@')}])"
operations {
create = true
update = true
delete = true
}
}
}
}`, rnd, accountID, domain)
}

func testAccCloudflareAccessApplicationSCIMConfigInvalidMappingSchema(rnd, accountID, domain string) string {
return fmt.Sprintf(`
resource "cloudflare_zero_trust_access_identity_provider" "%[1]s" {
Expand Down
Loading
Loading