Skip to content

Commit

Permalink
Add support for Attack Challenge Mode (#181)
Browse files Browse the repository at this point in the history
  • Loading branch information
dglsparsons committed May 2, 2024
1 parent f30b68f commit 38c997e
Show file tree
Hide file tree
Showing 13 changed files with 865 additions and 0 deletions.
54 changes: 54 additions & 0 deletions client/attack_challenge_mode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package client

import (
"context"
"fmt"
)

type AttackChallengeMode struct {
ProjectID string `json:"projectId"`
TeamID string `json:"-"`
Enabled bool `json:"attackModeEnabled"`
}

func (c *Client) GetAttackChallengeMode(ctx context.Context, projectID, teamID string) (a AttackChallengeMode, err error) {
project, err := c.GetProject(ctx, projectID, teamID)
if err != nil {
return a, err
}
var enabled bool
if project.Security != nil {
enabled = project.Security.AttackModeEnabled
}
return AttackChallengeMode{
ProjectID: projectID,
TeamID: teamID,
Enabled: enabled,
}, err
}

func (c *Client) UpdateAttackChallengeMode(ctx context.Context, request AttackChallengeMode) (a AttackChallengeMode, err error) {
url := fmt.Sprintf("%s/security/attack-mode", c.baseURL)
if c.teamID(request.TeamID) != "" {
url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID))
}

payload := string(mustMarshal(request))
var res struct {
AttackModeEnabled bool `json:"attackModeEnabled"`
}
err = c.doRequest(clientRequest{
ctx: ctx,
method: "POST",
url: url,
body: payload,
}, &res)
if err != nil {
return a, err
}
return AttackChallengeMode{
ProjectID: request.ProjectID,
TeamID: request.TeamID,
Enabled: res.AttackModeEnabled,
}, err
}
5 changes: 5 additions & 0 deletions client/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,18 @@ type ProjectResponse struct {
DirectoryListing bool `json:"directoryListing"`
SkewProtectionMaxAge int `json:"skewProtectionMaxAge"`
GitComments *GitComments `json:"gitComments"`
Security *Security `json:"security"`
}

type GitComments struct {
OnCommit bool `json:"onCommit"`
OnPullRequest bool `json:"onPullRequest"`
}

type Security struct {
AttackModeEnabled bool `json:"attackModeEnabled"`
}

// GetProject retrieves information about an existing project from Vercel.
func (c *Client) GetProject(ctx context.Context, projectID, teamID string) (r ProjectResponse, err error) {
url := fmt.Sprintf("%s/v10/projects/%s", c.baseURL, projectID)
Expand Down
38 changes: 38 additions & 0 deletions docs/data-sources/attack_challenge_mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "vercel_attack_challenge_mode Data Source - terraform-provider-vercel"
subcategory: ""
description: |-
Provides an Attack Challenge Mode resource.
Attack Challenge Mode prevent malicious traffic by showing a verification challenge for every visitor.
---

# vercel_attack_challenge_mode (Data Source)

Provides an Attack Challenge Mode resource.

Attack Challenge Mode prevent malicious traffic by showing a verification challenge for every visitor.

## Example Usage

```terraform
data "vercel_attack_challenge_mode" "example" {
project_id = vercel_project.example.id
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `project_id` (String) The ID of the Project to adjust the CPU for.

### Optional

- `team_id` (String) The ID of the team the Project exists under. Required when configuring a team resource if a default team has not been set in the provider.

### Read-Only

- `enabled` (Boolean) Whether Attack Challenge Mode is enabled or not.
- `id` (String) The resource identifier.
54 changes: 54 additions & 0 deletions docs/resources/attack_challenge_mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "vercel_attack_challenge_mode Resource - terraform-provider-vercel"
subcategory: ""
description: |-
Provides an Attack Challenge Mode resource.
Attack Challenge Mode prevent malicious traffic by showing a verification challenge for every visitor.
---

# vercel_attack_challenge_mode (Resource)

Provides an Attack Challenge Mode resource.

Attack Challenge Mode prevent malicious traffic by showing a verification challenge for every visitor.

## Example Usage

```terraform
resource "vercel_project" "example" {
name = "example-project"
}
resource "vercel_attack_challenge_mode" "example" {
project_id = vercel_project.example.id
enabled = true
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `enabled` (Boolean) Whether Attack Challenge Mode is enabled or not.
- `project_id` (String) The ID of the Project to adjust the CPU for.

### Optional

- `team_id` (String) The ID of the team the Project exists under. Required when configuring a team resource if a default team has not been set in the provider.

### Read-Only

- `id` (String) The resource identifier.

## Import

Import is supported using the following syntax:

```shell
# You can import via the team_id and project_id.
# - team_id can be found in the team `settings` tab in the Vercel UI.
# - project_id can be found in the project `settings` tab in the Vercel UI.
terraform import vercel_attack_challenge_mode.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
data "vercel_attack_challenge_mode" "example" {
project_id = vercel_project.example.id
}
4 changes: 4 additions & 0 deletions examples/resources/vercel_attack_challenge_mode/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# You can import via the team_id and project_id.
# - team_id can be found in the team `settings` tab in the Vercel UI.
# - project_id can be found in the project `settings` tab in the Vercel UI.
terraform import vercel_attack_challenge_mode.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
8 changes: 8 additions & 0 deletions examples/resources/vercel_attack_challenge_mode/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
resource "vercel_project" "example" {
name = "example-project"
}

resource "vercel_attack_challenge_mode" "example" {
project_id = vercel_project.example.id
enabled = true
}
113 changes: 113 additions & 0 deletions vercel/data_source_attack_challenge_mode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package vercel

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/vercel/terraform-provider-vercel/client"
)

// Ensure the implementation satisfies the expected interfaces.
var (
_ datasource.DataSource = &attackChallengeModeDataSource{}
_ datasource.DataSourceWithConfigure = &attackChallengeModeDataSource{}
)

func newAttackChallengeModeDataSource() datasource.DataSource {
return &attackChallengeModeDataSource{}
}

type attackChallengeModeDataSource struct {
client *client.Client
}

func (d *attackChallengeModeDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_attack_challenge_mode"
}

func (d *attackChallengeModeDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(*client.Client)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}

d.client = client
}

func (r *attackChallengeModeDataSource) Schema(_ context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: `
Provides an Attack Challenge Mode resource.
Attack Challenge Mode prevent malicious traffic by showing a verification challenge for every visitor.`,
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The resource identifier.",
Computed: true,
},
"project_id": schema.StringAttribute{
Description: "The ID of the Project to adjust the CPU for.",
Required: true,
},
"team_id": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "The ID of the team the Project exists under. Required when configuring a team resource if a default team has not been set in the provider.",
},
"enabled": schema.BoolAttribute{
Computed: true,
Description: "Whether Attack Challenge Mode is enabled or not.",
},
},
}
}

func (d *attackChallengeModeDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var config AttackChallengeMode
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

out, err := d.client.GetAttackChallengeMode(ctx, config.ProjectID.ValueString(), config.TeamID.ValueString())
if client.NotFound(err) {
resp.State.RemoveResource(ctx)
return
}
if err != nil {
resp.Diagnostics.AddError(
"Error reading Attack Challenge Mode",
fmt.Sprintf("Could not get Attack Challenge Mode %s %s, unexpected error: %s",
config.TeamID.ValueString(),
config.ProjectID.ValueString(),
err,
),
)
return
}

result := responseToAttackChallengeMode(out)
tflog.Info(ctx, "read attack challenge mode", map[string]interface{}{
"team_id": result.TeamID.ValueString(),
"project_id": result.ProjectID.ValueString(),
})

diags = resp.State.Set(ctx, result)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
73 changes: 73 additions & 0 deletions vercel/data_source_attack_challenge_mode_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package vercel_test

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

func TestAcc_AttackChallengeModeDataSource(t *testing.T) {
name := acctest.RandString(16)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccAttackChallengeModeConfig(name, teamIDConfig()),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.vercel_attack_challenge_mode.never_enabled", "enabled", "false"),
resource.TestCheckResourceAttr("data.vercel_attack_challenge_mode.enabled", "enabled", "true"),
resource.TestCheckResourceAttr("data.vercel_attack_challenge_mode.disabled", "enabled", "false"),
),
},
},
})
}

func testAccAttackChallengeModeConfig(name, teamID string) string {
return fmt.Sprintf(`
resource "vercel_project" "never_enabled" {
name = "test-acc-%[1]s"
%[2]s
}
data "vercel_attack_challenge_mode" "never_enabled" {
project_id = vercel_project.never_enabled.id
%[2]s
}
resource "vercel_project" "enabled" {
name = "test-acc-%[1]s-enabled"
%[2]s
}
resource "vercel_attack_challenge_mode" "enabled" {
project_id = vercel_project.enabled.id
enabled = true
%[2]s
}
data "vercel_attack_challenge_mode" "enabled" {
project_id = vercel_attack_challenge_mode.enabled.project_id
%[2]s
}
resource "vercel_project" "disabled" {
name = "test-acc-%[1]s-disabled"
%[2]s
}
resource "vercel_attack_challenge_mode" "disabled" {
project_id = vercel_project.disabled.id
enabled = false
%[2]s
}
data "vercel_attack_challenge_mode" "disabled" {
project_id = vercel_attack_challenge_mode.disabled.project_id
%[2]s
}
`, name, teamID)
}
2 changes: 2 additions & 0 deletions vercel/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Use the navigation to the left to read about the available resources.
func (p *vercelProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
newAliasResource,
newAttackChallengeModeResource,
newDNSRecordResource,
newDeploymentResource,
newEdgeConfigResource,
Expand All @@ -69,6 +70,7 @@ func (p *vercelProvider) Resources(_ context.Context) []func() resource.Resource
func (p *vercelProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
newAliasDataSource,
newAttackChallengeModeDataSource,
newDeploymentDataSource,
newEdgeConfigDataSource,
newEdgeConfigSchemaDataSource,
Expand Down
Loading

0 comments on commit 38c997e

Please sign in to comment.