Skip to content

Commit

Permalink
DXE-3169 Merge pull request #197 from akamai/release/v7.5.0
Browse files Browse the repository at this point in the history
DXE-3169 Release/v7.5.0
  • Loading branch information
dawiddzhafarov committed Nov 28, 2023
2 parents bb2c04f + 696bec8 commit 8f8b577
Show file tree
Hide file tree
Showing 8 changed files with 469 additions and 55 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# EDGEGRID GOLANG RELEASE NOTES

## 7.5.0 (November 28, 2023)

#### FEATURES/ENHANCEMENTS:

* APPSEC
* Added `ASNControls` field to `UpdateIPGeoRequest` and `IPGeoFirewall` structs to support firewall blocking by ASN client lists

* BOTMAN
* Added API support for Custom Code - read and update

## 7.4.0 (October 24, 2023)

#### FEATURES/ENHANCEMENTS:
Expand All @@ -12,7 +22,7 @@

* IAM
* Phone number is no longer required for IAM user for `CreateUser` and `UpdateUserInfo` methods

## 7.3.0 (September 19, 2023)

#### FEATURES/ENHANCEMENTS:
Expand Down
9 changes: 8 additions & 1 deletion pkg/appsec/ip_geo.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ type (
BlockedIPNetworkLists *IPGeoNetworkLists `json:"blockedIPNetworkLists,omitempty"`
}

// IPGeoIPControls is used to specify IP or GEO network lists to be blocked or allowed.
// IPGeoASNControls is used to specify ASN network lists to be blocked.
IPGeoASNControls struct {
BlockedIPNetworkLists *IPGeoNetworkLists `json:"blockedIPNetworkLists,omitempty"`
}

// IPGeoIPControls is used to specify IP, GEO or ASN network lists to be blocked or allowed.
IPGeoIPControls struct {
AllowedIPNetworkLists *IPGeoNetworkLists `json:"allowedIPNetworkLists,omitempty"`
BlockedIPNetworkLists *IPGeoNetworkLists `json:"blockedIPNetworkLists,omitempty"`
Expand All @@ -59,6 +64,7 @@ type (
Block string `json:"block"`
GeoControls *IPGeoGeoControls `json:"geoControls,omitempty"`
IPControls *IPGeoIPControls `json:"ipControls,omitempty"`
ASNControls *IPGeoASNControls `json:"asnControls,omitempty"`
UkraineGeoControls *UkraineGeoControl `json:"ukraineGeoControl,omitempty"`
}

Expand All @@ -67,6 +73,7 @@ type (
Block string `json:"block"`
GeoControls *IPGeoGeoControls `json:"geoControls,omitempty"`
IPControls *IPGeoIPControls `json:"ipControls,omitempty"`
ASNControls *IPGeoASNControls `json:"asnControls,omitempty"`
UkraineGeoControls *UkraineGeoControl `json:"ukraineGeoControl,omitempty"`
}

Expand Down
144 changes: 116 additions & 28 deletions pkg/appsec/ip_geo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package appsec

import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
Expand All @@ -15,13 +14,6 @@ import (

// Test IPGeo
func TestAppSec_GetIPGeo(t *testing.T) {

result := GetIPGeoResponse{}

respData := compactJSON(loadFixtureBytes("testdata/TestIPGeo/IPGeo.json"))
err := json.Unmarshal([]byte(respData), &result)
require.NoError(t, err)

tests := map[string]struct {
params GetIPGeoRequest
responseStatus int
Expand All @@ -36,10 +28,64 @@ func TestAppSec_GetIPGeo(t *testing.T) {
Version: 15,
PolicyID: "AAAA_81230",
},
responseStatus: http.StatusOK,
responseBody: respData,
expectedPath: "/appsec/v1/configs/43253/versions/15/security-policies/AAAA_81230/ip-geo-firewall",
expectedResponse: &result,
responseStatus: http.StatusOK,
responseBody: `{
"block": "blockSpecificIPGeo",
"asnControls": {
"blockedIPNetworkLists": {
"networkList": [
"12345_ASNTEST"
]
}
},
"geoControls": {
"blockedIPNetworkLists": {
"networkList": [
"72138_TEST1"
]
}
},
"ipControls": {
"allowedIPNetworkLists": {
"networkList": [
"56921_TEST"
]
},
"blockedIPNetworkLists": {
"networkList": [
"53712_TESTLIST123"
]
}
},
"ukraineGeoControl": {
"action": "alert"
}
}`,
expectedPath: "/appsec/v1/configs/43253/versions/15/security-policies/AAAA_81230/ip-geo-firewall",
expectedResponse: &GetIPGeoResponse{
Block: "blockSpecificIPGeo",
GeoControls: &IPGeoGeoControls{
BlockedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"72138_TEST1"},
},
},
IPControls: &IPGeoIPControls{
AllowedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"56921_TEST"},
},
BlockedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"53712_TESTLIST123"},
},
},
ASNControls: &IPGeoASNControls{
BlockedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"12345_ASNTEST"},
},
},
UkraineGeoControls: &UkraineGeoControl{
Action: "alert",
},
},
},
"500 internal server error": {
params: GetIPGeoRequest{
Expand Down Expand Up @@ -87,18 +133,6 @@ func TestAppSec_GetIPGeo(t *testing.T) {

// Test Update IPGeo.
func TestAppSec_UpdateIPGeo(t *testing.T) {
result := UpdateIPGeoResponse{}

respData := compactJSON(loadFixtureBytes("testdata/TestIPGeo/IPGeo.json"))
err := json.Unmarshal([]byte(respData), &result)
require.NoError(t, err)

req := UpdateIPGeoRequest{}

reqData := compactJSON(loadFixtureBytes("testdata/TestIPGeo/IPGeo.json"))
err = json.Unmarshal([]byte(reqData), &req)
require.NoError(t, err)

tests := map[string]struct {
params UpdateIPGeoRequest
responseStatus int
Expand All @@ -117,10 +151,64 @@ func TestAppSec_UpdateIPGeo(t *testing.T) {
headers: http.Header{
"Content-Type": []string{"application/json;charset=UTF-8"},
},
responseStatus: http.StatusCreated,
responseBody: respData,
expectedResponse: &result,
expectedPath: "/appsec/v1/configs/43253/versions/15/security-policies/AAAA_81230/ip-geo-firewall",
responseStatus: http.StatusCreated,
responseBody: `{
"block": "blockSpecificIPGeo",
"asnControls": {
"blockedIPNetworkLists": {
"networkList": [
"12345_ASNTEST"
]
}
},
"geoControls": {
"blockedIPNetworkLists": {
"networkList": [
"72138_TEST1"
]
}
},
"ipControls": {
"allowedIPNetworkLists": {
"networkList": [
"56921_TEST"
]
},
"blockedIPNetworkLists": {
"networkList": [
"53712_TESTLIST123"
]
}
},
"ukraineGeoControl": {
"action": "alert"
}
}`,
expectedResponse: &UpdateIPGeoResponse{
Block: "blockSpecificIPGeo",
GeoControls: &IPGeoGeoControls{
BlockedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"72138_TEST1"},
},
},
IPControls: &IPGeoIPControls{
AllowedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"56921_TEST"},
},
BlockedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"53712_TESTLIST123"},
},
},
ASNControls: &IPGeoASNControls{
BlockedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"12345_ASNTEST"},
},
},
UkraineGeoControls: &UkraineGeoControl{
Action: "alert",
},
},
expectedPath: "/appsec/v1/configs/43253/versions/15/security-policies/AAAA_81230/ip-geo-firewall",
},
"500 internal server error": {
params: UpdateIPGeoRequest{
Expand Down
25 changes: 0 additions & 25 deletions pkg/appsec/testdata/TestIPGeo/IPGeo.json

This file was deleted.

1 change: 1 addition & 0 deletions pkg/botman/botman.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type (
CustomClientSequence
CustomDefinedBot
CustomDenyAction
CustomCode
JavascriptInjection
RecategorizedAkamaiDefinedBot
ResponseAction
Expand Down
112 changes: 112 additions & 0 deletions pkg/botman/custom_code.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package botman

import (
"context"
"encoding/json"
"fmt"
"net/http"

validation "github.com/go-ozzo/ozzo-validation/v4"
)

type (
// The CustomCode interface supports retrieving and updating custom code
CustomCode interface {
GetCustomCode(ctx context.Context, params GetCustomCodeRequest) (map[string]interface{}, error)

UpdateCustomCode(ctx context.Context, params UpdateCustomCodeRequest) (map[string]interface{}, error)
}

// GetCustomCodeRequest is used to retrieve custom code
GetCustomCodeRequest struct {
ConfigID int64
Version int64
}

// UpdateCustomCodeRequest is used to modify custom code
UpdateCustomCodeRequest struct {
ConfigID int64
Version int64
JsonPayload json.RawMessage
}
)

// Validate validates a GetCustomCodeRequest.
func (v GetCustomCodeRequest) Validate() error {
return validation.Errors{
"ConfigID": validation.Validate(v.ConfigID, validation.Required),
"Version": validation.Validate(v.Version, validation.Required),
}.Filter()
}

// Validate validates an UpdateCustomCodeRequest.
func (v UpdateCustomCodeRequest) Validate() error {
return validation.Errors{
"ConfigID": validation.Validate(v.ConfigID, validation.Required),
"Version": validation.Validate(v.Version, validation.Required),
"JsonPayload": validation.Validate(v.JsonPayload, validation.Required),
}.Filter()
}

func (b *botman) GetCustomCode(ctx context.Context, params GetCustomCodeRequest) (map[string]interface{}, error) {
logger := b.Log(ctx)
logger.Debug("GetCustomCode")

if err := params.Validate(); err != nil {
return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
}

uri := fmt.Sprintf(
"/appsec/v1/configs/%d/versions/%d/advanced-settings/transactional-endpoint-protection/custom-code",
params.ConfigID,
params.Version)

req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return nil, fmt.Errorf("failed to create GetCustomCode request: %w", err)
}

var result map[string]interface{}
resp, err := b.Exec(req, &result)
if err != nil {
return nil, fmt.Errorf("GetCustomCode request failed: %w", err)
}

if resp.StatusCode != http.StatusOK {
return nil, b.Error(resp)
}

return result, nil
}

func (b *botman) UpdateCustomCode(ctx context.Context, params UpdateCustomCodeRequest) (map[string]interface{}, error) {
logger := b.Log(ctx)
logger.Debug("UpdateCustomCode")

if err := params.Validate(); err != nil {
return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
}

putURL := fmt.Sprintf(
"/appsec/v1/configs/%d/versions/%d/advanced-settings/transactional-endpoint-protection/custom-code",
params.ConfigID,
params.Version,
)

req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to create UpdateCustomCode request: %w", err)
}

var result map[string]interface{}
resp, err := b.Exec(req, &result, params.JsonPayload)
if err != nil {
return nil, fmt.Errorf("UpdateCustomCode request failed: %w", err)
}

if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusNoContent {
return nil, b.Error(resp)
}

return result, nil
}
Loading

0 comments on commit 8f8b577

Please sign in to comment.