github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/k8s/appprotect/app_protect_configuration.go (about) 1 package appprotect 2 3 import ( 4 "fmt" 5 "sort" 6 "time" 7 8 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 9 "k8s.io/apimachinery/pkg/runtime/schema" 10 ) 11 12 const timeLayout = time.RFC3339 13 14 // reasons for invalidity 15 const ( 16 failedValidationErrorMsg = "Validation Failed" 17 missingUserSigErrorMsg = "Policy has unsatisfied signature requirements" 18 duplicatedTagsErrorMsg = "Duplicate tag set" 19 invalidTimestampErrorMsg = "Invalid timestamp" 20 ) 21 22 var ( 23 // PolicyGVR is the group version resource of the appprotect policy 24 PolicyGVR = schema.GroupVersionResource{ 25 Group: "appprotect.f5.com", 26 Version: "v1beta1", 27 Resource: "appolicies", 28 } 29 // PolicyGVK is the group version kind of the appprotect policy 30 PolicyGVK = schema.GroupVersionKind{ 31 Group: "appprotect.f5.com", 32 Version: "v1beta1", 33 Kind: "APPolicy", 34 } 35 36 // LogConfGVR is the group version resource of the appprotect policy 37 LogConfGVR = schema.GroupVersionResource{ 38 Group: "appprotect.f5.com", 39 Version: "v1beta1", 40 Resource: "aplogconfs", 41 } 42 // LogConfGVK is the group version kind of the appprotect policy 43 LogConfGVK = schema.GroupVersionKind{ 44 Group: "appprotect.f5.com", 45 Version: "v1beta1", 46 Kind: "APLogConf", 47 } 48 49 // UserSigGVR is the group version resource of the appprotect policy 50 UserSigGVR = schema.GroupVersionResource{ 51 Group: "appprotect.f5.com", 52 Version: "v1beta1", 53 Resource: "apusersigs", 54 } 55 // UserSigGVK is the group version kind of the appprotect policy 56 UserSigGVK = schema.GroupVersionKind{ 57 Group: "appprotect.f5.com", 58 Version: "v1beta1", 59 Kind: "APUserSig", 60 } 61 ) 62 63 // UserSigChange holds resources that are affected by changes in UserSigs 64 type UserSigChange struct { 65 PolicyDeletions []*unstructured.Unstructured 66 PolicyAddsOrUpdates []*unstructured.Unstructured 67 UserSigs []*unstructured.Unstructured 68 } 69 70 // Operation defines an operation to perform for an App Protect resource. 71 type Operation int 72 73 const ( 74 // Delete the config of the resource 75 Delete Operation = iota 76 // AddOrUpdate the config of the resource 77 AddOrUpdate 78 ) 79 80 // Change represents a change in an App Protect resource 81 type Change struct { 82 // Op is an operation that needs be performed on the resource. 83 Op Operation 84 // Resource is the target resource. 85 Resource interface{} 86 } 87 88 // Problem represents a problem with an App Protect resource 89 type Problem struct { 90 // Object is a configuration object. 91 Object *unstructured.Unstructured 92 // Reason tells the reason. It matches the reason in the events of our configuration objects. 93 Reason string 94 // Message gives the details about the problem. It matches the message in the events of our configuration objects. 95 Message string 96 } 97 98 // Configuration configures App Protect resources that the Ingress Controller uses. 99 type Configuration interface { 100 AddOrUpdatePolicy(policyObj *unstructured.Unstructured) (changes []Change, problems []Problem) 101 AddOrUpdateLogConf(logConfObj *unstructured.Unstructured) (changes []Change, problems []Problem) 102 AddOrUpdateUserSig(userSigObj *unstructured.Unstructured) (change UserSigChange, problems []Problem) 103 GetAppResource(kind, key string) (*unstructured.Unstructured, error) 104 DeletePolicy(key string) (changes []Change, problems []Problem) 105 DeleteLogConf(key string) (changes []Change, problems []Problem) 106 DeleteUserSig(key string) (change UserSigChange, problems []Problem) 107 } 108 109 // ConfigurationImpl holds representations of App Protect cluster resources 110 type ConfigurationImpl struct { 111 Policies map[string]*PolicyEx 112 LogConfs map[string]*LogConfEx 113 UserSigs map[string]*UserSigEx 114 } 115 116 // NewConfiguration creates a new App Protect Configuration 117 func NewConfiguration() Configuration { 118 return newConfigurationImpl() 119 } 120 121 // NewConfiguration creates a new App Protect Configuration 122 func newConfigurationImpl() *ConfigurationImpl { 123 return &ConfigurationImpl{ 124 Policies: make(map[string]*PolicyEx), 125 LogConfs: make(map[string]*LogConfEx), 126 UserSigs: make(map[string]*UserSigEx), 127 } 128 } 129 130 // PolicyEx represents an App Protect policy cluster resource 131 type PolicyEx struct { 132 Obj *unstructured.Unstructured 133 SignatureReqs []SignatureReq 134 IsValid bool 135 ErrorMsg string 136 } 137 138 func (pol *PolicyEx) setInvalid(reason string) { 139 pol.IsValid = false 140 pol.ErrorMsg = reason 141 } 142 143 func (pol *PolicyEx) setValid() { 144 pol.IsValid = true 145 pol.ErrorMsg = "" 146 } 147 148 // SignatureReq describes a signature that is required by the policy 149 type SignatureReq struct { 150 Tag string 151 RevTimes *RevTimes 152 } 153 154 // RevTimes are requirements for signature revision time 155 type RevTimes struct { 156 MinRevTime *time.Time 157 MaxRevTime *time.Time 158 } 159 160 // LogConfEx represents an App Protect Log Configuration cluster resource 161 type LogConfEx struct { 162 Obj *unstructured.Unstructured 163 IsValid bool 164 ErrorMsg string 165 } 166 167 // UserSigEx represents an App Protect User Defined Signature cluster resource 168 type UserSigEx struct { 169 Obj *unstructured.Unstructured 170 Tag string 171 RevTime *time.Time 172 IsValid bool 173 ErrorMsg string 174 } 175 176 func (sig *UserSigEx) setInvalid(reason string) { 177 sig.IsValid = false 178 sig.ErrorMsg = reason 179 } 180 181 func (sig *UserSigEx) setValid() { 182 sig.IsValid = true 183 sig.ErrorMsg = "" 184 } 185 186 type appProtectUserSigSlice []*UserSigEx 187 188 func (s appProtectUserSigSlice) Len() int { 189 return len(s) 190 } 191 192 func (s appProtectUserSigSlice) Less(i, j int) bool { 193 if s[i].Obj.GetCreationTimestamp().Time.Equal(s[j].Obj.GetCreationTimestamp().Time) { 194 return s[i].Obj.GetUID() > s[j].Obj.GetUID() 195 } 196 return s[i].Obj.GetCreationTimestamp().Time.Before(s[j].Obj.GetCreationTimestamp().Time) 197 } 198 199 func (s appProtectUserSigSlice) Swap(i, j int) { 200 s[i], s[j] = s[j], s[i] 201 } 202 203 func createAppProtectPolicyEx(policyObj *unstructured.Unstructured) (*PolicyEx, error) { 204 err := validateAppProtectPolicy(policyObj) 205 if err != nil { 206 errMsg := fmt.Sprintf("Error validating policy %s: %v", policyObj.GetName(), err) 207 return &PolicyEx{Obj: policyObj, IsValid: false, ErrorMsg: failedValidationErrorMsg}, fmt.Errorf(errMsg) 208 } 209 sigReqs := []SignatureReq{} 210 // Check if policy has signature requirement (revision timestamp) and map them to tags 211 list, found, err := unstructured.NestedSlice(policyObj.Object, "spec", "policy", "signature-requirements") 212 if err != nil { 213 errMsg := fmt.Sprintf("Error retrieving Signature requirements from %s: %v", policyObj.GetName(), err) 214 return &PolicyEx{Obj: policyObj, IsValid: false, ErrorMsg: failedValidationErrorMsg}, fmt.Errorf(errMsg) 215 } 216 if found { 217 for _, req := range list { 218 requirement := req.(map[string]interface{}) 219 if reqTag, ok := requirement["tag"]; ok { 220 timeReq, err := buildRevTimes(requirement) 221 if err != nil { 222 errMsg := fmt.Sprintf("Error creating time requirements from %s: %v", policyObj.GetName(), err) 223 return &PolicyEx{Obj: policyObj, IsValid: false, ErrorMsg: invalidTimestampErrorMsg}, fmt.Errorf(errMsg) 224 } 225 sigReqs = append(sigReqs, SignatureReq{Tag: reqTag.(string), RevTimes: &timeReq}) 226 } 227 } 228 } 229 return &PolicyEx{ 230 Obj: policyObj, 231 SignatureReqs: sigReqs, 232 IsValid: true, 233 }, nil 234 } 235 236 func buildRevTimes(requirement map[string]interface{}) (RevTimes, error) { 237 timeReq := RevTimes{} 238 if minRev, ok := requirement["minRevisionDatetime"]; ok { 239 minRevTime, err := time.Parse(timeLayout, minRev.(string)) 240 if err != nil { 241 errMsg := fmt.Sprintf("Error Parsing time from minRevisionDatetime %v", err) 242 return timeReq, fmt.Errorf(errMsg) 243 } 244 timeReq.MinRevTime = &minRevTime 245 } 246 if maxRev, ok := requirement["maxRevisionDatetime"]; ok { 247 maxRevTime, err := time.Parse(timeLayout, maxRev.(string)) 248 if err != nil { 249 errMsg := fmt.Sprintf("Error Parsing time from maxRevisionDatetime %v", err) 250 return timeReq, fmt.Errorf(errMsg) 251 } 252 timeReq.MaxRevTime = &maxRevTime 253 } 254 return timeReq, nil 255 } 256 257 func createAppProtectLogConfEx(logConfObj *unstructured.Unstructured) (*LogConfEx, error) { 258 err := validateAppProtectLogConf(logConfObj) 259 if err != nil { 260 return &LogConfEx{ 261 Obj: logConfObj, 262 IsValid: false, 263 ErrorMsg: failedValidationErrorMsg, 264 }, err 265 } 266 return &LogConfEx{ 267 Obj: logConfObj, 268 IsValid: true, 269 }, nil 270 } 271 272 func createAppProtectUserSigEx(userSigObj *unstructured.Unstructured) (*UserSigEx, error) { 273 sTag := "" 274 err := validateAppProtectUserSig(userSigObj) 275 if err != nil { 276 errMsg := failedValidationErrorMsg 277 return &UserSigEx{Obj: userSigObj, IsValid: false, Tag: sTag, ErrorMsg: errMsg}, fmt.Errorf(errMsg) 278 } 279 // Previous validation ensures there will be no errors 280 tag, found, _ := unstructured.NestedString(userSigObj.Object, "spec", "tag") 281 if found { 282 sTag = tag 283 } 284 revTimeString, revTimeFound, _ := unstructured.NestedString(userSigObj.Object, "spec", "revisionDatetime") 285 if revTimeFound { 286 revTime, err := time.Parse(timeLayout, revTimeString) 287 if err != nil { 288 errMsg := invalidTimestampErrorMsg 289 return &UserSigEx{Obj: userSigObj, IsValid: false, ErrorMsg: errMsg}, fmt.Errorf(errMsg) 290 } 291 return &UserSigEx{Obj: userSigObj, 292 Tag: sTag, 293 RevTime: &revTime, 294 IsValid: true}, nil 295 } 296 return &UserSigEx{ 297 Obj: userSigObj, 298 Tag: sTag, 299 IsValid: true}, nil 300 } 301 302 func isReqSatisfiedByUserSig(sigReq SignatureReq, sig *UserSigEx) bool { 303 if sig.Tag == "" || sig.Tag != sigReq.Tag { 304 return false 305 } 306 if sigReq.RevTimes == nil || sig.RevTime == nil { 307 return sig.Tag == sigReq.Tag 308 } 309 if sigReq.RevTimes.MinRevTime != nil && sigReq.RevTimes.MaxRevTime != nil { 310 return sig.RevTime.Before(*sigReq.RevTimes.MaxRevTime) && sig.RevTime.After(*sigReq.RevTimes.MinRevTime) 311 } 312 if sigReq.RevTimes.MaxRevTime != nil && sig.RevTime.Before(*sigReq.RevTimes.MaxRevTime) { 313 return true 314 } 315 if sigReq.RevTimes.MinRevTime != nil && sig.RevTime.After(*sigReq.RevTimes.MinRevTime) { 316 return true 317 } 318 return false 319 } 320 321 func isReqSatisfiedByUserSigs(sigReq SignatureReq, sigs map[string]*UserSigEx) bool { 322 for _, sig := range sigs { 323 if isReqSatisfiedByUserSig(sigReq, sig) && sig.IsValid { 324 return true 325 } 326 } 327 return false 328 } 329 330 func (ci *ConfigurationImpl) verifyPolicyAgainstUserSigs(policy *PolicyEx) bool { 331 for _, sigreq := range policy.SignatureReqs { 332 if !isReqSatisfiedByUserSigs(sigreq, ci.UserSigs) { 333 return false 334 } 335 } 336 return true 337 } 338 339 // AddOrUpdatePolicy adds or updates an App Protect Policy to App Protect Configuration 340 func (ci *ConfigurationImpl) AddOrUpdatePolicy(policyObj *unstructured.Unstructured) (changes []Change, problems []Problem) { 341 resNsName := GetNsName(policyObj) 342 policy, err := createAppProtectPolicyEx(policyObj) 343 if err != nil { 344 ci.Policies[resNsName] = policy 345 return append(changes, Change{Op: Delete, Resource: policy}), 346 append(problems, Problem{Object: policyObj, Reason: "Rejected", Message: err.Error()}) 347 } 348 if ci.verifyPolicyAgainstUserSigs(policy) { 349 ci.Policies[resNsName] = policy 350 return append(changes, Change{Op: AddOrUpdate, Resource: policy}), problems 351 } 352 policy.IsValid = false 353 policy.ErrorMsg = missingUserSigErrorMsg 354 ci.Policies[resNsName] = policy 355 return append(changes, Change{Op: Delete, Resource: policy}), 356 append(problems, Problem{Object: policyObj, Reason: "Rejected", Message: missingUserSigErrorMsg}) 357 } 358 359 // AddOrUpdateLogConf adds or updates App Protect Log Configuration to App Protect Configuration 360 func (ci *ConfigurationImpl) AddOrUpdateLogConf(logconfObj *unstructured.Unstructured) (changes []Change, problems []Problem) { 361 resNsName := GetNsName(logconfObj) 362 logConf, err := createAppProtectLogConfEx(logconfObj) 363 ci.LogConfs[resNsName] = logConf 364 if err != nil { 365 return append(changes, Change{Op: Delete, Resource: logConf}), 366 append(problems, Problem{Object: logconfObj, Reason: "Rejected", Message: err.Error()}) 367 } 368 return append(changes, Change{Op: AddOrUpdate, Resource: logConf}), problems 369 } 370 371 // AddOrUpdateUserSig adds or updates App Protect User Defined Signature to App Protect Configuration 372 func (ci *ConfigurationImpl) AddOrUpdateUserSig(userSigObj *unstructured.Unstructured) (change UserSigChange, problems []Problem) { 373 resNsName := GetNsName(userSigObj) 374 userSig, err := createAppProtectUserSigEx(userSigObj) 375 ci.UserSigs[resNsName] = userSig 376 if err != nil { 377 problems = append(problems, Problem{Object: userSigObj, Reason: "Rejected", Message: err.Error()}) 378 } 379 change.UserSigs = append(change.UserSigs, userSigObj) 380 ci.buildUserSigChangeAndProblems(&problems, &change) 381 382 return change, problems 383 } 384 385 // GetAppResource returns a pointer to an App Protect resource 386 func (ci *ConfigurationImpl) GetAppResource(kind, key string) (*unstructured.Unstructured, error) { 387 switch kind { 388 case PolicyGVK.Kind: 389 if obj, ok := ci.Policies[key]; ok { 390 if obj.IsValid { 391 return obj.Obj, nil 392 } 393 return nil, fmt.Errorf(obj.ErrorMsg) 394 } 395 return nil, fmt.Errorf("App Protect Policy %s not found", key) 396 case LogConfGVK.Kind: 397 if obj, ok := ci.LogConfs[key]; ok { 398 if obj.IsValid { 399 return obj.Obj, nil 400 } 401 return nil, fmt.Errorf(obj.ErrorMsg) 402 } 403 return nil, fmt.Errorf("App Protect LogConf %s not found", key) 404 case UserSigGVK.Kind: 405 if obj, ok := ci.UserSigs[key]; ok { 406 if obj.IsValid { 407 return obj.Obj, nil 408 } 409 return nil, fmt.Errorf(obj.ErrorMsg) 410 } 411 return nil, fmt.Errorf("App Protect UserSig %s not found", key) 412 } 413 return nil, fmt.Errorf("Unknown App Protect resource kind %s", kind) 414 } 415 416 // DeletePolicy deletes an App Protect Policy from App Protect Configuration 417 func (ci *ConfigurationImpl) DeletePolicy(key string) (changes []Change, problems []Problem) { 418 if _, has := ci.Policies[key]; has { 419 change := Change{Op: Delete, Resource: ci.Policies[key]} 420 delete(ci.Policies, key) 421 return append(changes, change), problems 422 } 423 return changes, problems 424 } 425 426 // DeleteLogConf deletes an App Protect Log Configuration from App Protect Configuration 427 func (ci *ConfigurationImpl) DeleteLogConf(key string) (changes []Change, problems []Problem) { 428 if _, has := ci.LogConfs[key]; has { 429 change := Change{Op: Delete, Resource: ci.LogConfs[key]} 430 delete(ci.LogConfs, key) 431 return append(changes, change), problems 432 } 433 return changes, problems 434 } 435 436 // DeleteUserSig deletes an App Protect User Defined Signature from App Protect Configuration 437 func (ci *ConfigurationImpl) DeleteUserSig(key string) (change UserSigChange, problems []Problem) { 438 if _, has := ci.UserSigs[key]; has { 439 change.UserSigs = append(change.UserSigs, ci.UserSigs[key].Obj) 440 delete(ci.UserSigs, key) 441 ci.buildUserSigChangeAndProblems(&problems, &change) 442 } 443 return change, problems 444 } 445 446 func (ci *ConfigurationImpl) detectDuplicateTags() (outcome [][]*UserSigEx) { 447 tmp := make(map[string][]*UserSigEx) 448 for _, sig := range ci.UserSigs { 449 if val, has := tmp[sig.Tag]; has { 450 if sig.ErrorMsg != failedValidationErrorMsg { 451 tmp[sig.Tag] = append(val, sig) 452 } 453 } else { 454 if sig.ErrorMsg != failedValidationErrorMsg { 455 tmp[sig.Tag] = []*UserSigEx{sig} 456 } 457 } 458 } 459 for key, vals := range tmp { 460 if key != "" { 461 outcome = append(outcome, vals) 462 } 463 } 464 return outcome 465 } 466 467 // reconcileUserSigs verifies if tags defined in uds resources are unique 468 func (ci *ConfigurationImpl) reconcileUserSigs() (changes []Change, problems []Problem) { 469 dupTag := ci.detectDuplicateTags() 470 for _, sigs := range dupTag { 471 sort.Sort(appProtectUserSigSlice(sigs)) 472 winner := sigs[0] 473 if !winner.IsValid { 474 winner.setValid() 475 change := Change{Op: AddOrUpdate, Resource: winner} 476 changes = append(changes, change) 477 } 478 for _, sig := range sigs[1:] { 479 if sig.IsValid { 480 sig.setInvalid(duplicatedTagsErrorMsg) 481 looserProblem := Problem{Object: sig.Obj, Reason: "Rejected", Message: duplicatedTagsErrorMsg} 482 looserChange := Change{Op: Delete, Resource: sig} 483 changes = append(changes, looserChange) 484 problems = append(problems, looserProblem) 485 } 486 } 487 } 488 return changes, problems 489 } 490 491 func (ci *ConfigurationImpl) verifyPolicies() (changes []Change, problems []Problem) { 492 for _, pol := range ci.Policies { 493 if !pol.IsValid && pol.ErrorMsg == missingUserSigErrorMsg { 494 if ci.verifyPolicyAgainstUserSigs(pol) { 495 pol.setValid() 496 change := Change{Op: AddOrUpdate, Resource: pol} 497 changes = append(changes, change) 498 } 499 } 500 if pol.IsValid { 501 if !ci.verifyPolicyAgainstUserSigs(pol) { 502 pol.setInvalid(missingUserSigErrorMsg) 503 polProb := Problem{Object: pol.Obj, Reason: "Rejected", Message: missingUserSigErrorMsg} 504 polCh := Change{Op: Delete, Resource: pol} 505 changes = append(changes, polCh) 506 problems = append(problems, polProb) 507 } 508 } 509 } 510 return changes, problems 511 } 512 513 func (ci *ConfigurationImpl) getAllUserSigObjects() []*unstructured.Unstructured { 514 out := []*unstructured.Unstructured{} 515 for _, uds := range ci.UserSigs { 516 if uds.IsValid { 517 out = append(out, uds.Obj) 518 } 519 } 520 return out 521 } 522 523 func (ci *ConfigurationImpl) buildUserSigChangeAndProblems(problems *[]Problem, udschange *UserSigChange) { 524 reconChanges, reconProblems := ci.reconcileUserSigs() 525 verChanges, verProblems := ci.verifyPolicies() 526 *problems = append(*problems, reconProblems...) 527 *problems = append(*problems, verProblems...) 528 reconChanges = append(reconChanges, verChanges...) 529 for _, cha := range reconChanges { 530 switch impl := cha.Resource.(type) { 531 case *PolicyEx: 532 if cha.Op == Delete { 533 udschange.PolicyDeletions = append(udschange.PolicyDeletions, impl.Obj) 534 } 535 if cha.Op == AddOrUpdate { 536 udschange.PolicyAddsOrUpdates = append(udschange.PolicyAddsOrUpdates, impl.Obj) 537 } 538 case *UserSigEx: 539 continue 540 } 541 } 542 udschange.UserSigs = ci.getAllUserSigObjects() 543 } 544 545 // FakeConfiguration holds representations of fake App Protect cluster resources 546 type FakeConfiguration struct { 547 Policies map[string]*PolicyEx 548 LogConfs map[string]*LogConfEx 549 UserSigs map[string]*UserSigEx 550 } 551 552 // NewFakeConfiguration creates a new App Protect Configuration 553 func NewFakeConfiguration() Configuration { 554 return &FakeConfiguration{ 555 Policies: make(map[string]*PolicyEx), 556 LogConfs: make(map[string]*LogConfEx), 557 UserSigs: make(map[string]*UserSigEx), 558 } 559 } 560 561 // AddOrUpdatePolicy adds or updates an App Protect Policy to App Protect Configuration 562 func (fc *FakeConfiguration) AddOrUpdatePolicy(policyObj *unstructured.Unstructured) (changes []Change, problems []Problem) { 563 resNsName := GetNsName(policyObj) 564 policy := &PolicyEx{ 565 Obj: policyObj, 566 IsValid: true, 567 } 568 fc.Policies[resNsName] = policy 569 return changes, problems 570 } 571 572 // AddOrUpdateLogConf adds or updates App Protect Log Configuration to App Protect Configuration 573 func (fc *FakeConfiguration) AddOrUpdateLogConf(logConfObj *unstructured.Unstructured) (changes []Change, problems []Problem) { 574 resNsName := GetNsName(logConfObj) 575 logConf := &LogConfEx{ 576 Obj: logConfObj, 577 IsValid: true, 578 } 579 fc.LogConfs[resNsName] = logConf 580 return changes, problems 581 } 582 583 // AddOrUpdateUserSig adds or updates App Protect User Defined Signature to App Protect Configuration 584 func (fc *FakeConfiguration) AddOrUpdateUserSig(userSigObj *unstructured.Unstructured) (change UserSigChange, problems []Problem) { 585 return change, problems 586 } 587 588 // GetAppResource returns a pointer to an App Protect resource 589 func (fc *FakeConfiguration) GetAppResource(kind, key string) (*unstructured.Unstructured, error) { 590 switch kind { 591 case PolicyGVK.Kind: 592 if obj, ok := fc.Policies[key]; ok { 593 return obj.Obj, nil 594 } 595 return nil, fmt.Errorf("App Protect Policy %s not found", key) 596 case LogConfGVK.Kind: 597 if obj, ok := fc.LogConfs[key]; ok { 598 return obj.Obj, nil 599 } 600 return nil, fmt.Errorf("App Protect LogConf %s not found", key) 601 } 602 return nil, fmt.Errorf("Unknown App Protect resource kind %s", kind) 603 } 604 605 // DeletePolicy deletes an App Protect Policy from App Protect Configuration 606 func (fc *FakeConfiguration) DeletePolicy(key string) (changes []Change, problems []Problem) { 607 return changes, problems 608 } 609 610 // DeleteLogConf deletes an App Protect Log Configuration from App Protect Configuration 611 func (fc *FakeConfiguration) DeleteLogConf(key string) (changes []Change, problems []Problem) { 612 return changes, problems 613 } 614 615 // DeleteUserSig deletes an App Protect User Defined Signature from App Protect Configuration 616 func (fc *FakeConfiguration) DeleteUserSig(key string) (change UserSigChange, problems []Problem) { 617 return change, problems 618 }