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 }