From 38b862437cb2e5bc702d9661543d1a255d502d3d Mon Sep 17 00:00:00 2001 From: Daniel Harper <529730+djhworld@users.noreply.github.com> Date: Thu, 12 Oct 2023 12:00:08 +0100 Subject: [PATCH] APISHI-2362 resource/cloudflare_api_shield_schema_validation_settings Adds new resource `resource/cloudflare_api_shield_schema_validation_settings` --- .changelog/2841.txt | 3 + .../api_shield_schema_validation_settings.md | 37 ++++++ .../resource.tf | 5 + internal/sdkv2provider/provider.go | 1 + ...e_api_shield_schema_validation_settings.go | 122 ++++++++++++++++++ ..._shield_schema_validation_settings_test.go | 97 ++++++++++++++ ...e_api_shield_schema_validation_settings.go | 28 ++++ 7 files changed, 293 insertions(+) create mode 100644 .changelog/2841.txt create mode 100644 docs/resources/api_shield_schema_validation_settings.md create mode 100644 examples/resources/cloudflare_api_shield_schema_validation_settings/resource.tf create mode 100644 internal/sdkv2provider/resource_cloudflare_api_shield_schema_validation_settings.go create mode 100644 internal/sdkv2provider/resource_cloudflare_api_shield_schema_validation_settings_test.go create mode 100644 internal/sdkv2provider/schema_cloudflare_api_shield_schema_validation_settings.go diff --git a/.changelog/2841.txt b/.changelog/2841.txt new file mode 100644 index 00000000000..2ea29079ac1 --- /dev/null +++ b/.changelog/2841.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +cloudflare_api_shield_schema_validation_settings +``` diff --git a/docs/resources/api_shield_schema_validation_settings.md b/docs/resources/api_shield_schema_validation_settings.md new file mode 100644 index 00000000000..30efb5b1fcf --- /dev/null +++ b/docs/resources/api_shield_schema_validation_settings.md @@ -0,0 +1,37 @@ +--- +page_title: "cloudflare_api_shield_schema_validation_settings Resource - Cloudflare" +subcategory: "" +description: |- + Provides a resource to manage settings in API Shield Schema Validation 2.0. +--- + +# cloudflare_api_shield_schema_validation_settings (Resource) + +Provides a resource to manage settings in API Shield Schema Validation 2.0. + +## Example Usage + +```terraform +resource "cloudflare_api_shield_schema_validation_settings" "example" { + zone_id = "0da42c8d2132a9ddaf714f9e7c920711" + validation_default_mitigation_action = "log" + validation_override_mitigation_action = "none" # optional +} +``` + +## Schema + +### Required + +- `validation_default_mitigation_action` (String) The default mitigation action used when there is no mitigation action defined on the operation. +- `zone_id` (String) The zone identifier to target for the resource. **Modifying this attribute will force creation of a new resource.** + +### Optional + +- `validation_override_mitigation_action` (String) When set, this overrides both zone level and operation level mitigation actions. Defaults to `disable_override`. + +### Read-Only + +- `id` (String) The ID of this resource. + + diff --git a/examples/resources/cloudflare_api_shield_schema_validation_settings/resource.tf b/examples/resources/cloudflare_api_shield_schema_validation_settings/resource.tf new file mode 100644 index 00000000000..fc11af613f3 --- /dev/null +++ b/examples/resources/cloudflare_api_shield_schema_validation_settings/resource.tf @@ -0,0 +1,5 @@ +resource "cloudflare_api_shield_schema_validation_settings" "example" { + zone_id = "0da42c8d2132a9ddaf714f9e7c920711" + validation_default_mitigation_action = "log" + validation_override_mitigation_action = "none" # optional +} diff --git a/internal/sdkv2provider/provider.go b/internal/sdkv2provider/provider.go index 581bcd92f6f..04fa9242b5d 100644 --- a/internal/sdkv2provider/provider.go +++ b/internal/sdkv2provider/provider.go @@ -199,6 +199,7 @@ func New(version string) func() *schema.Provider { "cloudflare_api_shield": resourceCloudflareAPIShield(), "cloudflare_api_shield_operation": resourceCloudflareAPIShieldOperation(), "cloudflare_api_shield_schema": resourceCloudflareAPIShieldSchemas(), + "cloudflare_api_shield_schema_validation_settings": resourceCloudflareAPIShieldSchemaValidationSettings(), "cloudflare_api_token": resourceCloudflareApiToken(), "cloudflare_argo": resourceCloudflareArgo(), "cloudflare_authenticated_origin_pulls_certificate": resourceCloudflareAuthenticatedOriginPullsCertificate(), diff --git a/internal/sdkv2provider/resource_cloudflare_api_shield_schema_validation_settings.go b/internal/sdkv2provider/resource_cloudflare_api_shield_schema_validation_settings.go new file mode 100644 index 00000000000..17e92e50e8b --- /dev/null +++ b/internal/sdkv2provider/resource_cloudflare_api_shield_schema_validation_settings.go @@ -0,0 +1,122 @@ +package sdkv2provider + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/cloudflare/cloudflare-go" + "github.com/cloudflare/terraform-provider-cloudflare/internal/consts" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCloudflareAPIShieldSchemaValidationSettings() *schema.Resource { + return &schema.Resource{ + Schema: resourceCloudflareAPIShieldSchemaValidationSettingsSchema(), + CreateContext: resourceCloudflareAPIShieldSchemaValidationSettingsCreate, + ReadContext: resourceCloudflareAPIShieldSchemaValidationSettingsRead, + UpdateContext: resourceCloudflareAPIShieldSchemaValidationSettingsUpdate, + DeleteContext: resourceCloudflareAPIShieldSchemaValidationSettingsDelete, + Importer: &schema.ResourceImporter{ + StateContext: nil, + }, + Description: heredoc.Doc(` + Provides a resource to manage settings in API Shield Schema Validation 2.0. + `), + } +} + +func resourceCloudflareAPIShieldSchemaValidationSettingsCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return resourceCloudflareAPIShieldSchemaValidationSettingsUpdate(ctx, d, meta) +} + +func resourceCloudflareAPIShieldSchemaValidationSettingsUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*cloudflare.API) + zoneID := d.Get(consts.ZoneIDSchemaKey).(string) + + dm := d.Get("validation_default_mitigation_action").(string) + + var overrideAction *string + if oa, ok := d.GetOk("validation_override_mitigation_action"); ok { + overrideAction = cloudflare.StringPtr(oa.(string)) + } + + _, err := client.UpdateAPIShieldSchemaValidationSettings( + ctx, + cloudflare.ZoneIdentifier(zoneID), + cloudflare.UpdateAPIShieldSchemaValidationSettingsParams{ + DefaultMitigationAction: &dm, + OverrideMitigationAction: overrideAction, + }, + ) + + if err != nil { + return diag.FromErr(errors.Wrap(err, "failed to create API Shield Schema Validation Settings")) + } + + // Settings are configured at the zone level so using the zoneID as the ID + d.SetId(zoneID) + return resourceCloudflareAPIShieldSchemaValidationSettingsRead(ctx, d, meta) +} + +func resourceCloudflareAPIShieldSchemaValidationSettingsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*cloudflare.API) + zoneID := d.Get(consts.ZoneIDSchemaKey).(string) + + settings, err := client.GetAPIShieldSchemaValidationSettings( + ctx, + cloudflare.ZoneIdentifier(zoneID), + ) + + if err != nil { + return diag.FromErr(fmt.Errorf("failed to fetch API Shield Schema Validation Settings: %w", err)) + } + + if err := d.Set("validation_default_mitigation_action", settings.DefaultMitigationAction); err != nil { + return diag.FromErr(err) + } + + if settings.OverrideMitigationAction != nil { + if err := d.Set("validation_override_mitigation_action", settings.OverrideMitigationAction); err != nil { + return diag.FromErr(err) + } + } + + // Settings are configured at the zone level so using the zoneID as the ID + d.SetId(zoneID) + return nil +} + +func resourceCloudflareAPIShieldSchemaValidationSettingsDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*cloudflare.API) + zoneID := d.Get(consts.ZoneIDSchemaKey).(string) + + defaultSettings := cloudflareAPIShieldSchemaValidationSettingsDefault() + + // There is no DELETE endpoint for schema validation settings, + // so terraform should reset the state to default settings + _, err := client.UpdateAPIShieldSchemaValidationSettings( + ctx, + cloudflare.ZoneIdentifier(zoneID), + cloudflare.UpdateAPIShieldSchemaValidationSettingsParams{ + DefaultMitigationAction: &defaultSettings.DefaultMitigationAction, + OverrideMitigationAction: defaultSettings.OverrideMitigationAction, + }, + ) + if err != nil { + return diag.FromErr(fmt.Errorf("failed to delete API Shield Schema Validation Settings: %w", err)) + } + + return nil +} + +func cloudflareAPIShieldSchemaValidationSettingsDefault() *cloudflare.APIShieldSchemaValidationSettings { + disableOverride := "disable_override" + return &cloudflare.APIShieldSchemaValidationSettings{ + DefaultMitigationAction: "none", + OverrideMitigationAction: &disableOverride, + } +} diff --git a/internal/sdkv2provider/resource_cloudflare_api_shield_schema_validation_settings_test.go b/internal/sdkv2provider/resource_cloudflare_api_shield_schema_validation_settings_test.go new file mode 100644 index 00000000000..1f2c409ed5a --- /dev/null +++ b/internal/sdkv2provider/resource_cloudflare_api_shield_schema_validation_settings_test.go @@ -0,0 +1,97 @@ +package sdkv2provider + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/cloudflare/cloudflare-go" + "github.com/cloudflare/terraform-provider-cloudflare/internal/consts" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +func TestAccCloudflareAPIShieldSchemaValidationSettings_Create(t *testing.T) { + // Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the API token + // endpoint does not yet support the API tokens without an explicit scope. + if os.Getenv("CLOUDFLARE_API_TOKEN") != "" { + t.Setenv("CLOUDFLARE_API_TOKEN", "") + } + + rnd := generateRandomResourceName() + resourceID := "cloudflare_api_shield_schema_validation_settings." + rnd + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckAPIShieldSchemaValidationSettingsDelete, + Steps: []resource.TestStep{ + { + Config: testAccCloudflareAPIShieldSchemaValidationSettingsDefaultMitigationSet(rnd, zoneID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceID, consts.ZoneIDSchemaKey, zoneID), + resource.TestCheckResourceAttr(resourceID, "validation_default_mitigation_action", "log"), + // default + resource.TestCheckResourceAttr(resourceID, "validation_override_mitigation_action", "disable_override"), + ), + }, + { + Config: testAccCloudflareAPIShieldSchemaValidationSettingsAllMitigationsSet(rnd, zoneID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceID, consts.ZoneIDSchemaKey, zoneID), + resource.TestCheckResourceAttr(resourceID, "validation_default_mitigation_action", "block"), + resource.TestCheckResourceAttr(resourceID, "validation_override_mitigation_action", "none"), + ), + }, + }, + }) +} + +func testAccCheckAPIShieldSchemaValidationSettingsDelete(s *terraform.State) error { + client := testAccProvider.Meta().(*cloudflare.API) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "cloudflare_api_shield_schema_validation_settings" { + continue + } + + result, err := client.GetAPIShieldSchemaValidationSettings( + context.Background(), + cloudflare.ZoneIdentifier(rs.Primary.Attributes[consts.ZoneIDSchemaKey]), + ) + if err != nil { + return fmt.Errorf("encountered error getting schema validation settings: %w", err) + } + + if result.DefaultMitigationAction != cloudflareAPIShieldSchemaValidationSettingsDefault().DefaultMitigationAction { + return fmt.Errorf("expected validation_default_mitigation_action to be 'none' but got: %s", result.DefaultMitigationAction) + } + + if result.OverrideMitigationAction != nil { + return fmt.Errorf("expected validation_override_mitigation_action to be nil") + } + } + + return nil +} + +func testAccCloudflareAPIShieldSchemaValidationSettingsDefaultMitigationSet(resourceName, zone string) string { + return fmt.Sprintf(` + resource "cloudflare_api_shield_schema_validation_settings" "%[1]s" { + zone_id = "%[2]s" + validation_default_mitigation_action = "log" + } +`, resourceName, zone) +} + +func testAccCloudflareAPIShieldSchemaValidationSettingsAllMitigationsSet(resourceName, zone string) string { + return fmt.Sprintf(` + resource "cloudflare_api_shield_schema_validation_settings" "%[1]s" { + zone_id = "%[2]s" + validation_default_mitigation_action = "block" + validation_override_mitigation_action = "none" + } +`, resourceName, zone) +} diff --git a/internal/sdkv2provider/schema_cloudflare_api_shield_schema_validation_settings.go b/internal/sdkv2provider/schema_cloudflare_api_shield_schema_validation_settings.go new file mode 100644 index 00000000000..491ae65c6d8 --- /dev/null +++ b/internal/sdkv2provider/schema_cloudflare_api_shield_schema_validation_settings.go @@ -0,0 +1,28 @@ +package sdkv2provider + +import ( + "github.com/cloudflare/terraform-provider-cloudflare/internal/consts" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCloudflareAPIShieldSchemaValidationSettingsSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + consts.ZoneIDSchemaKey: { + Description: consts.ZoneIDSchemaDescription, + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "validation_default_mitigation_action": { + Description: "The default mitigation action used when there is no mitigation action defined on the operation", + Type: schema.TypeString, + Required: true, + }, + "validation_override_mitigation_action": { + Description: "When set, this overrides both zone level and operation level mitigation actions", + Type: schema.TypeString, + Optional: true, + Default: "disable_override", + }, + } +}