Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SiteShield API added #135

Open
wants to merge 1 commit into
base: v2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions pkg/siteshield/errors.go
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()
}
68 changes: 68 additions & 0 deletions pkg/siteshield/errors_test.go
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)
})
}
}
43 changes: 43 additions & 0 deletions pkg/siteshield/siteshield.go
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
}
161 changes: 161 additions & 0 deletions pkg/siteshield/siteshield_map.go
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
}
Loading