github.com/fastly/go-fastly/v5@v5.3.0/fastly/waf_active_rule.go (about)

     1  package fastly
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"reflect"
     8  	"strconv"
     9  	"time"
    10  
    11  	"github.com/google/jsonapi"
    12  )
    13  
    14  // WAFActiveRuleType is used for reflection because JSONAPI wants to know what it's
    15  // decoding into.
    16  var WAFActiveRuleType = reflect.TypeOf(new(WAFActiveRule))
    17  
    18  // WAFActiveRule is the information about a WAF active rule object.
    19  type WAFActiveRule struct {
    20  	ID             string     `jsonapi:"primary,waf_active_rule,omitempty"`
    21  	Status         string     `jsonapi:"attr,status,omitempty"`
    22  	ModSecID       int        `jsonapi:"attr,modsec_rule_id,omitempty"`
    23  	Revision       int        `jsonapi:"attr,revision,omitempty"`
    24  	Outdated       bool       `jsonapi:"attr,outdated,omitempty"`
    25  	LatestRevision int        `jsonapi:"attr,latest_revision,omitempty"`
    26  	CreatedAt      *time.Time `jsonapi:"attr,created_at,iso8601,omitempty"`
    27  	UpdatedAt      *time.Time `jsonapi:"attr,updated_at,iso8601,omitempty"`
    28  }
    29  
    30  // WAFActiveRuleResponse represents a list of active rules - full response.
    31  type WAFActiveRuleResponse struct {
    32  	Items []*WAFActiveRule
    33  	Info  infoResponse
    34  }
    35  
    36  // ListWAFActiveRulesInput used as input for listing a WAF's active rules.
    37  type ListWAFActiveRulesInput struct {
    38  	// The Web Application Firewall's ID.
    39  	WAFID string
    40  	// The Web Application Firewall's version number.
    41  	WAFVersionNumber int
    42  	// Limit results to active rules with the specified status.
    43  	FilterStatus string
    44  	// Limit results to active rules with the specified message.
    45  	FilterMessage string
    46  	// Limit results to active rules that represent the specified ModSecurity modsec_rule_id.
    47  	FilterModSedID string
    48  	// Limit the number of returned pages.
    49  	PageSize int
    50  	// Request a specific page of active rules.
    51  	PageNumber int
    52  	// Include relationships. Optional, comma-separated values. Permitted values: waf_rule_revision and waf_firewall_version.
    53  	Include string
    54  }
    55  
    56  func (i *ListWAFActiveRulesInput) formatFilters() map[string]string {
    57  
    58  	result := map[string]string{}
    59  	pairings := map[string]interface{}{
    60  		"filter[status]":                            i.FilterStatus,
    61  		"filter[waf_rule_revision][message]":        i.FilterMessage,
    62  		"filter[waf_rule_revision][modsec_rule_id]": i.FilterModSedID,
    63  		"page[size]":                                i.PageSize,
    64  		"page[number]":                              i.PageNumber,
    65  		"include":                                   i.Include,
    66  	}
    67  
    68  	for key, value := range pairings {
    69  		switch value := value.(type) {
    70  		case string:
    71  			if value != "" {
    72  				result[key] = value
    73  			}
    74  		case int:
    75  			if value != 0 {
    76  				result[key] = strconv.Itoa(value)
    77  			}
    78  		}
    79  	}
    80  	return result
    81  }
    82  
    83  // ListWAFActiveRules returns the list of active rules for a given WAF ID.
    84  func (c *Client) ListWAFActiveRules(i *ListWAFActiveRulesInput) (*WAFActiveRuleResponse, error) {
    85  
    86  	if i.WAFID == "" {
    87  		return nil, ErrMissingWAFID
    88  	}
    89  
    90  	if i.WAFVersionNumber == 0 {
    91  		return nil, ErrMissingWAFVersionNumber
    92  	}
    93  
    94  	path := fmt.Sprintf("/waf/firewalls/%s/versions/%d/active-rules", i.WAFID, i.WAFVersionNumber)
    95  	resp, err := c.Get(path, &RequestOptions{
    96  		Params: i.formatFilters(),
    97  	})
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	var buf bytes.Buffer
   103  	tee := io.TeeReader(resp.Body, &buf)
   104  
   105  	info, err := getResponseInfo(tee)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	data, err := jsonapi.UnmarshalManyPayload(bytes.NewReader(buf.Bytes()), WAFActiveRuleType)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	wafRules := make([]*WAFActiveRule, len(data))
   116  	for i := range data {
   117  		typed, ok := data[i].(*WAFActiveRule)
   118  		if !ok {
   119  			return nil, fmt.Errorf("got back a non-WAFActiveRule response")
   120  		}
   121  		wafRules[i] = typed
   122  	}
   123  	return &WAFActiveRuleResponse{
   124  		Items: wafRules,
   125  		Info:  info,
   126  	}, nil
   127  }
   128  
   129  // ListAllWAFActiveRulesInput used as input for listing all WAF active rules.
   130  type ListAllWAFActiveRulesInput struct {
   131  	// The Web Application Firewall's ID.
   132  	WAFID string
   133  	// The Web Application Firewall's version number.
   134  	WAFVersionNumber int
   135  	// Limit results to active rules with the specified status.
   136  	FilterStatus string
   137  	// Limit results to active rules with the specified message.
   138  	FilterMessage string
   139  	// Limit results to active rules that represent the specified ModSecurity modsec_rule_id.
   140  	FilterModSedID string
   141  	// Include relationships. Optional, comma-separated values. Permitted values: waf_rule_revision and waf_firewall_version.
   142  	Include string
   143  }
   144  
   145  // ListAllWAFActiveRules returns the complete list of WAF active rules for a given WAF ID. It iterates through
   146  // all existing pages to ensure all WAF active rules are returned at once.
   147  func (c *Client) ListAllWAFActiveRules(i *ListAllWAFActiveRulesInput) (*WAFActiveRuleResponse, error) {
   148  
   149  	if i.WAFID == "" {
   150  		return nil, ErrMissingWAFID
   151  	}
   152  
   153  	if i.WAFVersionNumber == 0 {
   154  		return nil, ErrMissingWAFVersionNumber
   155  	}
   156  
   157  	currentPage := 1
   158  	result := &WAFActiveRuleResponse{Items: []*WAFActiveRule{}}
   159  	for {
   160  		r, err := c.ListWAFActiveRules(&ListWAFActiveRulesInput{
   161  			WAFID:            i.WAFID,
   162  			WAFVersionNumber: i.WAFVersionNumber,
   163  			PageNumber:       currentPage,
   164  			PageSize:         WAFPaginationPageSize,
   165  			Include:          i.Include,
   166  			FilterStatus:     i.FilterStatus,
   167  			FilterModSedID:   i.FilterModSedID,
   168  			FilterMessage:    i.FilterMessage,
   169  		})
   170  		if err != nil {
   171  			return r, err
   172  		}
   173  
   174  		currentPage++
   175  		result.Items = append(result.Items, r.Items...)
   176  
   177  		if r.Info.Links.Next == "" || len(r.Items) == 0 {
   178  			return result, nil
   179  		}
   180  	}
   181  }
   182  
   183  // CreateWAFActiveRulesInput used as input for adding rules to a WAF.
   184  type CreateWAFActiveRulesInput struct {
   185  	// The Web Application Firewall's ID.
   186  	WAFID string
   187  	// The Web Application Firewall's version number.
   188  	WAFVersionNumber int
   189  	// The list of WAF active rules (ModSecID, Status and Revision are required).
   190  	Rules []*WAFActiveRule
   191  }
   192  
   193  // CreateWAFActiveRules adds rules to a particular WAF.
   194  func (c *Client) CreateWAFActiveRules(i *CreateWAFActiveRulesInput) ([]*WAFActiveRule, error) {
   195  
   196  	if i.WAFID == "" {
   197  		return nil, ErrMissingWAFID
   198  	}
   199  
   200  	if i.WAFVersionNumber == 0 {
   201  		return nil, ErrMissingWAFVersionNumber
   202  	}
   203  
   204  	if len(i.Rules) == 0 {
   205  		return nil, ErrMissingWAFActiveRule
   206  	}
   207  
   208  	path := fmt.Sprintf("/waf/firewalls/%s/versions/%d/active-rules", i.WAFID, i.WAFVersionNumber)
   209  	resp, err := c.PostJSONAPIBulk(path, i.Rules, nil)
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	data, err := jsonapi.UnmarshalManyPayload(resp.Body, WAFActiveRuleType)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	wafRules := make([]*WAFActiveRule, len(data))
   220  	for i := range data {
   221  		typed, ok := data[i].(*WAFActiveRule)
   222  		if !ok {
   223  			return nil, fmt.Errorf("got back a non-WAFActiveRule response")
   224  		}
   225  		wafRules[i] = typed
   226  	}
   227  
   228  	return wafRules, nil
   229  }
   230  
   231  // BatchModificationWAFActiveRulesInput is used for active rules batch modifications.
   232  type BatchModificationWAFActiveRulesInput struct {
   233  	// The Web Application Firewall's ID.
   234  	WAFID string
   235  	// The Web Application Firewall's version number.
   236  	WAFVersionNumber int
   237  	// The list of WAF active rules (ModSecID, Status and Revision are required for upsert, ModSecID is required for delete).
   238  	Rules []*WAFActiveRule
   239  	// The batch operation to be performed (allowed operations are upsert and delete).
   240  	OP BatchOperation
   241  }
   242  
   243  // BatchModificationWAFActiveRules is a generic function for creating or deleting WAF active rules in batches.
   244  // Upsert and delete are the only operations allowed.
   245  func (c *Client) BatchModificationWAFActiveRules(i *BatchModificationWAFActiveRulesInput) ([]*WAFActiveRule, error) {
   246  
   247  	if len(i.Rules) > BatchModifyMaximumOperations {
   248  		return nil, ErrMaxExceededRules
   249  	}
   250  
   251  	switch i.OP {
   252  	case UpsertBatchOperation:
   253  		return c.CreateWAFActiveRules(&CreateWAFActiveRulesInput{
   254  			WAFID:            i.WAFID,
   255  			WAFVersionNumber: i.WAFVersionNumber,
   256  			Rules:            i.Rules,
   257  		})
   258  	case DeleteBatchOperation:
   259  		return nil, c.DeleteWAFActiveRules(&DeleteWAFActiveRulesInput{
   260  			WAFID:            i.WAFID,
   261  			WAFVersionNumber: i.WAFVersionNumber,
   262  			Rules:            i.Rules,
   263  		})
   264  	default:
   265  		return nil, fmt.Errorf("operation %s not supported", i.OP)
   266  	}
   267  }
   268  
   269  // DeleteWAFActiveRulesInput used as input for removing rules from a WAF.
   270  type DeleteWAFActiveRulesInput struct {
   271  	// The Web Application Firewall's ID.
   272  	WAFID string
   273  	// The Web Application Firewall's version number.
   274  	WAFVersionNumber int
   275  	// The list of WAF active rules (ModSecID is required).
   276  	Rules []*WAFActiveRule
   277  }
   278  
   279  // DeleteWAFActiveRules removes rules from a particular WAF.
   280  func (c *Client) DeleteWAFActiveRules(i *DeleteWAFActiveRulesInput) error {
   281  
   282  	if i.WAFID == "" {
   283  		return ErrMissingWAFID
   284  	}
   285  
   286  	if i.WAFVersionNumber == 0 {
   287  		return ErrMissingWAFVersionNumber
   288  	}
   289  
   290  	if len(i.Rules) == 0 {
   291  		return ErrMissingWAFActiveRule
   292  	}
   293  
   294  	path := fmt.Sprintf("/waf/firewalls/%s/versions/%d/active-rules", i.WAFID, i.WAFVersionNumber)
   295  	_, err := c.DeleteJSONAPIBulk(path, i.Rules, nil)
   296  	return err
   297  }