Skip to content

Commit b176f7c

Browse files
authored
Merge pull request #5720 from GreenStage/egomes/AUTH-7151
AUTH-7151 fix self-hosted-app destination validations
2 parents a0d7f7e + 4218a54 commit b176f7c

File tree

7 files changed

+122
-10
lines changed

7 files changed

+122
-10
lines changed

internal/customvalidator/required_when_other_attribute_is_one_of_validator.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,13 @@ func (i requiredWhenOtherAttributeIsOneOfValidator) MarkdownDescription(ctx cont
4141
return i.Description(ctx)
4242
}
4343

44-
func (i requiredWhenOtherAttributeIsOneOfValidator) validateAny(ctx context.Context, cfg *tfsdk.Config, attrPath path.Path, value attr.Value, resDiagnostics *diag.Diagnostics) {
44+
func (i requiredWhenOtherAttributeIsOneOfValidator) validateAny(ctx context.Context, cfg *tfsdk.Config, attrPathExpr path.Expression, attrPath path.Path, value attr.Value, resDiagnostics *diag.Diagnostics) {
4545
if !value.IsNull() || value.IsUnknown() {
4646
return
4747
}
4848

49-
matchedPaths, diags := cfg.PathMatches(ctx, i.pathExpr)
49+
expression := attrPathExpr.Merge(i.pathExpr)
50+
matchedPaths, diags := cfg.PathMatches(ctx, expression)
5051
resDiagnostics.Append(diags...)
5152

5253
for _, mp := range matchedPaths {
@@ -82,5 +83,5 @@ func (i requiredWhenOtherAttributeIsOneOfValidator) validateAny(ctx context.Cont
8283
}
8384

8485
func (i requiredWhenOtherAttributeIsOneOfValidator) ValidateObject(ctx context.Context, req validator.ObjectRequest, res *validator.ObjectResponse) {
85-
i.validateAny(ctx, &req.Config, req.Path, req.ConfigValue, &res.Diagnostics)
86+
i.validateAny(ctx, &req.Config, req.PathExpression, req.Path, req.ConfigValue, &res.Diagnostics)
8687
}

internal/customvalidator/requires_other_attribute_to_be_one_of_validator.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,13 @@ func (i requiresOtherAttributeToBeOneOfValidator) MarkdownDescription(ctx contex
4747
return i.Description(ctx)
4848
}
4949

50-
func (i requiresOtherAttributeToBeOneOfValidator) validateAny(ctx context.Context, cfg *tfsdk.Config, attrPath path.Path, value attr.Value, resDiagnostics *diag.Diagnostics) {
50+
func (i requiresOtherAttributeToBeOneOfValidator) validateAny(ctx context.Context, cfg *tfsdk.Config, attrPathExpr path.Expression, attrPath path.Path, value attr.Value, resDiagnostics *diag.Diagnostics) {
5151
if value.IsNull() || value.IsUnknown() {
5252
return
5353
}
5454

55-
matchedPaths, diags := cfg.PathMatches(ctx, i.pathExpr)
55+
expression := attrPathExpr.Merge(i.pathExpr)
56+
matchedPaths, diags := cfg.PathMatches(ctx, expression)
5657
resDiagnostics.Append(diags...)
5758

5859
for _, mp := range matchedPaths {
@@ -94,17 +95,17 @@ func (i requiresOtherAttributeToBeOneOfValidator) validateAny(ctx context.Contex
9495
}
9596

9697
func (i requiresOtherAttributeToBeOneOfValidator) ValidateBool(ctx context.Context, req validator.BoolRequest, res *validator.BoolResponse) {
97-
i.validateAny(ctx, &req.Config, req.Path, req.ConfigValue, &res.Diagnostics)
98+
i.validateAny(ctx, &req.Config, req.PathExpression, req.Path, req.ConfigValue, &res.Diagnostics)
9899
}
99100

100101
func (i requiresOtherAttributeToBeOneOfValidator) ValidateString(ctx context.Context, req validator.StringRequest, res *validator.StringResponse) {
101-
i.validateAny(ctx, &req.Config, req.Path, req.ConfigValue, &res.Diagnostics)
102+
i.validateAny(ctx, &req.Config, req.PathExpression, req.Path, req.ConfigValue, &res.Diagnostics)
102103
}
103104

104105
func (i requiresOtherAttributeToBeOneOfValidator) ValidateObject(ctx context.Context, req validator.ObjectRequest, res *validator.ObjectResponse) {
105-
i.validateAny(ctx, &req.Config, req.Path, req.ConfigValue, &res.Diagnostics)
106+
i.validateAny(ctx, &req.Config, req.PathExpression, req.Path, req.ConfigValue, &res.Diagnostics)
106107
}
107108

108109
func (i requiresOtherAttributeToBeOneOfValidator) ValidateList(ctx context.Context, req validator.ListRequest, res *validator.ListResponse) {
109-
i.validateAny(ctx, &req.Config, req.Path, req.ConfigValue, &res.Diagnostics)
110+
i.validateAny(ctx, &req.Config, req.PathExpression, req.Path, req.ConfigValue, &res.Diagnostics)
110111
}

internal/services/zero_trust_access_application/model.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ type ZeroTrustAccessApplicationTargetCriteriaModel struct {
123123
}
124124

125125
type ZeroTrustAccessApplicationDestinationsModel struct {
126-
Type types.String `tfsdk:"type" json:"type,optional"`
126+
Type types.String `tfsdk:"type" json:"type,computed_optional"`
127127
URI types.String `tfsdk:"uri" json:"uri,optional"`
128128
CIDR types.String `tfsdk:"cidr" json:"cidr,optional"`
129129
Hostname types.String `tfsdk:"hostname" json:"hostname,optional"`

internal/services/zero_trust_access_application/resource_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,60 @@ func TestAccCloudflareAccessApplicationWithInvalidSessionDuration(t *testing.T)
13601360
})
13611361
}
13621362

1363+
func TestAccCloudflareAccessApplicationWithInvalidPrivateDestination(t *testing.T) {
1364+
rnd := utils.GenerateRandomResourceName()
1365+
1366+
resource.Test(t, resource.TestCase{
1367+
PreCheck: func() {
1368+
acctest.TestAccPreCheck(t)
1369+
acctest.TestAccPreCheck_AccountID(t)
1370+
},
1371+
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories,
1372+
Steps: []resource.TestStep{
1373+
{
1374+
Config: testAccessApplicationWithInvalidPrivateDestination(rnd, accountID),
1375+
ExpectError: regexp.MustCompile(`"destinations\[0]\.(hostname|port_range)" can only be set if "<\.type" is one of: "private"`),
1376+
},
1377+
},
1378+
})
1379+
}
1380+
1381+
func TestAccCloudflareAccessApplicationWithDestinations(t *testing.T) {
1382+
rnd := utils.GenerateRandomResourceName()
1383+
1384+
name := fmt.Sprintf("cloudflare_zero_trust_access_application.%s", rnd)
1385+
publicDomain := fmt.Sprintf("d1.%[1]s.%[2]s", rnd, domain)
1386+
privateDomain := fmt.Sprintf("%[1]s.private", rnd)
1387+
1388+
resource.Test(t, resource.TestCase{
1389+
PreCheck: func() {
1390+
acctest.TestAccPreCheck(t)
1391+
acctest.TestAccPreCheck_AccountID(t)
1392+
},
1393+
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories,
1394+
CheckDestroy: testAccCheckCloudflareAccessApplicationDestroy,
1395+
Steps: []resource.TestStep{
1396+
{
1397+
Config: testAccessApplicationWithDestinations(rnd, domain, cloudflare.AccountIdentifier(accountID)),
1398+
Check: resource.ComposeTestCheckFunc(
1399+
resource.TestCheckResourceAttr(name, consts.AccountIDSchemaKey, accountID),
1400+
resource.TestCheckResourceAttr(name, "name", rnd),
1401+
resource.TestCheckResourceAttr(name, "destinations.#", "2"),
1402+
resource.TestCheckResourceAttr(name, "destinations.0.type", "private"),
1403+
resource.TestCheckResourceAttr(name, "destinations.0.hostname", privateDomain),
1404+
resource.TestCheckResourceAttr(name, "destinations.1.type", "public"),
1405+
resource.TestCheckResourceAttr(name, "destinations.1.uri", publicDomain),
1406+
),
1407+
},
1408+
{
1409+
// Ensures no diff on last plan
1410+
Config: testAccessApplicationWithDestinations(rnd, domain, cloudflare.AccountIdentifier(accountID)),
1411+
PlanOnly: true,
1412+
},
1413+
},
1414+
})
1415+
}
1416+
13631417
func TestAccCloudflareAccessApplicationMisconfiguredCORSCredentialsAllowingAllOrigins(t *testing.T) {
13641418
rnd := utils.GenerateRandomResourceName()
13651419
zone := os.Getenv("CLOUDFLARE_DOMAIN")
@@ -1419,6 +1473,14 @@ func testAccessApplicationWithInvalidSessionDuration(resourceID, zone, zoneID st
14191473
return acctest.LoadTestCase("accessapplicationwithinvalidsessionduration.tf", resourceID, zone, zoneID)
14201474
}
14211475

1476+
func testAccessApplicationWithInvalidPrivateDestination(resourceID, accountID string) string {
1477+
return acctest.LoadTestCase("accessapplicationconfiginvalidprivatedestination.tf", resourceID, accountID)
1478+
}
1479+
1480+
func testAccessApplicationWithDestinations(rnd string, domain string, identifier *cloudflare.ResourceContainer) string {
1481+
return acctest.LoadTestCase("accessapplicationconfigwithdestinations.tf", rnd, domain, identifier.Type, identifier.Identifier)
1482+
}
1483+
14221484
func testAccessApplicationMisconfiguredCORSAllowAllOriginsWithCredentials(resourceID, zone, zoneID string) string {
14231485
return acctest.LoadTestCase("accessapplicationmisconfiguredcorsallowalloriginswithcredentials.tf", resourceID, zone, zoneID)
14241486
}

internal/services/zero_trust_access_application/schema.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,36 +499,54 @@ func ResourceSchema(ctx context.Context) schema.Schema {
499499
"type": schema.StringAttribute{
500500
Description: `Available values: "public", "private".`,
501501
Optional: true,
502+
Computed: true,
503+
Default: stringdefault.StaticString("public"),
502504
Validators: []validator.String{
503505
stringvalidator.OneOfCaseInsensitive("public", "private"),
504506
},
505507
},
506508
"uri": schema.StringAttribute{
507509
Description: "The URI of the destination. Public destinations' URIs can include a domain and path with [wildcards](https://developers.cloudflare.com/cloudflare-one/policies/access/app-paths/).",
508510
Optional: true,
511+
Validators: []validator.String{
512+
customvalidator.RequiresOtherStringAttributeToNullOrBeOneOf(path.MatchRelative().AtParent().AtName("type"), "public"),
513+
},
509514
},
510515
"cidr": schema.StringAttribute{
511516
Description: "The CIDR range of the destination. Single IPs will be computed as /32.",
512517
Optional: true,
518+
Validators: []validator.String{
519+
customvalidator.RequiresOtherStringAttributeToBeOneOf(path.MatchRelative().AtParent().AtName("type"), "private"),
520+
},
513521
},
514522
"hostname": schema.StringAttribute{
515523
Description: "The hostname of the destination. Matches a valid SNI served by an HTTPS origin.",
516524
Optional: true,
525+
Validators: []validator.String{
526+
customvalidator.RequiresOtherStringAttributeToBeOneOf(path.MatchRelative().AtParent().AtName("type"), "private"),
527+
},
517528
},
518529
"l4_protocol": schema.StringAttribute{
519530
Description: "The L4 protocol of the destination. When omitted, both UDP and TCP traffic will match.\nAvailable values: \"tcp\", \"udp\".",
520531
Optional: true,
521532
Validators: []validator.String{
522533
stringvalidator.OneOfCaseInsensitive("tcp", "udp"),
534+
customvalidator.RequiresOtherStringAttributeToBeOneOf(path.MatchRelative().AtParent().AtName("type"), "private"),
523535
},
524536
},
525537
"port_range": schema.StringAttribute{
526538
Description: "The port range of the destination. Can be a single port or a range of ports. When omitted, all ports will match.",
527539
Optional: true,
540+
Validators: []validator.String{
541+
customvalidator.RequiresOtherStringAttributeToBeOneOf(path.MatchRelative().AtParent().AtName("type"), "private"),
542+
},
528543
},
529544
"vnet_id": schema.StringAttribute{
530545
Description: "The VNET ID to match the destination. When omitted, all VNETs will match.",
531546
Optional: true,
547+
Validators: []validator.String{
548+
customvalidator.RequiresOtherStringAttributeToBeOneOf(path.MatchRelative().AtParent().AtName("type"), "private"),
549+
},
532550
},
533551
},
534552
},
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
resource "cloudflare_zero_trust_access_application" "%[1]s" {
2+
account_id = "%[2]s"
3+
name = "%[1]s"
4+
type = "self_hosted"
5+
session_duration = "24h"
6+
auto_redirect_to_identity = false
7+
destinations = [
8+
{
9+
"hostname" : "abc.private"
10+
"port_range": "443"
11+
}
12+
]
13+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
resource "cloudflare_zero_trust_access_application" "%[1]s" {
2+
%[3]s_id = "%[4]s"
3+
name = "%[1]s"
4+
type = "self_hosted"
5+
session_duration = "24h"
6+
auto_redirect_to_identity = false
7+
destinations = [
8+
{
9+
"type" : "private",
10+
"hostname" : "%[1]s.private"
11+
"port_range": "443"
12+
},
13+
{
14+
"uri" : "d1.%[1]s.%[2]s",
15+
}
16+
]
17+
}

0 commit comments

Comments
 (0)