-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Andrei Petriv
committed
Jul 26, 2021
1 parent
093f835
commit d0ee313
Showing
8 changed files
with
746 additions
and
0 deletions.
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,75 @@ | ||
package siteshield | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
) | ||
|
||
var ( | ||
// ErrBadRequest is returned when a required parameter is missing | ||
ErrBadRequest = errors.New("missing argument") | ||
) | ||
|
||
type ( | ||
// Error is a appsec error interface | ||
Error struct { | ||
Type string `json:"type"` | ||
Title string `json:"title"` | ||
Detail string `json:"detail"` | ||
Instance string `json:"instance,omitempty"` | ||
BehaviorName string `json:"behaviorName,omitempty"` | ||
ErrorLocation string `json:"errorLocation,omitempty"` | ||
StatusCode int `json:"-"` | ||
} | ||
) | ||
|
||
// Error parses an error from the response | ||
func (s *siteshieldmap) Error(r *http.Response) error { | ||
var e Error | ||
|
||
var body []byte | ||
|
||
body, err := ioutil.ReadAll(r.Body) | ||
if err != nil { | ||
s.Log(r.Request.Context()).Errorf("reading error response body: %s", err) | ||
e.StatusCode = r.StatusCode | ||
e.Title = fmt.Sprintf("Failed to read error body") | ||
e.Detail = err.Error() | ||
return &e | ||
} | ||
|
||
if err := json.Unmarshal(body, &e); err != nil { | ||
s.Log(r.Request.Context()).Errorf("could not unmarshal API error: %s", err) | ||
e.Title = fmt.Sprintf("Failed to unmarshal error body") | ||
e.Detail = err.Error() | ||
} | ||
|
||
e.StatusCode = r.StatusCode | ||
|
||
return &e | ||
} | ||
|
||
func (e *Error) Error() string { | ||
return fmt.Sprintf("Title: %s; Type: %s; Detail: %s", e.Title, e.Type, e.Detail) | ||
} | ||
|
||
// Is handles error comparisons | ||
func (e *Error) Is(target error) bool { | ||
var t *Error | ||
if !errors.As(target, &t) { | ||
return false | ||
} | ||
|
||
if e == t { | ||
return true | ||
} | ||
|
||
if e.StatusCode != t.StatusCode { | ||
return false | ||
} | ||
|
||
return e.Error() == t.Error() | ||
} |
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,68 @@ | ||
package siteshield | ||
|
||
import ( | ||
"context" | ||
"io/ioutil" | ||
"net/http" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v2/pkg/session" | ||
"github.com/stretchr/testify/require" | ||
"github.com/tj/assert" | ||
) | ||
|
||
func TestNewError(t *testing.T) { | ||
sess, err := session.New() | ||
require.NoError(t, err) | ||
|
||
req, err := http.NewRequestWithContext( | ||
context.TODO(), | ||
http.MethodHead, | ||
"/", | ||
nil) | ||
require.NoError(t, err) | ||
|
||
tests := map[string]struct { | ||
response *http.Response | ||
expected *Error | ||
}{ | ||
"valid response, status code 500": { | ||
response: &http.Response{ | ||
Status: "Internal Server Error", | ||
StatusCode: http.StatusInternalServerError, | ||
Body: ioutil.NopCloser(strings.NewReader( | ||
`{"type":"a","title":"b","detail":"c"}`), | ||
), | ||
Request: req, | ||
}, | ||
expected: &Error{ | ||
Type: "a", | ||
Title: "b", | ||
Detail: "c", | ||
StatusCode: http.StatusInternalServerError, | ||
}, | ||
}, | ||
"invalid response body, assign status code": { | ||
response: &http.Response{ | ||
Status: "Internal Server Error", | ||
StatusCode: http.StatusInternalServerError, | ||
Body: ioutil.NopCloser(strings.NewReader( | ||
`test`), | ||
), | ||
Request: req, | ||
}, | ||
expected: &Error{ | ||
Title: "Failed to unmarshal error body", | ||
Detail: "invalid character 'e' in literal true (expecting 'r')", | ||
StatusCode: http.StatusInternalServerError, | ||
}, | ||
}, | ||
} | ||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
res := Client(sess).(*siteshieldmap).Error(test.response) | ||
assert.Equal(t, test.expected, res) | ||
}) | ||
} | ||
} |
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,43 @@ | ||
// Package siteshield provides access to the Akamai Site Shield APIs | ||
package siteshield | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v2/pkg/session" | ||
) | ||
|
||
var ( | ||
// ErrStructValidation is returned returned when given struct validation failed | ||
ErrStructValidation = errors.New("struct validation") | ||
) | ||
|
||
type ( | ||
// SSMAPS is the siteshieldmap api interface | ||
SSMAPS interface { | ||
SiteShieldMap | ||
} | ||
|
||
siteshieldmap struct { | ||
session.Session | ||
usePrefixes bool | ||
} | ||
|
||
// Option defines a siteshieldmap option | ||
Option func(*siteshieldmap) | ||
|
||
// ClientFunc is a siteshieldmap client new method, this can used for mocking | ||
ClientFunc func(sess session.Session, opts ...Option) SSMAPS | ||
) | ||
|
||
// Client returns a new siteshieldmap Client instance with the specified controller | ||
func Client(sess session.Session, opts ...Option) SSMAPS { | ||
s := &siteshieldmap{ | ||
Session: sess, | ||
} | ||
|
||
for _, opt := range opts { | ||
opt(s) | ||
} | ||
return s | ||
} |
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 siteshield | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
|
||
validation "github.com/go-ozzo/ozzo-validation" | ||
) | ||
|
||
// SiteShieldMap represents a collection of Site Shield | ||
// | ||
// See: SiteShieldMap.GetSiteShieldMaps() | ||
// API Docs: // site_shield v1 | ||
// | ||
// https://developer.akamai.com/api/cloud_security/site_shield/v1.html | ||
|
||
type ( | ||
// SiteShieldMap contains operations available on SiteShieldMap resource | ||
// See: // site_shield v1 | ||
// | ||
// https://developer.akamai.com/api/cloud_security/site_shield/v1.html#getamap | ||
SiteShieldMap interface { | ||
GetSiteShieldMaps(ctx context.Context) (*GetSiteShieldMapsResponse, error) | ||
GetSiteShieldMap(ctx context.Context, params SiteShieldMapRequest) (*SiteShieldMapResponse, error) | ||
AckSiteShieldMap(ctx context.Context, params SiteShieldMapRequest) (*SiteShieldMapResponse, error) | ||
} | ||
|
||
SiteShieldMapRequest struct { | ||
UniqueID int | ||
} | ||
|
||
GetSiteShieldMapsResponse struct { | ||
SiteShieldMaps []SiteShieldMapResponse `json:"siteShieldMaps"` | ||
} | ||
|
||
SiteShieldMapResponse struct { | ||
Acknowledged bool `json:"acknowledged"` | ||
Contacts []string `json:"contacts"` | ||
CurrentCidrs []string `json:"currentCidrs"` | ||
ProposedCidrs []string `json:"proposedCidrs"` | ||
RuleName string `json:"ruleName"` | ||
Type string `json:"type"` | ||
Service string `json:"service"` | ||
Shared bool `json:"shared"` | ||
AcknowledgeRequiredBy int64 `json:"acknowledgeRequiredBy"` | ||
PreviouslyAcknowledgedOn int64 `json:"previouslyAcknowledgedOn"` | ||
ID int `json:"id,omitempty"` | ||
LatestTicketID int `json:"latestTicketId,omitempty"` | ||
MapAlias string `json:"mapAlias,omitempty"` | ||
McmMapRuleID int `json:"mcmMapRuleId,omitempty"` | ||
} | ||
) | ||
|
||
// Validate validates SiteShieldMapRequest | ||
func (v SiteShieldMapRequest) Validate() error { | ||
return validation.Errors{ | ||
"UniqueID": validation.Validate(v.UniqueID, validation.Required), | ||
}.Filter() | ||
} | ||
|
||
// GetSiteShieldMaps will get a list of SiteShieldMap. | ||
// | ||
// API Docs: // site_shield v1 | ||
// | ||
// https://developer.akamai.com/api/cloud_security/site_shield/v1.html#listmaps | ||
|
||
func (s *siteshieldmap) GetSiteShieldMaps(ctx context.Context) (*GetSiteShieldMapsResponse, error) { | ||
logger := s.Log(ctx) | ||
logger.Debug("GetSiteShieldMaps") | ||
|
||
var rval GetSiteShieldMapsResponse | ||
|
||
uri := "/siteshield/v1/maps" | ||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create getSiteShieldMaps request: %s", err.Error()) | ||
} | ||
|
||
resp, err := s.Exec(req, &rval) | ||
if err != nil { | ||
return nil, fmt.Errorf("getsiteshieldmaps request failed: %s", err.Error()) | ||
} | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
return nil, s.Error(resp) | ||
} | ||
|
||
return &rval, nil | ||
|
||
} | ||
|
||
// GetSiteShieldMap will get a SiteShieldMap by unique ID. | ||
// | ||
// API Docs: // site_shield v1 | ||
// | ||
// https://developer.akamai.com/api/cloud_security/site_shield/v1.html#getamap | ||
|
||
func (s *siteshieldmap) GetSiteShieldMap(ctx context.Context, params SiteShieldMapRequest) (*SiteShieldMapResponse, error) { | ||
if err := params.Validate(); err != nil { | ||
return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error()) | ||
} | ||
|
||
logger := s.Log(ctx) | ||
logger.Debug("GetSiteShieldMap") | ||
|
||
var rval SiteShieldMapResponse | ||
|
||
uri := fmt.Sprintf("/siteshield/v1/maps/%d", params.UniqueID) | ||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create getSiteShieldMap request: %s", err.Error()) | ||
} | ||
|
||
resp, err := s.Exec(req, &rval) | ||
if err != nil { | ||
return nil, fmt.Errorf("getSiteShieldMap request failed: %s", err.Error()) | ||
} | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
return nil, s.Error(resp) | ||
} | ||
|
||
return &rval, nil | ||
} | ||
|
||
// AckSiteShieldMap will acknowledge changes to a SiteShieldMap. | ||
// | ||
// API Docs: // site_shield v1 | ||
// | ||
// https://developer.akamai.com/api/cloud_security/site_shield/v1.html#acknowledgeamap | ||
|
||
func (s *siteshieldmap) AckSiteShieldMap(ctx context.Context, params SiteShieldMapRequest) (*SiteShieldMapResponse, error) { | ||
if err := params.Validate(); err != nil { | ||
return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error()) | ||
} | ||
|
||
logger := s.Log(ctx) | ||
logger.Debug("AckSiteShieldMap") | ||
|
||
postURL := fmt.Sprintf("/siteshield/v1/maps/%d/acknowledge", params.UniqueID) | ||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, nil) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create AckSiteShieldMap: %s", err.Error()) | ||
} | ||
|
||
var rval SiteShieldMapResponse | ||
resp, err := s.Exec(req, &rval, params) | ||
if err != nil { | ||
return nil, fmt.Errorf("AckSiteShieldMap request failed: %s", err.Error()) | ||
} | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
return nil, s.Error(resp) | ||
} | ||
|
||
return &rval, nil | ||
} |
Oops, something went wrong.