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 }