From 3824f2eb23aac7f26ae874842edb17289d6e1303 Mon Sep 17 00:00:00 2001 From: Tom Cinbis Date: Wed, 10 Apr 2024 09:03:17 +0000 Subject: [PATCH 1/3] feat: adding UpdateRulesetRule in rulesets.go --- rulesets.go | 23 +++++++++++++++ rulesets_test.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/rulesets.go b/rulesets.go index c8a476251a7..c5500ad789c 100644 --- a/rulesets.go +++ b/rulesets.go @@ -733,6 +733,10 @@ type UpdateRulesetParams struct { Rules []RulesetRule `json:"rules"` } +type UpdateRulesetRuleParams struct { + RulesetRule +} + type UpdateEntrypointRulesetParams struct { Phase string `json:"-"` Description string `json:"description,omitempty"` @@ -842,6 +846,25 @@ func (api *API) UpdateRuleset(ctx context.Context, rc *ResourceContainer, params return result.Result, nil } +func (api *API) UpdateRulesetRule(ctx context.Context, rc *ResourceContainer, rulesetID string, params UpdateRulesetRuleParams) (Ruleset, error) { + if params.ID == "" { + return Ruleset{}, ErrMissingResourceIdentifier + } + + uri := fmt.Sprintf("/%s/%s/rulesets/%s/rules/%s", rc.Level, rc.Identifier, rulesetID, params.ID) + res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, params) + if err != nil { + return Ruleset{}, err + } + + result := UpdateRulesetResponse{} + if err := json.Unmarshal(res, &result); err != nil { + return Ruleset{}, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return result.Result, nil +} + // GetEntrypointRuleset returns an entry point ruleset base on the phase. // // API reference: https://developers.cloudflare.com/api/operations/getAccountEntrypointRuleset diff --git a/rulesets_test.go b/rulesets_test.go index 91f432529ec..f55be029f92 100644 --- a/rulesets_test.go +++ b/rulesets_test.go @@ -864,3 +864,80 @@ func TestUpdateRuleset(t *testing.T) { assert.Equal(t, want, accountActual) } } + +func TestUpdateRulesetRule(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPatch, r.Method, "Expected method 'PUT', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprint(w, `{ + "result": { + "id": "2c0fc9fa937b11eaa1b71c4d701ab86e", + "name": "ruleset1", + "description": "Test Firewall Ruleset Update", + "kind": "root", + "version": "1", + "last_updated": "2020-12-02T20:24:07.776073Z", + "phase": "magic_transit", + "rules": [ + { + "id": "62449e2e0de149619edb35e59c10d802", + "version": "1", + "action": "skip", + "action_parameters":{ + "ruleset":"current" + }, + "expression": "udp.dstport in { 32768..65535 }", + "description": "Allow UDP Ephemeral Ports", + "last_updated": "2020-12-02T20:24:07.776073Z", + "ref": "72449e2e0de149619edb35e59c10d801", + "enabled": true + } + ] + }, + "success": true, + "errors": [], + "messages": [] + }`) + } + + mux.HandleFunc("/zones/"+testZoneID+"/rulesets/2c0fc9fa937b11eaa1b71c4d701ab86e/rules/62449e2e0de149619edb35e59c10d802", handler) + + lastUpdated, _ := time.Parse(time.RFC3339, "2020-12-02T20:24:07.776073Z") + + rule := RulesetRule{ + ID: "62449e2e0de149619edb35e59c10d802", + Version: StringPtr("1"), + Action: string(RulesetRuleActionSkip), + ActionParameters: &RulesetRuleActionParameters{ + Ruleset: "current", + }, + Expression: "udp.dstport in { 32768..65535 }", + Description: "Allow UDP Ephemeral Ports", + LastUpdated: &lastUpdated, + Ref: "72449e2e0de149619edb35e59c10d801", + Enabled: BoolPtr(true), + } + + updated := UpdateRulesetRuleParams{ + rule, + } + + want := Ruleset{ + ID: "2c0fc9fa937b11eaa1b71c4d701ab86e", + Name: "ruleset1", + Description: "Test Firewall Ruleset Update", + Kind: "root", + Version: StringPtr("1"), + LastUpdated: &lastUpdated, + Phase: string(RulesetPhaseMagicTransit), + Rules: []RulesetRule{rule}, + } + + zoneActual, err := client.UpdateRulesetRule(context.Background(), ZoneIdentifier(testZoneID), "2c0fc9fa937b11eaa1b71c4d701ab86e", updated) + if assert.NoError(t, err) { + assert.Equal(t, want, zoneActual) + } +} From c348c3bb23ed343fa82f787fecf94a5c9828279f Mon Sep 17 00:00:00 2001 From: Tom Cinbis Date: Wed, 10 Apr 2024 13:52:07 +0000 Subject: [PATCH 2/3] feat: adding CreateRulesetRule in rulesets.go --- rulesets.go | 27 +++++++++++++++ rulesets_test.go | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/rulesets.go b/rulesets.go index c5500ad789c..1b99ed8bab1 100644 --- a/rulesets.go +++ b/rulesets.go @@ -733,6 +733,10 @@ type UpdateRulesetParams struct { Rules []RulesetRule `json:"rules"` } +type CreateRulesetRuleParams struct { + RulesetRule +} + type UpdateRulesetRuleParams struct { RulesetRule } @@ -846,6 +850,29 @@ func (api *API) UpdateRuleset(ctx context.Context, rc *ResourceContainer, params return result.Result, nil } +// CreateRulesetRule creates a new ruleset rule. +// +// API reference: https://developers.cloudflare.com/api/operations/createAccountRulesetRule +// API reference: https://developers.cloudflare.com/api/operations/createZoneRulesetRule +func (api *API) CreateRulesetRule(ctx context.Context, rc *ResourceContainer, rulesetID string, params CreateRulesetRuleParams) (Ruleset, error) { + uri := fmt.Sprintf("/%s/%s/rulesets/%s/rules", rc.Level, rc.Identifier, rulesetID) + res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params) + if err != nil { + return Ruleset{}, err + } + + result := CreateRulesetResponse{} + if err := json.Unmarshal(res, &result); err != nil { + return Ruleset{}, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return result.Result, nil +} + +// UpdateRulesetRule updates a ruleset rule based on the ruleset and rule ID. +// +// API reference: https://developers.cloudflare.com/api/operations/updateAccountRulesetRule +// API reference: https://developers.cloudflare.com/api/operations/updateZoneRulesetRule func (api *API) UpdateRulesetRule(ctx context.Context, rc *ResourceContainer, rulesetID string, params UpdateRulesetRuleParams) (Ruleset, error) { if params.ID == "" { return Ruleset{}, ErrMissingResourceIdentifier diff --git a/rulesets_test.go b/rulesets_test.go index f55be029f92..d43cd8b8846 100644 --- a/rulesets_test.go +++ b/rulesets_test.go @@ -903,6 +903,7 @@ func TestUpdateRulesetRule(t *testing.T) { }`) } + mux.HandleFunc("/accounts/"+testAccountID+"/rulesets/2c0fc9fa937b11eaa1b71c4d701ab86e/rules/62449e2e0de149619edb35e59c10d802", handler) mux.HandleFunc("/zones/"+testZoneID+"/rulesets/2c0fc9fa937b11eaa1b71c4d701ab86e/rules/62449e2e0de149619edb35e59c10d802", handler) lastUpdated, _ := time.Parse(time.RFC3339, "2020-12-02T20:24:07.776073Z") @@ -940,4 +941,90 @@ func TestUpdateRulesetRule(t *testing.T) { if assert.NoError(t, err) { assert.Equal(t, want, zoneActual) } + + accountActual, err := client.UpdateRulesetRule(context.Background(), AccountIdentifier(testAccountID), "2c0fc9fa937b11eaa1b71c4d701ab86e", updated) + if assert.NoError(t, err) { + assert.Equal(t, want, accountActual) + } +} + +func TestCreateRulesetRule(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprint(w, `{ + "result": { + "id": "2c0fc9fa937b11eaa1b71c4d701ab86e", + "name": "my example ruleset", + "description": "Test magic transit ruleset", + "kind": "root", + "version": "1", + "last_updated": "2020-12-02T20:24:07.776073Z", + "phase": "magic_transit", + "rules": [ + { + "id": "62449e2e0de149619edb35e59c10d801", + "version": "1", + "action": "skip", + "action_parameters":{ + "ruleset":"current" + }, + "expression": "tcp.dstport in { 32768..65535 }", + "description": "Allow TCP Ephemeral Ports", + "last_updated": "2020-12-02T20:24:07.776073Z", + "ref": "72449e2e0de149619edb35e59c10d801", + "enabled": true + } + ] + }, + "success": true, + "errors": [], + "messages": [] + }`) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/rulesets/2c0fc9fa937b11eaa1b71c4d701ab86e/rules", handler) + mux.HandleFunc("/zones/"+testZoneID+"/rulesets/2c0fc9fa937b11eaa1b71c4d701ab86e/rules", handler) + + lastUpdated, _ := time.Parse(time.RFC3339, "2020-12-02T20:24:07.776073Z") + + rule := RulesetRule{ + ID: "62449e2e0de149619edb35e59c10d801", + Version: StringPtr("1"), + Action: string(RulesetRuleActionSkip), + ActionParameters: &RulesetRuleActionParameters{ + Ruleset: "current", + }, + Expression: "tcp.dstport in { 32768..65535 }", + Description: "Allow TCP Ephemeral Ports", + LastUpdated: &lastUpdated, + Ref: "72449e2e0de149619edb35e59c10d801", + Enabled: BoolPtr(true), + } + + newRule := CreateRulesetRuleParams{rule} + + want := Ruleset{ + ID: "2c0fc9fa937b11eaa1b71c4d701ab86e", + Name: "my example ruleset", + Description: "Test magic transit ruleset", + Kind: "root", + Version: StringPtr("1"), + LastUpdated: &lastUpdated, + Phase: string(RulesetPhaseMagicTransit), + Rules: []RulesetRule{rule}, + } + + zoneActual, err := client.CreateRulesetRule(context.Background(), ZoneIdentifier(testZoneID), "2c0fc9fa937b11eaa1b71c4d701ab86e", newRule) + if assert.NoError(t, err) { + assert.Equal(t, want, zoneActual) + } + + accountActual, err := client.CreateRulesetRule(context.Background(), AccountIdentifier(testAccountID), "2c0fc9fa937b11eaa1b71c4d701ab86e", newRule) + if assert.NoError(t, err) { + assert.Equal(t, want, accountActual) + } } From 1ff428b1727f0e177d21e4df85349f8458778dfa Mon Sep 17 00:00:00 2001 From: Tom Cinbis Date: Wed, 10 Apr 2024 21:18:33 +0200 Subject: [PATCH 3/3] docs: add changelog entry for #1744 --- .changelog/1744.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/1744.txt diff --git a/.changelog/1744.txt b/.changelog/1744.txt new file mode 100644 index 00000000000..5dcb794cda7 --- /dev/null +++ b/.changelog/1744.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +rulesets: Add support for create/update calls for individual ruleset rules +```