github.com/akamai/AkamaiOPEN-edgegrid-golang/v2@v2.17.0/pkg/appsec/custom_rule.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 CustomRule interface supports creating, retrievinfg, modifying and removing custom rules
    15  	// for a configuration.
    16  	//
    17  	// https://developer.akamai.com/api/cloud_security/application_security/v1.html#customrule
    18  	CustomRule interface {
    19  		//https://developer.akamai.com/api/cloud_security/application_security/v1.html#getcustomrules
    20  		GetCustomRules(ctx context.Context, params GetCustomRulesRequest) (*GetCustomRulesResponse, error)
    21  
    22  		// https://developer.akamai.com/api/cloud_security/application_security/v1.html#getruleid
    23  		GetCustomRule(ctx context.Context, params GetCustomRuleRequest) (*GetCustomRuleResponse, error)
    24  
    25  		// https://developer.akamai.com/api/cloud_security/application_security/v1.html#postcustomrules
    26  		CreateCustomRule(ctx context.Context, params CreateCustomRuleRequest) (*CreateCustomRuleResponse, error)
    27  
    28  		// https://developer.akamai.com/api/cloud_security/application_security/v1.html#putruleid
    29  		UpdateCustomRule(ctx context.Context, params UpdateCustomRuleRequest) (*UpdateCustomRuleResponse, error)
    30  
    31  		// https://developer.akamai.com/api/cloud_security/application_security/v1.html#deleteruleid
    32  		RemoveCustomRule(ctx context.Context, params RemoveCustomRuleRequest) (*RemoveCustomRuleResponse, error)
    33  	}
    34  
    35  	// CustomRuleConditionsValue is a slice of strings used to indicate condition values in custom rule conditions.
    36  	CustomRuleConditionsValue []string
    37  
    38  	// CustomRuleConditionsName is a slice of strings used to indicate condition names in custom rule conditions.
    39  	CustomRuleConditionsName []string
    40  
    41  	// GetCustomRulesRequest is used to retrieve the custom rules for a configuration.
    42  	GetCustomRulesRequest struct {
    43  		ConfigID int `json:"configid,omitempty"`
    44  		ID       int `json:"-"`
    45  	}
    46  
    47  	// GetCustomRulesResponse is returned from a call to GetCustomRules.
    48  	GetCustomRulesResponse struct {
    49  		CustomRules []struct {
    50  			ID                  int                        `json:"id"`
    51  			Link                string                     `json:"link"`
    52  			Name                string                     `json:"name"`
    53  			Status              string                     `json:"status"`
    54  			Version             int                        `json:"version"`
    55  			EffectiveTimePeriod *CustomRuleEffectivePeriod `json:"effectiveTimePeriod,omitempty"`
    56  			SamplingRate        *int                       `json:"samplingRate,omitempty"`
    57  		} `json:"customRules"`
    58  	}
    59  
    60  	// GetCustomRuleRequest is used to retrieve the details of a custom rule.
    61  	GetCustomRuleRequest struct {
    62  		ConfigID int `json:"configid,omitempty"`
    63  		ID       int `json:"id,omitempty"`
    64  	}
    65  
    66  	// GetCustomRuleResponse is returned from a call to GetCustomRule.
    67  	GetCustomRuleResponse CustomRuleResponse
    68  
    69  	// CustomRuleResponse is returned from calls to GetCustomRule, UpdateCustomRule, and DeleteCustomRule.
    70  	CustomRuleResponse struct {
    71  		ID            int      `json:"-"`
    72  		Name          string   `json:"name"`
    73  		Description   string   `json:"description,omitempty"`
    74  		Version       int      `json:"-"`
    75  		RuleActivated bool     `json:"-"`
    76  		Structured    bool     `json:"-"`
    77  		Tag           []string `json:"tag"`
    78  		Conditions    []struct {
    79  			Name                  *json.RawMessage `json:"name,omitempty"`
    80  			NameCase              *bool            `json:"nameCase,omitempty"`
    81  			NameWildcard          *bool            `json:"nameWildcard,omitempty"`
    82  			PositiveMatch         bool             `json:"positiveMatch"`
    83  			Type                  string           `json:"type"`
    84  			Value                 *json.RawMessage `json:"value,omitempty"`
    85  			ValueCase             *bool            `json:"valueCase,omitempty"`
    86  			ValueExactMatch       *bool            `json:"valueExactMatch,omitempty"`
    87  			ValueIgnoreSegment    *bool            `json:"valueIgnoreSegment,omitempty"`
    88  			ValueNormalize        *bool            `json:"valueNormalize,omitempty"`
    89  			ValueRecursive        *bool            `json:"valueRecursive,omitempty"`
    90  			ValueWildcard         *bool            `json:"valueWildcard,omitempty"`
    91  			UseXForwardForHeaders *bool            `json:"useXForwardForHeaders,omitempty"`
    92  		} `json:"conditions"`
    93  		EffectiveTimePeriod *CustomRuleEffectivePeriod `json:"effectiveTimePeriod,omitempty"`
    94  		SamplingRate        int                        `json:"samplingRate,omitempty"`
    95  		LoggingOptions      *json.RawMessage           `json:"loggingOptions,omitempty"`
    96  		Operation           string                     `json:"operation,omitempty"`
    97  	}
    98  
    99  	// CustomRuleEffectivePeriod defines the period during which a custom rule is active as well as its current status.
   100  	CustomRuleEffectivePeriod struct {
   101  		EndDate   string `json:"endDate"`
   102  		StartDate string `json:"startDate"`
   103  		Status    string `json:"-"`
   104  	}
   105  
   106  	// CreateCustomRuleRequest is used to create a custom rule.
   107  	CreateCustomRuleRequest struct {
   108  		ConfigID       int             `json:"configid,omitempty"`
   109  		Version        int             `json:"version,omitempty"`
   110  		JsonPayloadRaw json.RawMessage `json:"-"`
   111  	}
   112  
   113  	// CreateCustomRuleResponse is returned from a call to CreateCustomRule.
   114  	CreateCustomRuleResponse struct {
   115  		ID            int      `json:"id,omitempty"`
   116  		Name          string   `json:"name"`
   117  		Description   string   `json:"description,omitempty"`
   118  		Version       int      `json:"-"`
   119  		RuleActivated bool     `json:"-"`
   120  		Structured    bool     `json:"-"`
   121  		Tag           []string `json:"tag"`
   122  		Conditions    []struct {
   123  			Name                  *json.RawMessage `json:"name,omitempty"`
   124  			NameCase              *bool            `json:"nameCase,omitempty"`
   125  			NameWildcard          *bool            `json:"nameWildcard,omitempty"`
   126  			PositiveMatch         bool             `json:"positiveMatch"`
   127  			Type                  string           `json:"type"`
   128  			Value                 *json.RawMessage `json:"value,omitempty"`
   129  			ValueCase             *bool            `json:"valueCase,omitempty"`
   130  			ValueExactMatch       *bool            `json:"valueExactMatch,omitempty"`
   131  			ValueIgnoreSegment    *bool            `json:"valueIgnoreSegment,omitempty"`
   132  			ValueNormalize        *bool            `json:"valueNormalize,omitempty"`
   133  			ValueRecursive        *bool            `json:"valueRecursive,omitempty"`
   134  			ValueWildcard         *bool            `json:"valueWildcard,omitempty"`
   135  			UseXForwardForHeaders *bool            `json:"useXForwardForHeaders,omitempty"`
   136  		} `json:"conditions"`
   137  		EffectiveTimePeriod *CustomRuleEffectivePeriod `json:"effectiveTimePeriod,omitempty"`
   138  		SamplingRate        int                        `json:"samplingRate,omitempty"`
   139  		LoggingOptions      *json.RawMessage           `json:"loggingOptions,omitempty"`
   140  		Operation           string                     `json:"operation,omitempty"`
   141  	}
   142  
   143  	// UpdateCustomRuleRequest is used to modify an existing custom rule.
   144  	UpdateCustomRuleRequest struct {
   145  		ConfigID       int             `json:"configid,omitempty"`
   146  		ID             int             `json:"id,omitempty"`
   147  		Version        int             `json:"version,omitempty"`
   148  		JsonPayloadRaw json.RawMessage `json:"-"`
   149  	}
   150  
   151  	// UpdateCustomRuleResponse is returned from a call to UpdateCustomRule.
   152  	UpdateCustomRuleResponse GetCustomRuleResponse
   153  
   154  	// RemoveCustomRuleRequest is used to remove a custom rule.
   155  	RemoveCustomRuleRequest struct {
   156  		ConfigID int `json:"configid,omitempty"`
   157  		ID       int `json:"id,omitempty"`
   158  	}
   159  
   160  	// RemoveCustomRuleResponse is returned from a call to RemoveCustomRule.
   161  	RemoveCustomRuleResponse UpdateCustomRuleResponse
   162  )
   163  
   164  // UnmarshalJSON reads a CustomRuleConditionsValue from its data argument.
   165  func (c *CustomRuleConditionsValue) UnmarshalJSON(data []byte) error {
   166  	var nums interface{}
   167  	err := json.Unmarshal(data, &nums)
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	items := reflect.ValueOf(nums)
   173  	switch items.Kind() {
   174  	case reflect.String:
   175  		*c = append(*c, items.String())
   176  
   177  	case reflect.Slice:
   178  		*c = make(CustomRuleConditionsValue, 0, items.Len())
   179  		for i := 0; i < items.Len(); i++ {
   180  			item := items.Index(i)
   181  			switch item.Kind() {
   182  			case reflect.String:
   183  				*c = append(*c, item.String())
   184  			case reflect.Interface:
   185  				*c = append(*c, item.Interface().(string))
   186  			}
   187  		}
   188  	}
   189  	return nil
   190  }
   191  
   192  // UnmarshalJSON reads a CustomRuleConditionsName from its data argument.
   193  func (c *CustomRuleConditionsName) UnmarshalJSON(data []byte) error {
   194  	var nums interface{}
   195  	err := json.Unmarshal(data, &nums)
   196  	if err != nil {
   197  		return err
   198  	}
   199  
   200  	items := reflect.ValueOf(nums)
   201  	switch items.Kind() {
   202  	case reflect.String:
   203  		*c = append(*c, items.String())
   204  
   205  	case reflect.Slice:
   206  		*c = make(CustomRuleConditionsName, 0, items.Len())
   207  		for i := 0; i < items.Len(); i++ {
   208  			item := items.Index(i)
   209  			switch item.Kind() {
   210  			case reflect.String:
   211  				*c = append(*c, item.String())
   212  			case reflect.Interface:
   213  				*c = append(*c, item.Interface().(string))
   214  			}
   215  		}
   216  	}
   217  	return nil
   218  }
   219  
   220  // Validate validates a GetCustomRuleRequest.
   221  func (v GetCustomRuleRequest) Validate() error {
   222  	return validation.Errors{
   223  		"ConfigID": validation.Validate(v.ConfigID, validation.Required),
   224  		"ID":       validation.Validate(v.ID, validation.Required),
   225  	}.Filter()
   226  }
   227  
   228  // Validate validates a GetCustomRulesRequest.
   229  func (v GetCustomRulesRequest) Validate() error {
   230  	return validation.Errors{
   231  		"ConfigID": validation.Validate(v.ConfigID, validation.Required),
   232  	}.Filter()
   233  }
   234  
   235  // Validate validates a CreateCustomRuleRequest.
   236  func (v CreateCustomRuleRequest) Validate() error {
   237  	return validation.Errors{
   238  		"ConfigID": validation.Validate(v.ConfigID, validation.Required),
   239  	}.Filter()
   240  }
   241  
   242  // Validate validates an UpdateCustomRuleRequest.
   243  func (v UpdateCustomRuleRequest) Validate() error {
   244  	return validation.Errors{
   245  		"ConfigID": validation.Validate(v.ConfigID, validation.Required),
   246  		"ID":       validation.Validate(v.ID, validation.Required),
   247  	}.Filter()
   248  }
   249  
   250  // Validate validates a RemoveCustomRuleRequest.
   251  func (v RemoveCustomRuleRequest) Validate() error {
   252  	return validation.Errors{
   253  		"ConfigID": validation.Validate(v.ConfigID, validation.Required),
   254  		"ID":       validation.Validate(v.ID, validation.Required),
   255  	}.Filter()
   256  }
   257  
   258  func (p *appsec) GetCustomRule(ctx context.Context, params GetCustomRuleRequest) (*GetCustomRuleResponse, error) {
   259  	logger := p.Log(ctx)
   260  	logger.Debug("GetCustomRule")
   261  
   262  	if err := params.Validate(); err != nil {
   263  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   264  	}
   265  
   266  	uri := fmt.Sprintf(
   267  		"/appsec/v1/configs/%d/custom-rules/%d",
   268  		params.ConfigID,
   269  		params.ID)
   270  
   271  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   272  	if err != nil {
   273  		return nil, fmt.Errorf("failed to create GetCustomRule request: %w", err)
   274  	}
   275  
   276  	var result GetCustomRuleResponse
   277  	resp, err := p.Exec(req, &result)
   278  	if err != nil {
   279  		return nil, fmt.Errorf("get custom rule request failed: %w", err)
   280  	}
   281  	if resp.StatusCode != http.StatusOK {
   282  		return nil, p.Error(resp)
   283  	}
   284  
   285  	return &result, nil
   286  }
   287  
   288  func (p *appsec) GetCustomRules(ctx context.Context, params GetCustomRulesRequest) (*GetCustomRulesResponse, error) {
   289  	logger := p.Log(ctx)
   290  	logger.Debug("GetCustomRules")
   291  
   292  	if err := params.Validate(); err != nil {
   293  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   294  	}
   295  
   296  	uri := fmt.Sprintf(
   297  		"/appsec/v1/configs/%d/custom-rules",
   298  		params.ConfigID,
   299  	)
   300  
   301  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   302  	if err != nil {
   303  		return nil, fmt.Errorf("failed to create GetCustomRules request: %w", err)
   304  	}
   305  
   306  	var result GetCustomRulesResponse
   307  	resp, err := p.Exec(req, &result)
   308  	if err != nil {
   309  		return nil, fmt.Errorf("get custom rules request failed: %w", err)
   310  	}
   311  	if resp.StatusCode != http.StatusOK {
   312  		return nil, p.Error(resp)
   313  	}
   314  
   315  	if params.ID != 0 {
   316  		var filteredResult GetCustomRulesResponse
   317  		for _, val := range result.CustomRules {
   318  			if val.ID == params.ID {
   319  				filteredResult.CustomRules = append(filteredResult.CustomRules, val)
   320  			}
   321  		}
   322  		return &filteredResult, nil
   323  	}
   324  
   325  	return &result, nil
   326  }
   327  
   328  func (p *appsec) UpdateCustomRule(ctx context.Context, params UpdateCustomRuleRequest) (*UpdateCustomRuleResponse, error) {
   329  	logger := p.Log(ctx)
   330  	logger.Debug("UpdateCustomRule")
   331  
   332  	if err := params.Validate(); err != nil {
   333  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   334  	}
   335  
   336  	uri := fmt.Sprintf(
   337  		"/appsec/v1/configs/%d/custom-rules/%d",
   338  		params.ConfigID,
   339  		params.ID,
   340  	)
   341  
   342  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri, nil)
   343  	if err != nil {
   344  		return nil, fmt.Errorf("failed to create UpdateCustomRule request: %w", err)
   345  	}
   346  
   347  	var result UpdateCustomRuleResponse
   348  	req.Header.Set("Content-Type", "application/json")
   349  	resp, err := p.Exec(req, &result, params.JsonPayloadRaw)
   350  	if err != nil {
   351  		return nil, fmt.Errorf("update custom rule request failed: %w", err)
   352  	}
   353  	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
   354  		return nil, p.Error(resp)
   355  	}
   356  
   357  	return &result, nil
   358  }
   359  
   360  func (p *appsec) CreateCustomRule(ctx context.Context, params CreateCustomRuleRequest) (*CreateCustomRuleResponse, error) {
   361  	logger := p.Log(ctx)
   362  	logger.Debug("CreateCustomRule")
   363  
   364  	if err := params.Validate(); err != nil {
   365  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   366  	}
   367  
   368  	uri := fmt.Sprintf(
   369  		"/appsec/v1/configs/%d/custom-rules",
   370  		params.ConfigID,
   371  	)
   372  
   373  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, nil)
   374  	if err != nil {
   375  		return nil, fmt.Errorf("failed to create CreateCustomRule request: %w", err)
   376  	}
   377  
   378  	var result CreateCustomRuleResponse
   379  	req.Header.Set("Content-Type", "application/json")
   380  	resp, err := p.Exec(req, &result, params.JsonPayloadRaw)
   381  	if err != nil {
   382  		return nil, fmt.Errorf("create custom rule request failed: %w", err)
   383  	}
   384  	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
   385  		return nil, p.Error(resp)
   386  	}
   387  
   388  	return &result, nil
   389  }
   390  
   391  func (p *appsec) RemoveCustomRule(ctx context.Context, params RemoveCustomRuleRequest) (*RemoveCustomRuleResponse, error) {
   392  	logger := p.Log(ctx)
   393  	logger.Debug("RemoveCustomRule")
   394  
   395  	if err := params.Validate(); err != nil {
   396  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   397  	}
   398  
   399  	var result RemoveCustomRuleResponse
   400  	uri := fmt.Sprintf("/appsec/v1/configs/%d/custom-rules/%d", params.ConfigID, params.ID)
   401  	req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri, nil)
   402  	if err != nil {
   403  		return nil, fmt.Errorf("failed to create RemoveCustomRule request: %w", err)
   404  	}
   405  
   406  	resp, err := p.Exec(req, nil)
   407  	if err != nil {
   408  		return nil, fmt.Errorf("remove custom rule request failed: %w", err)
   409  	}
   410  	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
   411  		return nil, p.Error(resp)
   412  	}
   413  
   414  	return &result, nil
   415  }