From 60aae15e8943ce723963c6c29c8bf13f53dfcc17 Mon Sep 17 00:00:00 2001 From: Michele Russo Date: Mon, 9 Dec 2024 16:23:21 +0100 Subject: [PATCH 01/12] Add content scanning expression resource --- .../content_scanning_expression/model.go | 9 + .../content_scanning_expression/resource.go | 196 ++++++++++++++++++ .../resource_test.go | 104 ++++++++++ .../content_scanning_expression/schema.go | 29 +++ 4 files changed, 338 insertions(+) create mode 100644 internal/framework/service/content_scanning_expression/model.go create mode 100644 internal/framework/service/content_scanning_expression/resource.go create mode 100644 internal/framework/service/content_scanning_expression/resource_test.go create mode 100644 internal/framework/service/content_scanning_expression/schema.go diff --git a/internal/framework/service/content_scanning_expression/model.go b/internal/framework/service/content_scanning_expression/model.go new file mode 100644 index 0000000000..100019c401 --- /dev/null +++ b/internal/framework/service/content_scanning_expression/model.go @@ -0,0 +1,9 @@ +package content_scanning_expression + +import "github.com/hashicorp/terraform-plugin-framework/types" + +type ContentScanningExpressionModel struct { + ZoneID types.String `tfsdk:"zone_id"` + ID types.String `tfsdk:"id"` + Payload types.String `tfsdk:"payload"` +} diff --git a/internal/framework/service/content_scanning_expression/resource.go b/internal/framework/service/content_scanning_expression/resource.go new file mode 100644 index 0000000000..089d9d6ef3 --- /dev/null +++ b/internal/framework/service/content_scanning_expression/resource.go @@ -0,0 +1,196 @@ +package content_scanning_expression + +import ( + "context" + "fmt" + "strings" + + "github.com/cloudflare/cloudflare-go" + "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/muxclient" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var ( + _ resource.Resource = &ContentScanningExpressionResource{} + _ resource.ResourceWithImportState = &ContentScanningExpressionResource{} +) + +func NewResource() resource.Resource { + return &ContentScanningExpressionResource{} +} + +type ContentScanningExpressionResource struct { + client *muxclient.Client +} + +func (r *ContentScanningExpressionResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_content_scanning_expression" +} + +func (r *ContentScanningExpressionResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*muxclient.Client) + + if !ok { + resp.Diagnostics.AddError( + "unexpected resource configure type", + fmt.Sprintf("Expected *muxclient.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = client +} + +func (r *ContentScanningExpressionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data ContentScanningExpressionModel + diags := req.Plan.Get(ctx, &data) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + params := cloudflare.ContentScanningAddCustomExpressionsParams{ + Payloads: []cloudflare.ContentScanningCustomPayload{ + { + Payload: data.Payload.ValueString(), + }, + }, + } + expressions, err := r.client.V1.ContentScanningAddCustomExpressions(ctx, cloudflare.ZoneIdentifier(data.ZoneID.ValueString()), params) + if err != nil { + resp.Diagnostics.AddError("Error creating a custom scan expression for Content Scanning", err.Error()) + return + } + + // The Add API returns a list of all exiting custom scan expression + // loop until we find the newly created one, matching on payload + // payload uniqueness is enforced by the service + for _, exp := range expressions { + if exp.Payload == data.Payload.ValueString() { + data.ID = types.StringValue(exp.ID) + break + } + } + + diags = resp.State.Set(ctx, &data) + resp.Diagnostics.Append(diags...) +} + +func (r *ContentScanningExpressionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ContentScanningExpressionModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + zoneID := state.ZoneID.ValueString() + var foundExp cloudflare.ContentScanningCustomExpression + expressions, err := r.client.V1.ContentScanningListCustomExpressions(ctx, cloudflare.ZoneIdentifier(zoneID), cloudflare.ContentScanningListCustomExpressionsParams{}) + if err != nil { + resp.Diagnostics.AddError("Error listing customs scan expressions for Content Scanning", err.Error()) + return + } + + // content scanning doens't offer a single get operation so + // loop until we find the matching ID. + for _, exp := range expressions { + if exp.ID == state.ID.ValueString() { + foundExp = exp + break + } + } + + state.ID = types.StringValue(foundExp.ID) + state.Payload = types.StringValue(foundExp.Payload) + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) +} + +func (r *ContentScanningExpressionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ContentScanningExpressionModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + var state ContentScanningExpressionModel + diags = req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + zoneID := cloudflare.ZoneIdentifier(plan.ZoneID.ValueString()) + plan.ID = state.ID + + // API does not offer an update operation so we use delete/create + delParams := cloudflare.ContentScanningDeleteCustomExpressionsParams{ID: plan.ID.ValueString()} + _, err := r.client.V1.ContentScanningDeleteCustomExpression(ctx, zoneID, delParams) + if err != nil { + resp.Diagnostics.AddError("Error in Update while deleting custom scan expression for Content Scanning", err.Error()) + return + } + createParams := cloudflare.ContentScanningAddCustomExpressionsParams{ + Payloads: []cloudflare.ContentScanningCustomPayload{ + { + Payload: plan.Payload.ValueString(), + }, + }, + } + expressions, err := r.client.V1.ContentScanningAddCustomExpressions(ctx, zoneID, createParams) + if err != nil { + resp.Diagnostics.AddError("Error in Update while creating a custom scan expression for Content Scanning", err.Error()) + return + } + + // The Add API returns a list of all exiting custom scan expression + // loop until we find the newly created one, matching on payload + // payload uniqueness is enforced by the service + for _, exp := range expressions { + if exp.Payload == plan.Payload.ValueString() { + plan.ID = types.StringValue(exp.ID) + break + } + } + + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) +} + +func (r *ContentScanningExpressionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state ContentScanningExpressionModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + zoneID := cloudflare.ZoneIdentifier(state.ZoneID.ValueString()) + deleteParam := cloudflare.ContentScanningDeleteCustomExpressionsParams{ID: state.ID.ValueString()} + _, err := r.client.V1.ContentScanningDeleteCustomExpression(ctx, zoneID, deleteParam) + if err != nil { + resp.Diagnostics.AddError("Error deleting custom scan expression for Content Scanning", err.Error()) + return + } +} + +func (r *ContentScanningExpressionResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + idparts := strings.Split(req.ID, "/") + if len(idparts) != 2 { + resp.Diagnostics.AddError("error importing content scanning custom expression", "invalid ID specified. Please specify the ID as \"zone_id/resource_id\"") + return + } + resp.Diagnostics.Append(resp.State.SetAttribute( + ctx, path.Root("zone_id"), idparts[0], + )...) + resp.Diagnostics.Append(resp.State.SetAttribute( + ctx, path.Root("id"), idparts[1], + )...) +} diff --git a/internal/framework/service/content_scanning_expression/resource_test.go b/internal/framework/service/content_scanning_expression/resource_test.go new file mode 100644 index 0000000000..4b74ec74ca --- /dev/null +++ b/internal/framework/service/content_scanning_expression/resource_test.go @@ -0,0 +1,104 @@ +package content_scanning_expression_test + +import ( + "context" + "errors" + "fmt" + "os" + "testing" + + cfv1 "github.com/cloudflare/cloudflare-go" + "github.com/cloudflare/terraform-provider-cloudflare/internal/acctest" + "github.com/cloudflare/terraform-provider-cloudflare/internal/utils" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func init() { + resource.AddTestSweepers("cloudflare_content_scanning_expression", &resource.Sweeper{ + Name: "cloudflare_content_scanning_expression", + F: testSweepCloudflareCSExpression, + }) +} + +func testSweepCloudflareCSExpression(r string) error { + ctx := context.Background() + client, clientErr := acctest.SharedV1Client() + if clientErr != nil { + tflog.Error(ctx, fmt.Sprintf("Sweeper: failed to create Cloudflare client: %s", clientErr)) + } + + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + if zoneID == "" { + return errors.New("CLOUDFLARE_ZONE_ID must be set") + } + // fetch existing expressions from API + expressions, err := client.ContentScanningListCustomExpressions(ctx, cfv1.ZoneIdentifier(zoneID), cfv1.ContentScanningListCustomExpressionsParams{}) + if err != nil { + tflog.Error(ctx, fmt.Sprintf("Sweeper: error listing customs scan expressions for Content Scanning: %s", err)) + return err + } + for _, exp := range expressions { + deleteParam := cfv1.ContentScanningDeleteCustomExpressionsParams{ID: exp.ID} + _, err := client.ContentScanningDeleteCustomExpression(ctx, cfv1.ZoneIdentifier(zoneID), deleteParam) + if err != nil { + tflog.Error(ctx, fmt.Sprintf("Sweeper: error deleting custom scan expression for Content Scanning: %s", err)) + } + } + + return nil +} + +func TestAccCloudflareContentScanningExpression_Basic(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + name := fmt.Sprintf("cloudflare_content_scanning_expression.%s", rnd) + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.TestAccPreCheck(t) + }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccBasicConfig(rnd, zoneID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name+"_first", "zone_id", zoneID), + resource.TestCheckResourceAttr(name+"_first", "payload", "lookup_json_string(http.request.body.raw, \"file\")"), + + resource.TestCheckResourceAttr(name+"_second", "zone_id", zoneID), + resource.TestCheckResourceAttr(name+"_second", "payload", "lookup_json_string(http.request.body.raw, \"document\")"), + ), + }, + { + Config: testAccBasicConfigChange(rnd, zoneID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name+"_second", "zone_id", zoneID), + resource.TestCheckResourceAttr(name+"_second", "payload", "lookup_json_string(http.request.body.raw, \"txt\")"), + ), + }, + }, + }) +} + +func testAccBasicConfig(name, zoneID string) string { + return fmt.Sprintf(` + resource "cloudflare_content_scanning_expression" "%[1]s_first" { + zone_id = "%[2]s" + payload = "lookup_json_string(http.request.body.raw, \"file\")" + } + + resource "cloudflare_content_scanning_expression" "%[1]s_second" { + zone_id = "%[2]s" + payload = "lookup_json_string(http.request.body.raw, \"document\")" + }`, name, zoneID) +} + +func testAccBasicConfigChange(name, zoneID string) string { + return fmt.Sprintf(` + resource "cloudflare_content_scanning_expression" "%[1]s_second" { + zone_id = "%[2]s" + payload = "lookup_json_string(http.request.body.raw, \"txt\")" + }`, name, zoneID) +} diff --git a/internal/framework/service/content_scanning_expression/schema.go b/internal/framework/service/content_scanning_expression/schema.go new file mode 100644 index 0000000000..398426d18b --- /dev/null +++ b/internal/framework/service/content_scanning_expression/schema.go @@ -0,0 +1,29 @@ +package content_scanning_expression + +import ( + "context" + + "github.com/cloudflare/terraform-provider-cloudflare/internal/consts" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" +) + +func (r *ContentScanningExpressionResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Provides a Cloudflare Content Scanning Expression resource for managing custom scan expression within a specific zone.", + Attributes: map[string]schema.Attribute{ + consts.IDSchemaKey: schema.StringAttribute{ + Description: consts.IDSchemaDescription, + Computed: true, + }, + consts.ZoneIDSchemaKey: schema.StringAttribute{ + Description: consts.ZoneIDSchemaDescription, + Required: true, + }, + "payload": schema.StringAttribute{ + Description: "Custom scan expression to tell the content scanner where to find the content objects.", + Required: true, + }, + }, + } +} From 0092caaa934741a3f0f8469aa60ea5df5ac4d7fe Mon Sep 17 00:00:00 2001 From: Michele Russo Date: Mon, 9 Dec 2024 16:24:09 +0100 Subject: [PATCH 02/12] Add content scanning expression examples --- .../import.sh | 1 + .../resource.tf | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 examples/resources/cloudflare_content_scanning_expression/import.sh create mode 100644 examples/resources/cloudflare_content_scanning_expression/resource.tf diff --git a/examples/resources/cloudflare_content_scanning_expression/import.sh b/examples/resources/cloudflare_content_scanning_expression/import.sh new file mode 100644 index 0000000000..3591b3ef76 --- /dev/null +++ b/examples/resources/cloudflare_content_scanning_expression/import.sh @@ -0,0 +1 @@ +terraform import cloudflare_content_scanning_expression.example / diff --git a/examples/resources/cloudflare_content_scanning_expression/resource.tf b/examples/resources/cloudflare_content_scanning_expression/resource.tf new file mode 100644 index 0000000000..3faf65ca79 --- /dev/null +++ b/examples/resources/cloudflare_content_scanning_expression/resource.tf @@ -0,0 +1,15 @@ +# Enable Content Scanning before trying to add custom scan expressions +resource "cloudflare_content_scanning" "example" { + zone_id = "399c6f4950c01a5a141b99ff7fbcbd8b" + enabled = true +} + +resource "cloudflare_content_scanning_expression" "first_example" { + zone_id = cloudflare_content_scanning.example.zone_id + payload = "lookup_json_string(http.request.body.raw, \"file\")" +} + +resource "cloudflare_content_scanning_expression" "second_example" { + zone_id = cloudflare_content_scanning.example.zone_id + payload = "lookup_json_string(http.request.body.raw, \"document\")" +} From 1d90f26ef220fa7f0a0c3d4d28e8febd75da789d Mon Sep 17 00:00:00 2001 From: Michele Russo Date: Mon, 9 Dec 2024 16:24:29 +0100 Subject: [PATCH 03/12] Add content scanning expression to provider --- internal/framework/provider/provider.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/framework/provider/provider.go b/internal/framework/provider/provider.go index 5330e91b58..b38a367b93 100644 --- a/internal/framework/provider/provider.go +++ b/internal/framework/provider/provider.go @@ -18,6 +18,7 @@ import ( "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/access_mutual_tls_hostname_settings" "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/api_token_permissions_groups" "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/cloud_connector_rules" + "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/content_scanning_expression" "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/d1" "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/dcv_delegation" "github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/dlp_datasets" @@ -395,6 +396,7 @@ func (p *CloudflareProvider) Resources(ctx context.Context) []func() resource.Re zero_trust_infrastructure_access_target.NewResource, leaked_credential_check.NewResource, leaked_credential_check_rule.NewResource, + content_scanning_expression.NewResource, } } From 5bfc95022b387d9bdf7df06becb07397efa4dd8f Mon Sep 17 00:00:00 2001 From: Michele Russo Date: Mon, 9 Dec 2024 16:24:39 +0100 Subject: [PATCH 04/12] `make docs` --- docs/resources/content_scanning_expression.md | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 docs/resources/content_scanning_expression.md diff --git a/docs/resources/content_scanning_expression.md b/docs/resources/content_scanning_expression.md new file mode 100644 index 0000000000..0828c46b66 --- /dev/null +++ b/docs/resources/content_scanning_expression.md @@ -0,0 +1,49 @@ +--- +page_title: "cloudflare_content_scanning_expression Resource - Cloudflare" +subcategory: "" +description: |- + Provides a Cloudflare Content Scanning Expression resource for managing custom scan expression within a specific zone. +--- + +# cloudflare_content_scanning_expression (Resource) + +Provides a Cloudflare Content Scanning Expression resource for managing custom scan expression within a specific zone. + +## Example Usage + +```terraform +# Enable Content Scanning before trying to add custom scan expressions +resource "cloudflare_content_scanning" "example" { + zone_id = "399c6f4950c01a5a141b99ff7fbcbd8b" + enabled = true +} + +resource "cloudflare_content_scanning_expression" "first_example" { + zone_id = cloudflare_content_scanning.example.zone_id + payload = "lookup_json_string(http.request.body.raw, \"file\")" +} + +resource "cloudflare_content_scanning_expression" "second_example" { + zone_id = cloudflare_content_scanning.example.zone_id + payload = "lookup_json_string(http.request.body.raw, \"document\")" +} +``` + +## Schema + +### Required + +- `payload` (String) Custom scan expression to tell the content scanner where to find the content objects. +- `zone_id` (String) The zone identifier to target for the resource. + +### Read-Only + +- `id` (String) The identifier of this resource. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import cloudflare_content_scanning_expression.example / +``` From c3048c6f1d2a975465eadacd3a9ae178830d9194 Mon Sep 17 00:00:00 2001 From: Michele Russo Date: Mon, 9 Dec 2024 16:30:32 +0100 Subject: [PATCH 05/12] Add changelog --- .changelog/4734.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/4734.txt diff --git a/.changelog/4734.txt b/.changelog/4734.txt new file mode 100644 index 0000000000..223d86cc92 --- /dev/null +++ b/.changelog/4734.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +cloudflare_content_scanning_expression +``` \ No newline at end of file From b14193819db9543ee5f05b83c3b4fe24e49362a9 Mon Sep 17 00:00:00 2001 From: Kyle Hiller Date: Tue, 10 Dec 2024 13:11:25 -0500 Subject: [PATCH 06/12] AUTH-6145 support multi-valued + service token authentication for SCIM provisioning to access applications --- .changelog/4743.txt | 3 + ...urce_cloudflare_access_application_test.go | 192 ++++-------------- .../schema_cloudflare_access_application.go | 151 ++++++++------ 3 files changed, 134 insertions(+), 212 deletions(-) create mode 100644 .changelog/4743.txt diff --git a/.changelog/4743.txt b/.changelog/4743.txt new file mode 100644 index 0000000000..5d6ea14b94 --- /dev/null +++ b/.changelog/4743.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/access_application: support multi-valued + Access service token authentication for SCIM provisioning to Access applications +``` diff --git a/internal/sdkv2provider/resource_cloudflare_access_application_test.go b/internal/sdkv2provider/resource_cloudflare_access_application_test.go index d220fe29e2..64b43713c6 100644 --- a/internal/sdkv2provider/resource_cloudflare_access_application_test.go +++ b/internal/sdkv2provider/resource_cloudflare_access_application_test.go @@ -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() { @@ -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) @@ -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), @@ -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\""), @@ -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) @@ -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" @@ -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" @@ -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" @@ -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 { @@ -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" { diff --git a/internal/sdkv2provider/schema_cloudflare_access_application.go b/internal/sdkv2provider/schema_cloudflare_access_application.go index e5e8967b8d..684e7ffa76 100644 --- a/internal/sdkv2provider/schema_cloudflare_access_application.go +++ b/internal/sdkv2provider/schema_cloudflare_access_application.go @@ -706,80 +706,65 @@ func resourceCloudflareAccessApplicationSchema() map[string]*schema.Schema { Type: schema.TypeList, Optional: true, Description: "Attributes for configuring HTTP Basic, OAuth Bearer token, or OAuth 2 authentication schemes for SCIM provisioning to an application.", - MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ // Common Attributes "scheme": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.StringInSlice([]string{"httpbasic", "oauthbearertoken", "oauth2"}, false), + ValidateFunc: validation.StringInSlice([]string{"httpbasic", "oauthbearertoken", "oauth2", "access_service_token"}, false), Description: "The authentication scheme to use when making SCIM requests to this application.", }, // HTTP Basic Authentication Attributes "user": { - Type: schema.TypeString, - Optional: true, - RequiredWith: []string{"scim_config.0.authentication.0.password"}, - ConflictsWith: []string{"scim_config.0.authentication.0.token", "scim_config.0.authentication.0.client_id", "scim_config.0.authentication.0.client_secret", "scim_config.0.authentication.0.authorization_url", "scim_config.0.authentication.0.token_url", "scim_config.0.authentication.0.scopes"}, - Description: "User name used to authenticate with the remote SCIM service.", + Type: schema.TypeString, + Optional: true, + Description: "User name used to authenticate with the remote SCIM service.", }, "password": { - Type: schema.TypeString, - Optional: true, - RequiredWith: []string{"scim_config.0.authentication.0.user"}, - ConflictsWith: []string{"scim_config.0.authentication.0.token", "scim_config.0.authentication.0.client_id", "scim_config.0.authentication.0.client_secret", "scim_config.0.authentication.0.authorization_url", "scim_config.0.authentication.0.token_url", "scim_config.0.authentication.0.scopes"}, + Type: schema.TypeString, + Optional: true, StateFunc: func(val interface{}) string { return CONCEALED_STRING }, }, // OAuth Bearer Token Authentication Attributes "token": { - Type: schema.TypeString, - Optional: true, - Description: "Token used to authenticate with the remote SCIM service.", - ConflictsWith: []string{"scim_config.0.authentication.0.user", "scim_config.0.authentication.0.password", "scim_config.0.authentication.0.client_id", "scim_config.0.authentication.0.client_secret", "scim_config.0.authentication.0.authorization_url", "scim_config.0.authentication.0.token_url", "scim_config.0.authentication.0.scopes"}, + Type: schema.TypeString, + Optional: true, + Description: "Token used to authenticate with the remote SCIM service.", StateFunc: func(val interface{}) string { return CONCEALED_STRING }, }, // OAuth 2 Authentication Attributes "client_id": { - Type: schema.TypeString, - Optional: true, - Description: "Client ID used to authenticate when generating a token for authenticating with the remote SCIM service.", - RequiredWith: []string{"scim_config.0.authentication.0.client_secret", "scim_config.0.authentication.0.authorization_url", "scim_config.0.authentication.0.token_url"}, - ConflictsWith: []string{"scim_config.0.authentication.0.user", "scim_config.0.authentication.0.password", "scim_config.0.authentication.0.token"}, + Type: schema.TypeString, + Optional: true, + Description: "Client ID used to authenticate when generating a token for authenticating with the remote SCIM service.", }, "client_secret": { - Type: schema.TypeString, - Optional: true, - Description: "Secret used to authenticate when generating a token for authenticating with the remove SCIM service.", - RequiredWith: []string{"scim_config.0.authentication.0.client_id", "scim_config.0.authentication.0.authorization_url", "scim_config.0.authentication.0.token_url"}, - ConflictsWith: []string{"scim_config.0.authentication.0.user", "scim_config.0.authentication.0.password", "scim_config.0.authentication.0.token"}, + Type: schema.TypeString, + Optional: true, + Description: "Secret used to authenticate when generating a token for authenticating with the remove SCIM service.", StateFunc: func(val interface{}) string { return CONCEALED_STRING }, }, "authorization_url": { - Type: schema.TypeString, - Optional: true, - Description: "URL used to generate the auth code used during token generation.", - RequiredWith: []string{"scim_config.0.authentication.0.client_secret", "scim_config.0.authentication.0.client_id", "scim_config.0.authentication.0.token_url"}, - ConflictsWith: []string{"scim_config.0.authentication.0.user", "scim_config.0.authentication.0.password", "scim_config.0.authentication.0.token"}, + Type: schema.TypeString, + Optional: true, + Description: "URL used to generate the auth code used during token generation.", }, "token_url": { - Type: schema.TypeString, - Optional: true, - Description: "URL used to generate the token used to authenticate with the remote SCIM service.", - RequiredWith: []string{"scim_config.0.authentication.0.client_secret", "scim_config.0.authentication.0.authorization_url", "scim_config.0.authentication.0.client_id"}, - ConflictsWith: []string{"scim_config.0.authentication.0.user", "scim_config.0.authentication.0.password", "scim_config.0.authentication.0.token"}, + Type: schema.TypeString, + Optional: true, + Description: "URL used to generate the token used to authenticate with the remote SCIM service.", }, "scopes": { - Type: schema.TypeSet, - Description: "The authorization scopes to request when generating the token used to authenticate with the remove SCIM service.", - Optional: true, - ConflictsWith: []string{"scim_config.0.authentication.0.user", "scim_config.0.authentication.0.password", "scim_config.0.authentication.0.token"}, + Type: schema.TypeSet, + Description: "The authorization scopes to request when generating the token used to authenticate with the remove SCIM service.", + Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, @@ -1210,37 +1195,48 @@ func convertScimConfigMappingsSchemaToStruct(mappingData map[string]interface{}) func convertScimConfigAuthenticationSchemaToStruct(d *schema.ResourceData) *cloudflare.AccessApplicationScimAuthenticationJson { auth := new(cloudflare.AccessApplicationScimAuthenticationJson) - - if _, ok := d.GetOk("scim_config.0.authentication"); ok { - scheme := cloudflare.AccessApplicationScimAuthenticationScheme(d.Get("scim_config.0.authentication.0.scheme").(string)) + multi := new(cloudflare.AccessApplicationMultipleScimAuthentication) + auth.Value = multi + + keyFmt := "scim_config.0.authentication.%d" + i := 0 + for _, ok := d.GetOk(fmt.Sprintf(keyFmt, i)); ok; _, ok = d.GetOk(fmt.Sprintf(keyFmt, i)) { + key := fmt.Sprintf(keyFmt, i) + scheme := cloudflare.AccessApplicationScimAuthenticationScheme(d.Get(key + ".scheme").(string)) switch scheme { case cloudflare.AccessApplicationScimAuthenticationSchemeHttpBasic: base := &cloudflare.AccessApplicationScimAuthenticationHttpBasic{ - User: d.Get("scim_config.0.authentication.0.user").(string), - Password: d.Get("scim_config.0.authentication.0.password").(string), + User: d.Get(key + ".user").(string), + Password: d.Get(key + ".password").(string), } base.Scheme = scheme - auth.Value = base - break + *multi = append(*multi, &cloudflare.AccessApplicationScimAuthenticationSingleJSON{Value: base}) case cloudflare.AccessApplicationScimAuthenticationSchemeOauthBearerToken: base := &cloudflare.AccessApplicationScimAuthenticationOauthBearerToken{ - Token: d.Get("scim_config.0.authentication.0.token").(string), + Token: d.Get(key + ".token").(string), } base.Scheme = scheme - auth.Value = base - break + *multi = append(*multi, &cloudflare.AccessApplicationScimAuthenticationSingleJSON{Value: base}) case cloudflare.AccessApplicationScimAuthenticationSchemeOauth2: base := &cloudflare.AccessApplicationScimAuthenticationOauth2{ - ClientID: d.Get("scim_config.0.authentication.0.client_id").(string), - ClientSecret: d.Get("scim_config.0.authentication.0.client_secret").(string), - AuthorizationURL: d.Get("scim_config.0.authentication.0.authorization_url").(string), - TokenURL: d.Get("scim_config.0.authentication.0.token_url").(string), - Scopes: expandInterfaceToStringList(d.Get("scim_config.0.authentication.0.scopes").(*schema.Set).List()), + ClientID: d.Get(key + ".client_id").(string), + ClientSecret: d.Get(key + ".client_secret").(string), + AuthorizationURL: d.Get(key + ".authorization_url").(string), + TokenURL: d.Get(key + ".token_url").(string), + Scopes: expandInterfaceToStringList(d.Get(key + ".scopes").(*schema.Set).List()), + } + base.Scheme = scheme + *multi = append(*multi, &cloudflare.AccessApplicationScimAuthenticationSingleJSON{Value: base}) + case cloudflare.AccessApplicationScimAuthenticationAccessServiceToken: + base := &cloudflare.AccessApplicationScimAuthenticationServiceToken{ + ClientID: d.Get(key + ".client_id").(string), + ClientSecret: d.Get(key + ".client_secret").(string), } base.Scheme = scheme - auth.Value = base - break + *multi = append(*multi, &cloudflare.AccessApplicationScimAuthenticationSingleJSON{Value: base}) } + + i++ } return auth @@ -1445,12 +1441,13 @@ func convertScimConfigStructToSchema(scimConfig *cloudflare.AccessApplicationSCI return []interface{}{} } + auth := convertScimConfigAuthenticationStructToSchema(scimConfig.Authentication) config := map[string]interface{}{ "enabled": scimConfig.Enabled, "remote_uri": scimConfig.RemoteURI, "idp_uid": scimConfig.IdPUID, "deactivate_on_delete": cloudflare.Bool(scimConfig.DeactivateOnDelete), - "authentication": convertScimConfigAuthenticationStructToSchema(scimConfig.Authentication), + "authentication": auth, "mappings": convertScimConfigMappingsStructsToSchema(scimConfig.Mappings), } @@ -1464,11 +1461,17 @@ func convertScimConfigAuthenticationStructToSchema(scimAuth *cloudflare.AccessAp auth := map[string]interface{}{} switch t := scimAuth.Value.(type) { + case *cloudflare.AccessApplicationMultipleScimAuthentication: + vals := []interface{}{} + for _, authn := range *t { + vals = append(vals, convertScimConfigSingleAuthentiationToSchema(&cloudflare.AccessApplicationScimAuthenticationJson{Value: authn.Value})) + } + + return vals case *cloudflare.AccessApplicationScimAuthenticationHttpBasic: auth["scheme"] = t.Scheme auth["user"] = t.User auth["password"] = t.Password - case *cloudflare.AccessApplicationScimAuthenticationOauthBearerToken: auth["scheme"] = t.Scheme auth["token"] = t.Token @@ -1479,11 +1482,43 @@ func convertScimConfigAuthenticationStructToSchema(scimAuth *cloudflare.AccessAp auth["authorization_url"] = t.AuthorizationURL auth["token_url"] = t.TokenURL auth["scopes"] = t.Scopes + case *cloudflare.AccessApplicationScimAuthenticationServiceToken: + auth["scheme"] = t.Scheme + auth["client_id"] = t.ClientID + auth["client_secret"] = t.ClientSecret } return []interface{}{auth} } +func convertScimConfigSingleAuthentiationToSchema(scimAuth *cloudflare.AccessApplicationScimAuthenticationJson) interface{} { + auth := map[string]interface{}{} + + switch t := scimAuth.Value.(type) { + case *cloudflare.AccessApplicationScimAuthenticationHttpBasic: + auth["scheme"] = t.Scheme + auth["user"] = t.User + auth["password"] = t.Password + + case *cloudflare.AccessApplicationScimAuthenticationOauthBearerToken: + auth["scheme"] = t.Scheme + auth["token"] = t.Token + case *cloudflare.AccessApplicationScimAuthenticationOauth2: + auth["scheme"] = t.Scheme + auth["client_id"] = t.ClientID + auth["client_secret"] = t.ClientSecret + auth["authorization_url"] = t.AuthorizationURL + auth["token_url"] = t.TokenURL + auth["scopes"] = t.Scopes + case *cloudflare.AccessApplicationScimAuthenticationServiceToken: + auth["scheme"] = t.Scheme + auth["client_id"] = t.ClientID + auth["client_secret"] = t.ClientSecret + } + + return auth +} + func convertScimConfigMappingsStructsToSchema(mappingsData []*cloudflare.AccessApplicationScimMapping) []interface{} { mappings := []interface{}{} From ceca12d4f1e359e436471d62832a8aa997b28bf5 Mon Sep 17 00:00:00 2001 From: guineveresaenger Date: Fri, 20 Dec 2024 12:53:33 -0800 Subject: [PATCH 07/12] Add extra nil check on ServeStale.DisableStaleWhileUpdating --- internal/framework/service/rulesets/resource.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/framework/service/rulesets/resource.go b/internal/framework/service/rulesets/resource.go index 84902ef3dc..6de78078a0 100644 --- a/internal/framework/service/rulesets/resource.go +++ b/internal/framework/service/rulesets/resource.go @@ -623,9 +623,11 @@ func toRulesetResourceModel(ctx context.Context, zoneID, accountID basetypes.Str } if ruleResponse.ActionParameters.ServeStale != nil { - rule.ActionParameters[0].ServeStale = []*ActionParameterServeStaleModel{{ - DisableStaleWhileUpdating: types.BoolValue(*ruleResponse.ActionParameters.ServeStale.DisableStaleWhileUpdating), - }} + if ruleResponse.ActionParameters.ServeStale.DisableStaleWhileUpdating != nil { + rule.ActionParameters[0].ServeStale = []*ActionParameterServeStaleModel{{ + DisableStaleWhileUpdating: types.BoolValue(*ruleResponse.ActionParameters.ServeStale.DisableStaleWhileUpdating), + }} + } } if ruleResponse.ActionParameters.FromList != nil { From 7f3cd670248ca22b1015c2d8ab161e07f36edf2b Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Thu, 26 Dec 2024 13:03:14 +1100 Subject: [PATCH 08/12] resource/cloudflare_teams_location: remove unusable `policy_ids` This field was removed from the product and API long ago and no longer does anything. It should have been deprecated long ago. Seeing how it's no longer usable, remove it from the schema to prevent confusion. --- .changelog/4817.txt | 3 +++ docs/resources/teams_location.md | 1 - docs/resources/zero_trust_dns_location.md | 1 - internal/sdkv2provider/resource_cloudflare_teams_location.go | 3 --- internal/sdkv2provider/schema_cloudflare_teams_location.go | 5 ----- 5 files changed, 3 insertions(+), 10 deletions(-) create mode 100644 .changelog/4817.txt diff --git a/.changelog/4817.txt b/.changelog/4817.txt new file mode 100644 index 0000000000..1405728ab7 --- /dev/null +++ b/.changelog/4817.txt @@ -0,0 +1,3 @@ +```release-note:note +resource/cloudflare_teams_location: remove unusable `policy_ids` attribute +``` diff --git a/docs/resources/teams_location.md b/docs/resources/teams_location.md index 7574ed4e25..fa7cf986c2 100644 --- a/docs/resources/teams_location.md +++ b/docs/resources/teams_location.md @@ -50,7 +50,6 @@ resource "cloudflare_teams_location" "example" { - `id` (String) The ID of this resource. - `ip` (String) Client IP address. - `ipv4_destination` (String) IP to direct all IPv4 DNS queries to. -- `policy_ids` (List of String) ### Nested Schema for `networks` diff --git a/docs/resources/zero_trust_dns_location.md b/docs/resources/zero_trust_dns_location.md index 7d8d806280..1b71ebc83b 100644 --- a/docs/resources/zero_trust_dns_location.md +++ b/docs/resources/zero_trust_dns_location.md @@ -50,7 +50,6 @@ resource "cloudflare_zero_trust_dns_location" "example" { - `id` (String) The ID of this resource. - `ip` (String) Client IP address. - `ipv4_destination` (String) IP to direct all IPv4 DNS queries to. -- `policy_ids` (List of String) ### Nested Schema for `networks` diff --git a/internal/sdkv2provider/resource_cloudflare_teams_location.go b/internal/sdkv2provider/resource_cloudflare_teams_location.go index 8fdb131eb1..1ca7fdef4c 100644 --- a/internal/sdkv2provider/resource_cloudflare_teams_location.go +++ b/internal/sdkv2provider/resource_cloudflare_teams_location.go @@ -68,9 +68,6 @@ func resourceCloudflareTeamsLocationRead(ctx context.Context, d *schema.Resource if err := d.Set("networks", flattenTeamsLocationNetworks(location.Networks)); err != nil { return diag.FromErr(fmt.Errorf("error parsing Location networks")) } - if err := d.Set("policy_ids", location.PolicyIDs); err != nil { - return diag.FromErr(fmt.Errorf("error parsing Location policy IDs")) - } if err := d.Set("ip", location.Ip); err != nil { return diag.FromErr(fmt.Errorf("error parsing Location IP")) } diff --git a/internal/sdkv2provider/schema_cloudflare_teams_location.go b/internal/sdkv2provider/schema_cloudflare_teams_location.go index 2d24e4cdf9..08334f0abf 100644 --- a/internal/sdkv2provider/schema_cloudflare_teams_location.go +++ b/internal/sdkv2provider/schema_cloudflare_teams_location.go @@ -45,11 +45,6 @@ func resourceCloudflareTeamsLocationSchema() map[string]*schema.Schema { Optional: true, Description: "Indicator that this location needs to resolve EDNS queries.", }, - "policy_ids": { - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - Computed: true, - }, "ip": { Type: schema.TypeString, Computed: true, From ff0305abcfe02dbb6d26785cf4dcbb25f3f30743 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Thu, 26 Dec 2024 13:39:09 +1100 Subject: [PATCH 09/12] add changelog entry --- .changelog/4814.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/4814.txt diff --git a/.changelog/4814.txt b/.changelog/4814.txt new file mode 100644 index 0000000000..d037303bca --- /dev/null +++ b/.changelog/4814.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/cloudflare_ruleset: handle when `disable_stale_while_updating` is an empty object but not nil +``` From b292458f57c32638ff574c839cf9ff593be4c865 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Thu, 26 Dec 2024 13:49:33 +1100 Subject: [PATCH 10/12] make docs --- docs/resources/access_application.md | 18 +++++++++--------- docs/resources/cloud_connector_rules.md | 2 +- .../resources/zero_trust_access_application.md | 18 +++++++++--------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/resources/access_application.md b/docs/resources/access_application.md index f86241e53d..e481556f54 100644 --- a/docs/resources/access_application.md +++ b/docs/resources/access_application.md @@ -281,7 +281,7 @@ Required: Optional: -- `authentication` (Block List, Max: 1) Attributes for configuring HTTP Basic, OAuth Bearer token, or OAuth 2 authentication schemes for SCIM provisioning to an application. (see [below for nested schema](#nestedblock--scim_config--authentication)) +- `authentication` (Block List) Attributes for configuring HTTP Basic, OAuth Bearer token, or OAuth 2 authentication schemes for SCIM provisioning to an application. (see [below for nested schema](#nestedblock--scim_config--authentication)) - `deactivate_on_delete` (Boolean) If false, propagates DELETE requests to the target application for SCIM resources. If true, sets 'active' to false on the SCIM resource. Note: Some targets do not support DELETE operations. - `enabled` (Boolean) Whether SCIM provisioning is turned on for this application. - `mappings` (Block List) A list of mappings to apply to SCIM resources before provisioning them in this application. These can transform or filter the resources to be provisioned. (see [below for nested schema](#nestedblock--scim_config--mappings)) @@ -295,14 +295,14 @@ Required: Optional: -- `authorization_url` (String) URL used to generate the auth code used during token generation. Required when using `scim_config.0.authentication.0.client_secret`, `scim_config.0.authentication.0.client_id`, `scim_config.0.authentication.0.token_url`. Conflicts with `scim_config.0.authentication.0.user`, `scim_config.0.authentication.0.password`, `scim_config.0.authentication.0.token`. -- `client_id` (String) Client ID used to authenticate when generating a token for authenticating with the remote SCIM service. Required when using `scim_config.0.authentication.0.client_secret`, `scim_config.0.authentication.0.authorization_url`, `scim_config.0.authentication.0.token_url`. Conflicts with `scim_config.0.authentication.0.user`, `scim_config.0.authentication.0.password`, `scim_config.0.authentication.0.token`. -- `client_secret` (String) Secret used to authenticate when generating a token for authenticating with the remove SCIM service. Required when using `scim_config.0.authentication.0.client_id`, `scim_config.0.authentication.0.authorization_url`, `scim_config.0.authentication.0.token_url`. Conflicts with `scim_config.0.authentication.0.user`, `scim_config.0.authentication.0.password`, `scim_config.0.authentication.0.token`. -- `password` (String) Required when using `scim_config.0.authentication.0.user`. Conflicts with `scim_config.0.authentication.0.token`, `scim_config.0.authentication.0.client_id`, `scim_config.0.authentication.0.client_secret`, `scim_config.0.authentication.0.authorization_url`, `scim_config.0.authentication.0.token_url`, `scim_config.0.authentication.0.scopes`. -- `scopes` (Set of String) The authorization scopes to request when generating the token used to authenticate with the remove SCIM service. Conflicts with `scim_config.0.authentication.0.user`, `scim_config.0.authentication.0.password`, `scim_config.0.authentication.0.token`. -- `token` (String) Token used to authenticate with the remote SCIM service. Conflicts with `scim_config.0.authentication.0.user`, `scim_config.0.authentication.0.password`, `scim_config.0.authentication.0.client_id`, `scim_config.0.authentication.0.client_secret`, `scim_config.0.authentication.0.authorization_url`, `scim_config.0.authentication.0.token_url`, `scim_config.0.authentication.0.scopes`. -- `token_url` (String) URL used to generate the token used to authenticate with the remote SCIM service. Required when using `scim_config.0.authentication.0.client_secret`, `scim_config.0.authentication.0.authorization_url`, `scim_config.0.authentication.0.client_id`. Conflicts with `scim_config.0.authentication.0.user`, `scim_config.0.authentication.0.password`, `scim_config.0.authentication.0.token`. -- `user` (String) User name used to authenticate with the remote SCIM service. Required when using `scim_config.0.authentication.0.password`. Conflicts with `scim_config.0.authentication.0.token`, `scim_config.0.authentication.0.client_id`, `scim_config.0.authentication.0.client_secret`, `scim_config.0.authentication.0.authorization_url`, `scim_config.0.authentication.0.token_url`, `scim_config.0.authentication.0.scopes`. +- `authorization_url` (String) URL used to generate the auth code used during token generation. +- `client_id` (String) Client ID used to authenticate when generating a token for authenticating with the remote SCIM service. +- `client_secret` (String) Secret used to authenticate when generating a token for authenticating with the remove SCIM service. +- `password` (String) +- `scopes` (Set of String) The authorization scopes to request when generating the token used to authenticate with the remove SCIM service. +- `token` (String) Token used to authenticate with the remote SCIM service. +- `token_url` (String) URL used to generate the token used to authenticate with the remote SCIM service. +- `user` (String) User name used to authenticate with the remote SCIM service. diff --git a/docs/resources/cloud_connector_rules.md b/docs/resources/cloud_connector_rules.md index e4d2ee0cae..49f53fbf13 100644 --- a/docs/resources/cloud_connector_rules.md +++ b/docs/resources/cloud_connector_rules.md @@ -2,7 +2,7 @@ page_title: "cloudflare_cloud_connector_rules Resource - Cloudflare" subcategory: "" description: |- - The Cloud Connector Rules resource allows you to create and manage cloud connector rules for a zone. + The Cloud Connector Rules https://developers.cloudflare.com/rules/cloud-connector/ resource allows you to create and manage cloud connector rules for a zone. --- # cloudflare_cloud_connector_rules (Resource) diff --git a/docs/resources/zero_trust_access_application.md b/docs/resources/zero_trust_access_application.md index c2762f49c6..1c508626fd 100644 --- a/docs/resources/zero_trust_access_application.md +++ b/docs/resources/zero_trust_access_application.md @@ -262,7 +262,7 @@ Required: Optional: -- `authentication` (Block List, Max: 1) Attributes for configuring HTTP Basic, OAuth Bearer token, or OAuth 2 authentication schemes for SCIM provisioning to an application. (see [below for nested schema](#nestedblock--scim_config--authentication)) +- `authentication` (Block List) Attributes for configuring HTTP Basic, OAuth Bearer token, or OAuth 2 authentication schemes for SCIM provisioning to an application. (see [below for nested schema](#nestedblock--scim_config--authentication)) - `deactivate_on_delete` (Boolean) If false, propagates DELETE requests to the target application for SCIM resources. If true, sets 'active' to false on the SCIM resource. Note: Some targets do not support DELETE operations. - `enabled` (Boolean) Whether SCIM provisioning is turned on for this application. - `mappings` (Block List) A list of mappings to apply to SCIM resources before provisioning them in this application. These can transform or filter the resources to be provisioned. (see [below for nested schema](#nestedblock--scim_config--mappings)) @@ -276,14 +276,14 @@ Required: Optional: -- `authorization_url` (String) URL used to generate the auth code used during token generation. Required when using `scim_config.0.authentication.0.client_secret`, `scim_config.0.authentication.0.client_id`, `scim_config.0.authentication.0.token_url`. Conflicts with `scim_config.0.authentication.0.user`, `scim_config.0.authentication.0.password`, `scim_config.0.authentication.0.token`. -- `client_id` (String) Client ID used to authenticate when generating a token for authenticating with the remote SCIM service. Required when using `scim_config.0.authentication.0.client_secret`, `scim_config.0.authentication.0.authorization_url`, `scim_config.0.authentication.0.token_url`. Conflicts with `scim_config.0.authentication.0.user`, `scim_config.0.authentication.0.password`, `scim_config.0.authentication.0.token`. -- `client_secret` (String) Secret used to authenticate when generating a token for authenticating with the remove SCIM service. Required when using `scim_config.0.authentication.0.client_id`, `scim_config.0.authentication.0.authorization_url`, `scim_config.0.authentication.0.token_url`. Conflicts with `scim_config.0.authentication.0.user`, `scim_config.0.authentication.0.password`, `scim_config.0.authentication.0.token`. -- `password` (String) Required when using `scim_config.0.authentication.0.user`. Conflicts with `scim_config.0.authentication.0.token`, `scim_config.0.authentication.0.client_id`, `scim_config.0.authentication.0.client_secret`, `scim_config.0.authentication.0.authorization_url`, `scim_config.0.authentication.0.token_url`, `scim_config.0.authentication.0.scopes`. -- `scopes` (Set of String) The authorization scopes to request when generating the token used to authenticate with the remove SCIM service. Conflicts with `scim_config.0.authentication.0.user`, `scim_config.0.authentication.0.password`, `scim_config.0.authentication.0.token`. -- `token` (String) Token used to authenticate with the remote SCIM service. Conflicts with `scim_config.0.authentication.0.user`, `scim_config.0.authentication.0.password`, `scim_config.0.authentication.0.client_id`, `scim_config.0.authentication.0.client_secret`, `scim_config.0.authentication.0.authorization_url`, `scim_config.0.authentication.0.token_url`, `scim_config.0.authentication.0.scopes`. -- `token_url` (String) URL used to generate the token used to authenticate with the remote SCIM service. Required when using `scim_config.0.authentication.0.client_secret`, `scim_config.0.authentication.0.authorization_url`, `scim_config.0.authentication.0.client_id`. Conflicts with `scim_config.0.authentication.0.user`, `scim_config.0.authentication.0.password`, `scim_config.0.authentication.0.token`. -- `user` (String) User name used to authenticate with the remote SCIM service. Required when using `scim_config.0.authentication.0.password`. Conflicts with `scim_config.0.authentication.0.token`, `scim_config.0.authentication.0.client_id`, `scim_config.0.authentication.0.client_secret`, `scim_config.0.authentication.0.authorization_url`, `scim_config.0.authentication.0.token_url`, `scim_config.0.authentication.0.scopes`. +- `authorization_url` (String) URL used to generate the auth code used during token generation. +- `client_id` (String) Client ID used to authenticate when generating a token for authenticating with the remote SCIM service. +- `client_secret` (String) Secret used to authenticate when generating a token for authenticating with the remove SCIM service. +- `password` (String) +- `scopes` (Set of String) The authorization scopes to request when generating the token used to authenticate with the remove SCIM service. +- `token` (String) Token used to authenticate with the remote SCIM service. +- `token_url` (String) URL used to generate the token used to authenticate with the remote SCIM service. +- `user` (String) User name used to authenticate with the remote SCIM service. From d42d64b752d476fd4e95477d36a1e03da0a0ef6e Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Thu, 26 Dec 2024 13:50:10 +1100 Subject: [PATCH 11/12] generate changelog --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67a986f4ff..efe021366a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ ## 4.49.0 (Unreleased) +NOTES: + +* resource/cloudflare_teams_location: remove unusable `policy_ids` attribute ([#4817](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4817)) + +FEATURES: + +* **New Resource:** `cloudflare_content_scanning_expression` ([#4734](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4734)) + +ENHANCEMENTS: + +* resource/access_application: support multi-valued + Access service token authentication for SCIM provisioning to Access applications ([#4743](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4743)) + +BUG FIXES: + +* resource/cloudflare_ruleset: handle when `disable_stale_while_updating` is an empty object but not nil ([#4814](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4814)) + +DEPENDENCIES: + +* provider: bump github.com/cloudflare/cloudflare-go from 0.111.0 to 0.112.0 ([#4803](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4803)) +* provider: bump github.com/hashicorp/terraform-plugin-framework-validators from 0.15.0 to 0.16.0 ([#4762](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4762)) +* provider: bump golang.org/x/crypto from 0.21.0 to 0.31.0 in /tools ([#4755](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4755)) +* provider: bump golang.org/x/crypto from 0.30.0 to 0.31.0 ([#4756](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4756)) +* provider: bump golang.org/x/net from 0.32.0 to 0.33.0 ([#4802](https://github.com/cloudflare/terraform-provider-cloudflare/issues/4802)) + ## 4.48.0 (December 11th, 2024) NOTES: From 7bf273839a631a53354a2e8df7735703174e50a4 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Thu, 26 Dec 2024 13:51:11 +1100 Subject: [PATCH 12/12] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efe021366a..2fb4638ef8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 4.49.0 (Unreleased) +## 4.50.0 (Unreleased) + +## 4.49.0 (December 25th, 2025) NOTES: