-
Notifications
You must be signed in to change notification settings - Fork 631
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4676 from mrusso19/add-new-lcc-rules-resource
Add Leaked Credential Check Rules resource
- Loading branch information
Showing
10 changed files
with
372 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:new-resource | ||
cloudflare_leaked_credential_check_rule | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
--- | ||
page_title: "cloudflare_leaked_credential_check_rule Resource - Cloudflare" | ||
subcategory: "" | ||
description: |- | ||
Provides a Cloudflare Leaked Credential Check Rule resource for managing user-defined Leaked Credential detection patterns within a specific zone. | ||
--- | ||
|
||
# cloudflare_leaked_credential_check_rule (Resource) | ||
|
||
Provides a Cloudflare Leaked Credential Check Rule resource for managing user-defined Leaked Credential detection patterns within a specific zone. | ||
|
||
## Example Usage | ||
|
||
```terraform | ||
# Enable the Leaked Credentials Check detection before trying | ||
# to add detections. | ||
resource "cloudflare_leaked_credential_check" "example" { | ||
zone_id = "399c6f4950c01a5a141b99ff7fbcbd8b" | ||
enabled = true | ||
} | ||
resource "cloudflare_leaked_credential_check_rule" "example" { | ||
zone_id = cloudflare_leaked_credential_check.example.zone_id | ||
username = "lookup_json_string(http.request.body.raw, \"user\")" | ||
password = "lookup_json_string(http.request.body.raw, \"pass\")" | ||
} | ||
``` | ||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- `password` (String) The ruleset expression to use in matching the password in a request | ||
- `username` (String) The ruleset expression to use in matching the username in a request. | ||
- `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_leaked_credential_check_rule.example <zone_id>/<resource_id> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
examples/resources/cloudflare_leaked_credential_check_rule/import.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
terraform import cloudflare_leaked_credential_check_rule.example <zone_id>/<resource_id> |
12 changes: 12 additions & 0 deletions
12
examples/resources/cloudflare_leaked_credential_check_rule/resource.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Enable the Leaked Credentials Check detection before trying | ||
# to add detections. | ||
resource "cloudflare_leaked_credential_check" "example" { | ||
zone_id = "399c6f4950c01a5a141b99ff7fbcbd8b" | ||
enabled = true | ||
} | ||
|
||
resource "cloudflare_leaked_credential_check_rule" "example" { | ||
zone_id = cloudflare_leaked_credential_check.example.zone_id | ||
username = "lookup_json_string(http.request.body.raw, \"user\")" | ||
password = "lookup_json_string(http.request.body.raw, \"pass\")" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
internal/framework/service/leaked_credential_check_rule/model.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package leaked_credential_check_rule | ||
|
||
import "github.com/hashicorp/terraform-plugin-framework/types" | ||
|
||
type LeakedCredentialCheckRulesModel struct { | ||
ZoneID types.String `tfsdk:"zone_id"` | ||
ID types.String `tfsdk:"id"` | ||
Username types.String `tfsdk:"username"` | ||
Password types.String `tfsdk:"password"` | ||
} |
161 changes: 161 additions & 0 deletions
161
internal/framework/service/leaked_credential_check_rule/resource.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
package leaked_credential_check_rule | ||
|
||
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 = &LeakedCredentialCheckRuleResource{} | ||
_ resource.ResourceWithImportState = &LeakedCredentialCheckRuleResource{} | ||
) | ||
|
||
func NewResource() resource.Resource { | ||
return &LeakedCredentialCheckRuleResource{} | ||
} | ||
|
||
type LeakedCredentialCheckRuleResource struct { | ||
client *muxclient.Client | ||
} | ||
|
||
func (r *LeakedCredentialCheckRuleResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { | ||
resp.TypeName = req.ProviderTypeName + "_leaked_credential_check_rule" | ||
} | ||
|
||
func (r *LeakedCredentialCheckRuleResource) 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 *LeakedCredentialCheckRuleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { | ||
var data LeakedCredentialCheckRulesModel | ||
diags := req.Plan.Get(ctx, &data) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
detection, err := r.client.V1.LeakedCredentialCheckCreateDetection(ctx, cloudflare.ZoneIdentifier(data.ZoneID.ValueString()), cloudflare.LeakedCredentialCheckCreateDetectionParams{ | ||
Username: data.Username.ValueString(), | ||
Password: data.Password.ValueString(), | ||
}) | ||
if err != nil { | ||
resp.Diagnostics.AddError("Error creating a user-defined detection patter for Leaked Credential Check", err.Error()) | ||
return | ||
} | ||
|
||
data.ID = types.StringValue(detection.ID) | ||
|
||
diags = resp.State.Set(ctx, &data) | ||
resp.Diagnostics.Append(diags...) | ||
} | ||
|
||
func (r *LeakedCredentialCheckRuleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { | ||
var state LeakedCredentialCheckRulesModel | ||
diags := req.State.Get(ctx, &state) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
zoneID := state.ZoneID.ValueString() | ||
var foundRule cloudflare.LeakedCredentialCheckDetectionEntry | ||
rules, err := r.client.V1.LeakedCredentialCheckListDetections(ctx, cloudflare.ZoneIdentifier(zoneID), cloudflare.LeakedCredentialCheckListDetectionsParams{}) | ||
if err != nil { | ||
resp.Diagnostics.AddError("Error listing Leaked Credential Check user-defined detection patterns", err.Error()) | ||
return | ||
} | ||
|
||
// leaked credentials doens't offer a single get operation so | ||
// loop until we find the matching ID. | ||
for _, rule := range rules { | ||
if rule.ID == state.ID.ValueString() { | ||
foundRule = rule | ||
break | ||
} | ||
} | ||
|
||
state.Password = types.StringValue(foundRule.Password) | ||
state.Username = types.StringValue(foundRule.Username) | ||
state.ID = types.StringValue(foundRule.ID) | ||
state.ZoneID = types.StringValue(zoneID) | ||
|
||
diags = resp.State.Set(ctx, &state) | ||
resp.Diagnostics.Append(diags...) | ||
} | ||
|
||
func (r *LeakedCredentialCheckRuleResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { | ||
var data LeakedCredentialCheckRulesModel | ||
diags := req.Plan.Get(ctx, &data) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
zoneID := cloudflare.ZoneIdentifier(data.ZoneID.ValueString()) | ||
_, err := r.client.V1.LeakedCredentialCheckUpdateDetection(ctx, zoneID, cloudflare.LeakedCredentialCheckUpdateDetectionParams{ | ||
LeakedCredentialCheckDetectionEntry: cloudflare.LeakedCredentialCheckDetectionEntry{ | ||
ID: data.ID.ValueString(), | ||
Username: data.Username.ValueString(), | ||
Password: data.Password.ValueString(), | ||
}, | ||
}) | ||
if err != nil { | ||
resp.Diagnostics.AddError("Error fetching Leaked Credential Check user-defined detection patterns", err.Error()) | ||
return | ||
} | ||
|
||
diags = resp.State.Set(ctx, &data) | ||
resp.Diagnostics.Append(diags...) | ||
} | ||
|
||
func (r *LeakedCredentialCheckRuleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { | ||
var state LeakedCredentialCheckRulesModel | ||
diags := req.State.Get(ctx, &state) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
zoneID := cloudflare.ZoneIdentifier(state.ZoneID.ValueString()) | ||
deleteParam := cloudflare.LeakedCredentialCheckDeleteDetectionParams{DetectionID: state.ID.ValueString()} | ||
_, err := r.client.V1.LeakedCredentialCheckDeleteDetection(ctx, zoneID, deleteParam) | ||
if err != nil { | ||
resp.Diagnostics.AddError("Error deleting a user-defined detection patter for Leaked Credential Check", err.Error()) | ||
return | ||
} | ||
} | ||
|
||
func (r *LeakedCredentialCheckRuleResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { | ||
idparts := strings.Split(req.ID, "/") | ||
if len(idparts) != 2 { | ||
resp.Diagnostics.AddError("error importing leaked credential detection", "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], | ||
)...) | ||
} |
102 changes: 102 additions & 0 deletions
102
internal/framework/service/leaked_credential_check_rule/resource_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package leaked_credential_check_rule_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_leaked_credential_check_rule", &resource.Sweeper{ | ||
Name: "cloudflare_leaked_credential_check_rule", | ||
F: testSweepCloudflareLCCRules, | ||
}) | ||
} | ||
|
||
func testSweepCloudflareLCCRules(r string) error { | ||
ctx := context.Background() | ||
client, clientErr := acctest.SharedV1Client() | ||
if clientErr != nil { | ||
tflog.Error(ctx, fmt.Sprintf("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 rules from API | ||
rules, err := client.LeakedCredentialCheckListDetections(ctx, cfv1.ZoneIdentifier(zoneID), cfv1.LeakedCredentialCheckListDetectionsParams{}) | ||
if err != nil { | ||
tflog.Error(ctx, fmt.Sprintf("Error fetching Leaked Credential Check user-defined detection patterns: %s", err)) | ||
return err | ||
} | ||
for _, rule := range rules { | ||
deleteParam := cfv1.LeakedCredentialCheckDeleteDetectionParams{DetectionID: rule.ID} | ||
_, err := client.LeakedCredentialCheckDeleteDetection(ctx, cfv1.ZoneIdentifier(zoneID), deleteParam) | ||
if err != nil { | ||
tflog.Error(ctx, fmt.Sprintf("Error deleting a user-defined detection patter for Leaked Credential Check: %s", err)) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func TestAccCloudflareLeakedCredentialCheckRule_Basic(t *testing.T) { | ||
rnd := utils.GenerateRandomResourceName() | ||
name := fmt.Sprintf("cloudflare_leaked_credential_check_rule.%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: testAccConfigAddHeader(rnd, zoneID, testAccLCCTwoSimpleRules(rnd)), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr(name+"_first", "zone_id", zoneID), | ||
resource.TestCheckResourceAttr(name+"_first", "username", "lookup_json_string(http.request.body.raw, \"user\")"), | ||
resource.TestCheckResourceAttr(name+"_first", "password", "lookup_json_string(http.request.body.raw, \"pass\")"), | ||
|
||
resource.TestCheckResourceAttr(name+"_second", "zone_id", zoneID), | ||
resource.TestCheckResourceAttr(name+"_second", "username", "lookup_json_string(http.request.body.raw, \"id\")"), | ||
resource.TestCheckResourceAttr(name+"_second", "password", "lookup_json_string(http.request.body.raw, \"secret\")"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccConfigAddHeader(name, zoneID, config string) string { | ||
header := fmt.Sprintf(` | ||
resource "cloudflare_leaked_credential_check" "%[1]s" { | ||
zone_id = "%[2]s" | ||
enabled = true | ||
}`, name, zoneID) | ||
return header + "\n" + config | ||
} | ||
|
||
func testAccLCCTwoSimpleRules(name string) string { | ||
return fmt.Sprintf(` | ||
resource "cloudflare_leaked_credential_check_rule" "%[1]s_first" { | ||
zone_id = cloudflare_leaked_credential_check.%[1]s.zone_id | ||
username = "lookup_json_string(http.request.body.raw, \"user\")" | ||
password = "lookup_json_string(http.request.body.raw, \"pass\")" | ||
} | ||
resource "cloudflare_leaked_credential_check_rule" "%[1]s_second" { | ||
zone_id = cloudflare_leaked_credential_check.%[1]s.zone_id | ||
username = "lookup_json_string(http.request.body.raw, \"id\")" | ||
password = "lookup_json_string(http.request.body.raw, \"secret\")" | ||
}`, name) | ||
} |
Oops, something went wrong.