github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/appsec/reputation_profile.go (about)

     1  package appsec
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"reflect"
     9  
    10  	validation "github.com/go-ozzo/ozzo-validation/v4"
    11  )
    12  
    13  type (
    14  	// The ReputationProfile interface supports creating, retrieving, modifying and removing reputation
    15  	// profiles for a specific security configuration version.
    16  	ReputationProfile interface {
    17  		// GetReputationProfiles returns reputation profiles for a specific security configuration version.
    18  		//
    19  		// See: https://techdocs.akamai.com/application-security/reference/get-reputation-profiles
    20  		GetReputationProfiles(ctx context.Context, params GetReputationProfilesRequest) (*GetReputationProfilesResponse, error)
    21  
    22  		// GetReputationProfile returns the details for a specific reputation profile.
    23  		//
    24  		// See: https://techdocs.akamai.com/application-security/reference/get-reputation-profile
    25  		GetReputationProfile(ctx context.Context, params GetReputationProfileRequest) (*GetReputationProfileResponse, error)
    26  
    27  		// CreateReputationProfile creates a new reputation profile for a specific configuration version.
    28  		//
    29  		// See: https://techdocs.akamai.com/application-security/reference/post-reputation-profiles
    30  		CreateReputationProfile(ctx context.Context, params CreateReputationProfileRequest) (*CreateReputationProfileResponse, error)
    31  
    32  		// UpdateReputationProfile updates details for a specific reputation profile.
    33  		//
    34  		// See: https://techdocs.akamai.com/application-security/reference/put-reputation-profile
    35  		UpdateReputationProfile(ctx context.Context, params UpdateReputationProfileRequest) (*UpdateReputationProfileResponse, error)
    36  
    37  		// RemoveReputationProfile deletes a reputation profile.
    38  		//
    39  		// See: https://techdocs.akamai.com/application-security/reference/delete-reputation-profile
    40  		RemoveReputationProfile(ctx context.Context, params RemoveReputationProfileRequest) (*RemoveReputationProfileResponse, error)
    41  	}
    42  
    43  	atomicConditionsName []string
    44  
    45  	// GetReputationProfilesRequest is used to retrieve the reputation profiles for a configuration.
    46  	GetReputationProfilesRequest struct {
    47  		ConfigID            int `json:"configId"`
    48  		ConfigVersion       int `json:"configVersion"`
    49  		ReputationProfileId int `json:"-"`
    50  	}
    51  
    52  	// GetReputationProfilesResponse is returned from a call to GetReputationProfiles.
    53  	GetReputationProfilesResponse struct {
    54  		ReputationProfiles []struct {
    55  			Condition        *ReputationProfileCondition `json:"condition,omitempty"`
    56  			Context          string                      `json:"context,omitempty"`
    57  			ContextReadable  string                      `json:"-"`
    58  			Enabled          bool                        `json:"-"`
    59  			ID               int                         `json:"id,omitempty"`
    60  			Name             string                      `json:"name,omitempty"`
    61  			SharedIPHandling string                      `json:"sharedIpHandling,omitempty"`
    62  			Threshold        int                         `json:"threshold,omitempty"`
    63  		} `json:"reputationProfiles,omitempty"`
    64  	}
    65  
    66  	// GetReputationProfileRequest is used to retrieve the details for a specific reputation profile.
    67  	GetReputationProfileRequest struct {
    68  		ConfigID            int `json:"configId"`
    69  		ConfigVersion       int `json:"configVersion"`
    70  		ReputationProfileId int `json:"-"`
    71  	}
    72  
    73  	// GetReputationProfileResponse is returned from a call to GetReputationProfile.
    74  	GetReputationProfileResponse struct {
    75  		Condition        *GetReputationProfileResponseCondition `json:"condition,omitempty"`
    76  		Context          string                                 `json:"context,omitempty"`
    77  		ContextReadable  string                                 `json:"-"`
    78  		Enabled          bool                                   `json:"-"`
    79  		ID               int                                    `json:"-"`
    80  		Name             string                                 `json:"name,omitempty"`
    81  		SharedIPHandling string                                 `json:"sharedIpHandling,omitempty"`
    82  		Threshold        int                                    `json:"threshold,omitempty"`
    83  	}
    84  
    85  	// CreateReputationProfileRequest is used to create a reputation profile.
    86  	CreateReputationProfileRequest struct {
    87  		ConfigID       int             `json:"-"`
    88  		ConfigVersion  int             `json:"-"`
    89  		JsonPayloadRaw json.RawMessage `json:"-"`
    90  	}
    91  
    92  	// CreateReputationProfileResponse is returned from a call to CreateReputationProfile.
    93  	CreateReputationProfileResponse struct {
    94  		ID               int    `json:"id"`
    95  		Name             string `json:"name"`
    96  		Context          string `json:"context"`
    97  		Description      string `json:"description"`
    98  		Threshold        int    `json:"threshold"`
    99  		SharedIPHandling string `json:"sharedIpHandling"`
   100  		Condition        struct {
   101  			AtomicConditions []struct {
   102  				CheckIps      string               `json:"checkIps,omitempty"`
   103  				ClassName     string               `json:"className"`
   104  				Index         int                  `json:"index"`
   105  				PositiveMatch bool                 `json:"positiveMatch"`
   106  				Value         []string             `json:"value,omitempty"`
   107  				Name          atomicConditionsName `json:"name,omitempty"`
   108  				NameCase      bool                 `json:"nameCase,omitempty"`
   109  				NameWildcard  bool                 `json:"nameWildcard,omitempty"`
   110  				ValueCase     bool                 `json:"valueCase,omitempty"`
   111  				ValueWildcard bool                 `json:"valueWildcard,omitempty"`
   112  				Host          []string             `json:"host,omitempty"`
   113  			} `json:"atomicConditions"`
   114  			PositiveMatch *json.RawMessage `json:"positiveMatch,omitempty"`
   115  		} `json:"condition"`
   116  		Enabled bool `json:"enabled"`
   117  	}
   118  
   119  	// UpdateReputationProfileRequest is used to modify an existing reputation profile.
   120  	UpdateReputationProfileRequest struct {
   121  		ConfigID            int             `json:"-"`
   122  		ConfigVersion       int             `json:"-"`
   123  		ReputationProfileId int             `json:"-"`
   124  		JsonPayloadRaw      json.RawMessage `json:"-"`
   125  	}
   126  
   127  	// UpdateReputationProfileResponse is returned from a call to UpdateReputationProfile.
   128  	UpdateReputationProfileResponse struct {
   129  		ID                    int    `json:"id"`
   130  		PolicyID              int    `json:"policyId"`
   131  		ConfigID              int    `json:"configId"`
   132  		ConfigVersion         int    `json:"configVersion"`
   133  		MatchType             string `json:"matchType"`
   134  		Type                  string `json:"type"`
   135  		Name                  string `json:"name"`
   136  		Description           string `json:"description"`
   137  		AverageThreshold      int    `json:"averageThreshold"`
   138  		BurstThreshold        int    `json:"burstThreshold"`
   139  		ClientIdentifier      string `json:"clientIdentifier"`
   140  		UseXForwardForHeaders bool   `json:"useXForwardForHeaders"`
   141  		RequestType           string `json:"requestType"`
   142  		SameActionOnIpv6      bool   `json:"sameActionOnIpv6"`
   143  		Path                  struct {
   144  			PositiveMatch bool     `json:"positiveMatch"`
   145  			Values        []string `json:"values"`
   146  		} `json:"path"`
   147  		PathMatchType        string `json:"pathMatchType"`
   148  		PathURIPositiveMatch bool   `json:"pathUriPositiveMatch"`
   149  		FileExtensions       struct {
   150  			PositiveMatch bool     `json:"positiveMatch"`
   151  			Values        []string `json:"values"`
   152  		} `json:"fileExtensions"`
   153  		Hostnames              []string `json:"hostNames"`
   154  		AdditionalMatchOptions []struct {
   155  			PositiveMatch bool     `json:"positiveMatch"`
   156  			Type          string   `json:"type"`
   157  			Values        []string `json:"values"`
   158  		} `json:"additionalMatchOptions"`
   159  		QueryParameters []struct {
   160  			Name          string   `json:"name"`
   161  			Values        []string `json:"values"`
   162  			PositiveMatch bool     `json:"positiveMatch"`
   163  			ValueInRange  bool     `json:"valueInRange"`
   164  		} `json:"queryParameters"`
   165  		CreateDate string `json:"createDate"`
   166  		UpdateDate string `json:"updateDate"`
   167  		Used       bool   `json:"used"`
   168  	}
   169  
   170  	// RemoveReputationProfileRequest is used to remove a reputation profile.
   171  	RemoveReputationProfileRequest struct {
   172  		ConfigID            int `json:"configId"`
   173  		ConfigVersion       int `json:"configVersion"`
   174  		ReputationProfileId int `json:"-"`
   175  	}
   176  
   177  	// RemoveReputationProfileResponse is returned from a call to RemoveReputationProfile.
   178  	RemoveReputationProfileResponse struct {
   179  		ID                    int    `json:"id"`
   180  		PolicyID              int    `json:"policyId"`
   181  		ConfigID              int    `json:"configId"`
   182  		ConfigVersion         int    `json:"configVersion"`
   183  		MatchType             string `json:"matchType"`
   184  		Type                  string `json:"type"`
   185  		Name                  string `json:"name"`
   186  		Description           string `json:"description"`
   187  		AverageThreshold      int    `json:"averageThreshold"`
   188  		BurstThreshold        int    `json:"burstThreshold"`
   189  		ClientIdentifier      string `json:"clientIdentifier"`
   190  		UseXForwardForHeaders bool   `json:"useXForwardForHeaders"`
   191  		RequestType           string `json:"requestType"`
   192  		SameActionOnIpv6      bool   `json:"sameActionOnIpv6"`
   193  		Path                  struct {
   194  			PositiveMatch bool     `json:"positiveMatch"`
   195  			Values        []string `json:"values"`
   196  		} `json:"path"`
   197  		PathMatchType        string `json:"pathMatchType"`
   198  		PathURIPositiveMatch bool   `json:"pathUriPositiveMatch"`
   199  		FileExtensions       struct {
   200  			PositiveMatch bool     `json:"positiveMatch"`
   201  			Values        []string `json:"values"`
   202  		} `json:"fileExtensions"`
   203  		Hostnames              []string `json:"hostNames"`
   204  		AdditionalMatchOptions []struct {
   205  			PositiveMatch bool     `json:"positiveMatch"`
   206  			Type          string   `json:"type"`
   207  			Values        []string `json:"values"`
   208  		} `json:"additionalMatchOptions"`
   209  		QueryParameters []struct {
   210  			Name          string   `json:"name"`
   211  			Values        []string `json:"values"`
   212  			PositiveMatch bool     `json:"positiveMatch"`
   213  			ValueInRange  bool     `json:"valueInRange"`
   214  		} `json:"queryParameters"`
   215  		CreateDate string `json:"createDate"`
   216  		UpdateDate string `json:"updateDate"`
   217  		Used       bool   `json:"used"`
   218  	}
   219  
   220  	// ReputationProfileCondition is used as part of a reputation profile description.
   221  	ReputationProfileCondition struct {
   222  		AtomicConditions []struct {
   223  			CheckIps      *json.RawMessage `json:"checkIps,omitempty"`
   224  			ClassName     string           `json:"className,omitempty"`
   225  			Index         int              `json:"index,omitempty"`
   226  			PositiveMatch *json.RawMessage `json:"positiveMatch,omitempty"`
   227  			Value         []string         `json:"value,omitempty"`
   228  			Name          *json.RawMessage `json:"name,omitempty"`
   229  			NameCase      bool             `json:"nameCase,omitempty"`
   230  			NameWildcard  *json.RawMessage `json:"nameWildcard,omitempty"`
   231  			ValueCase     bool             `json:"valueCase,omitempty"`
   232  			ValueWildcard *json.RawMessage `json:"valueWildcard,omitempty"`
   233  			Host          []string         `json:"host,omitempty"`
   234  		} `json:"atomicConditions,omitempty"`
   235  		PositiveMatch *json.RawMessage `json:"positiveMatch,omitempty"`
   236  	}
   237  
   238  	// GetReputationProfileResponseCondition is used as part of the response to GetReputationProfile.
   239  	GetReputationProfileResponseCondition struct {
   240  		AtomicConditions []struct {
   241  			CheckIps      *json.RawMessage `json:"checkIps,omitempty"`
   242  			ClassName     string           `json:"className,omitempty"`
   243  			Index         int              `json:"index,omitempty"`
   244  			PositiveMatch json.RawMessage  `json:"positiveMatch,omitempty"`
   245  			Value         []string         `json:"value,omitempty"`
   246  			Name          *json.RawMessage `json:"name,omitempty"`
   247  			NameCase      bool             `json:"nameCase,omitempty"`
   248  			NameWildcard  *json.RawMessage `json:"nameWildcard,omitempty"`
   249  			ValueCase     bool             `json:"valueCase,omitempty"`
   250  			ValueWildcard *json.RawMessage `json:"valueWildcard,omitempty"`
   251  			Host          []string         `json:"host,omitempty"`
   252  		} `json:"atomicConditions,omitempty"`
   253  		PositiveMatch *json.RawMessage `json:"positiveMatch,omitempty"`
   254  	}
   255  )
   256  
   257  func (c *atomicConditionsName) UnmarshalJSON(data []byte) error {
   258  	var nums interface{}
   259  	err := json.Unmarshal(data, &nums)
   260  	if err != nil {
   261  		return err
   262  	}
   263  
   264  	items := reflect.ValueOf(nums)
   265  	switch items.Kind() {
   266  	case reflect.String:
   267  		*c = append(*c, items.String())
   268  
   269  	case reflect.Slice:
   270  		*c = make(atomicConditionsName, 0, items.Len())
   271  		for i := 0; i < items.Len(); i++ {
   272  			item := items.Index(i)
   273  			switch item.Kind() {
   274  			case reflect.String:
   275  				*c = append(*c, item.String())
   276  			case reflect.Interface:
   277  				*c = append(*c, item.Interface().(string))
   278  			}
   279  		}
   280  	}
   281  	return nil
   282  }
   283  
   284  // Validate validates a GetReputationProfileRequest.
   285  func (v GetReputationProfileRequest) Validate() error {
   286  	return validation.Errors{
   287  		"ConfigID":      validation.Validate(v.ConfigID, validation.Required),
   288  		"ConfigVersion": validation.Validate(v.ConfigVersion, validation.Required),
   289  		"RatePolicyID":  validation.Validate(v.ReputationProfileId, validation.Required),
   290  	}.Filter()
   291  }
   292  
   293  // Validate validates a GetReputationProfilesRequest.
   294  func (v GetReputationProfilesRequest) Validate() error {
   295  	return validation.Errors{
   296  		"ConfigID":      validation.Validate(v.ConfigID, validation.Required),
   297  		"ConfigVersion": validation.Validate(v.ConfigVersion, validation.Required),
   298  	}.Filter()
   299  }
   300  
   301  // Validate validates a CreateReputationProfileRequest.
   302  func (v CreateReputationProfileRequest) Validate() error {
   303  	return validation.Errors{
   304  		"ConfigID":      validation.Validate(v.ConfigID, validation.Required),
   305  		"ConfigVersion": validation.Validate(v.ConfigVersion, validation.Required),
   306  	}.Filter()
   307  }
   308  
   309  // Validate validates an UpdateReputationProfileRequest.
   310  func (v UpdateReputationProfileRequest) Validate() error {
   311  	return validation.Errors{
   312  		"ConfigID":            validation.Validate(v.ConfigID, validation.Required),
   313  		"ConfigVersion":       validation.Validate(v.ConfigVersion, validation.Required),
   314  		"ReputationProfileId": validation.Validate(v.ReputationProfileId, validation.Required),
   315  	}.Filter()
   316  }
   317  
   318  // Validate validates a RemoveReputationProfileRequest.
   319  func (v RemoveReputationProfileRequest) Validate() error {
   320  	return validation.Errors{
   321  		"ConfigID":            validation.Validate(v.ConfigID, validation.Required),
   322  		"ConfigVersion":       validation.Validate(v.ConfigVersion, validation.Required),
   323  		"ReputationProfileId": validation.Validate(v.ReputationProfileId, validation.Required),
   324  	}.Filter()
   325  }
   326  
   327  func (p *appsec) GetReputationProfile(ctx context.Context, params GetReputationProfileRequest) (*GetReputationProfileResponse, error) {
   328  	logger := p.Log(ctx)
   329  	logger.Debug("GetReputationProfile")
   330  
   331  	if err := params.Validate(); err != nil {
   332  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   333  	}
   334  
   335  	uri := fmt.Sprintf(
   336  		"/appsec/v1/configs/%d/versions/%d/reputation-profiles/%d",
   337  		params.ConfigID,
   338  		params.ConfigVersion,
   339  		params.ReputationProfileId)
   340  
   341  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   342  	if err != nil {
   343  		return nil, fmt.Errorf("failed to create GetReputationProfile request: %w", err)
   344  	}
   345  
   346  	var result GetReputationProfileResponse
   347  	resp, err := p.Exec(req, &result)
   348  	if err != nil {
   349  		return nil, fmt.Errorf("get reputation profile request failed: %w", err)
   350  	}
   351  	if resp.StatusCode != http.StatusOK {
   352  		return nil, p.Error(resp)
   353  	}
   354  
   355  	return &result, nil
   356  }
   357  
   358  func (p *appsec) GetReputationProfiles(ctx context.Context, params GetReputationProfilesRequest) (*GetReputationProfilesResponse, error) {
   359  	logger := p.Log(ctx)
   360  	logger.Debug("GetReputationProfiles")
   361  
   362  	if err := params.Validate(); err != nil {
   363  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   364  	}
   365  
   366  	uri := fmt.Sprintf(
   367  		"/appsec/v1/configs/%d/versions/%d/reputation-profiles",
   368  		params.ConfigID,
   369  		params.ConfigVersion,
   370  	)
   371  
   372  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   373  	if err != nil {
   374  		return nil, fmt.Errorf("failed to create GetReputationProfiles request: %w", err)
   375  	}
   376  
   377  	var result GetReputationProfilesResponse
   378  	resp, err := p.Exec(req, &result)
   379  	if err != nil {
   380  		return nil, fmt.Errorf("get reputation profiles request failed: %w", err)
   381  	}
   382  	if resp.StatusCode != http.StatusOK {
   383  		return nil, p.Error(resp)
   384  	}
   385  
   386  	if params.ReputationProfileId != 0 {
   387  		var filteredResult GetReputationProfilesResponse
   388  		for _, val := range result.ReputationProfiles {
   389  			if val.ID == params.ReputationProfileId {
   390  				filteredResult.ReputationProfiles = append(filteredResult.ReputationProfiles, val)
   391  			}
   392  		}
   393  		return &filteredResult, nil
   394  	}
   395  
   396  	return &result, nil
   397  }
   398  
   399  func (p *appsec) UpdateReputationProfile(ctx context.Context, params UpdateReputationProfileRequest) (*UpdateReputationProfileResponse, error) {
   400  	logger := p.Log(ctx)
   401  	logger.Debug("UpdateReputationProfile")
   402  
   403  	if err := params.Validate(); err != nil {
   404  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   405  	}
   406  
   407  	uri := fmt.Sprintf(
   408  		"/appsec/v1/configs/%d/versions/%d/reputation-profiles/%d",
   409  		params.ConfigID,
   410  		params.ConfigVersion,
   411  		params.ReputationProfileId,
   412  	)
   413  
   414  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri, nil)
   415  	if err != nil {
   416  		return nil, fmt.Errorf("failed to create UpdateReputationProfile request: %w", err)
   417  	}
   418  	req.Header.Set("Content-Type", "application/json")
   419  
   420  	var result UpdateReputationProfileResponse
   421  	resp, err := p.Exec(req, &result, params.JsonPayloadRaw)
   422  	if err != nil {
   423  		return nil, fmt.Errorf("update reputation profile request failed: %w", err)
   424  	}
   425  	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
   426  		return nil, p.Error(resp)
   427  	}
   428  
   429  	return &result, nil
   430  }
   431  
   432  func (p *appsec) CreateReputationProfile(ctx context.Context, params CreateReputationProfileRequest) (*CreateReputationProfileResponse, error) {
   433  	logger := p.Log(ctx)
   434  	logger.Debug("CreateReputationProfile")
   435  
   436  	if err := params.Validate(); err != nil {
   437  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   438  	}
   439  
   440  	uri := fmt.Sprintf(
   441  		"/appsec/v1/configs/%d/versions/%d/reputation-profiles",
   442  		params.ConfigID,
   443  		params.ConfigVersion,
   444  	)
   445  
   446  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, nil)
   447  	if err != nil {
   448  		return nil, fmt.Errorf("failed to create CreateReputationProfile request: %w", err)
   449  	}
   450  
   451  	var result CreateReputationProfileResponse
   452  	req.Header.Set("Content-Type", "application/json")
   453  	resp, err := p.Exec(req, &result, params.JsonPayloadRaw)
   454  	if err != nil {
   455  		return nil, fmt.Errorf("create reputation profile request failed: %w", err)
   456  	}
   457  	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
   458  		return nil, p.Error(resp)
   459  	}
   460  
   461  	return &result, nil
   462  }
   463  
   464  func (p *appsec) RemoveReputationProfile(ctx context.Context, params RemoveReputationProfileRequest) (*RemoveReputationProfileResponse, error) {
   465  	logger := p.Log(ctx)
   466  	logger.Debug("RemoveReputationProfile")
   467  
   468  	if err := params.Validate(); err != nil {
   469  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   470  	}
   471  
   472  	uri := fmt.Sprintf("/appsec/v1/configs/%d/versions/%d/reputation-profiles/%d", params.ConfigID, params.ConfigVersion, params.ReputationProfileId)
   473  	req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri, nil)
   474  	if err != nil {
   475  		return nil, fmt.Errorf("failed to create RemoveReputationProfile request: %w", err)
   476  	}
   477  
   478  	var result RemoveReputationProfileResponse
   479  	resp, err := p.Exec(req, &result)
   480  	if err != nil {
   481  		return nil, fmt.Errorf("remove reputation profile request failed: %w", err)
   482  	}
   483  	if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK {
   484  		return nil, p.Error(resp)
   485  	}
   486  
   487  	return &result, nil
   488  }