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