2019-10-18 17:45:24 -07:00
package policy
2019-09-27 16:31:27 -07:00
import (
2020-03-20 20:23:34 +05:30
"encoding/json"
2019-09-27 16:31:27 -07:00
"errors"
"fmt"
"reflect"
2019-10-21 14:22:31 -07:00
"regexp"
2019-09-27 19:03:55 -07:00
"strconv"
2019-12-04 18:50:51 -08:00
"strings"
2019-09-27 16:31:27 -07:00
2020-03-04 19:16:26 +05:30
"github.com/nirmata/kyverno/pkg/openapi"
2019-11-13 13:41:08 -08:00
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
2019-10-21 14:22:31 -07:00
"github.com/nirmata/kyverno/pkg/engine/anchor"
2019-12-04 18:50:51 -08:00
rbacv1 "k8s.io/api/rbac/v1"
2019-09-27 16:31:27 -07:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
2019-10-21 14:22:31 -07:00
// Validate does some initial check to verify some conditions
// - One operation per rule
// - ResourceDescription mandatory checks
2019-10-18 17:45:24 -07:00
func Validate ( p kyverno . ClusterPolicy ) error {
2019-10-21 14:22:31 -07:00
if path , err := validateUniqueRuleName ( p ) ; err != nil {
return fmt . Errorf ( "path: spec.%s: %v" , path , err )
2019-10-03 16:49:41 -07:00
}
2020-01-07 15:13:57 -08:00
if p . Spec . Background == nil {
//skipped policy mutation default -> skip validation -> will not be processed for background processing
return nil
}
if * p . Spec . Background {
2019-12-30 17:08:50 -08:00
if err := ContainsUserInfo ( p ) ; err != nil {
// policy.spec.background -> "true"
// - cannot use variables with request.userInfo
// - cannot define userInfo(roles, cluserRoles, subjects) for filtering (match & exclude)
2020-02-18 15:00:59 -08:00
return fmt . Errorf ( "userInfo is not allowed in match or exclude when backgroud policy mode is true. Set spec.background=false to disable background mode for this policy rule. %s " , err )
2019-12-30 17:08:50 -08:00
}
}
2019-10-03 16:49:41 -07:00
2019-10-21 14:22:31 -07:00
for i , rule := range p . Spec . Rules {
// only one type of rule is allowed per rule
if err := validateRuleType ( rule ) ; err != nil {
return fmt . Errorf ( "path: spec.rules[%d]: %v" , i , err )
2019-09-27 19:03:55 -07:00
}
2019-09-27 16:31:27 -07:00
2019-10-21 14:22:31 -07:00
// validate resource description
if path , err := validateResources ( rule ) ; err != nil {
return fmt . Errorf ( "path: spec.rules[%d].%s: %v" , i , path , err )
}
// validate rule types
// only one type of rule is allowed per rule
if err := validateRuleType ( rule ) ; err != nil {
// as there are more than 1 operation in rule, not need to evaluate it further
return fmt . Errorf ( "path: spec.rules[%d]: %v" , i , err )
}
2020-03-20 20:23:34 +05:30
if err := validateMatchExcludeConflict ( rule ) ; err != nil {
return fmt . Errorf ( "path: spec.rules[%d]: %v" , i , err )
}
2019-10-21 14:22:31 -07:00
// Operation Validation
// Mutation
if rule . HasMutate ( ) {
if path , err := validateMutation ( rule . Mutation ) ; err != nil {
return fmt . Errorf ( "path: spec.rules[%d].mutate.%s.: %v" , i , path , err )
}
}
// Validation
if rule . HasValidate ( ) {
if path , err := validateValidation ( rule . Validation ) ; err != nil {
return fmt . Errorf ( "path: spec.rules[%d].validate.%s.: %v" , i , path , err )
}
}
// Generation
if rule . HasGenerate ( ) {
if path , err := validateGeneration ( rule . Generation ) ; err != nil {
return fmt . Errorf ( "path: spec.rules[%d].generate.%s.: %v" , i , path , err )
}
2019-09-27 19:03:55 -07:00
}
2020-02-26 16:08:56 +05:30
// If a rules match block does not match any kind,
// we should only allow such rules to have metadata in its overlay
if len ( rule . MatchResources . Kinds ) == 0 {
if ! ruleOnlyDealsWithResourceMetaData ( rule ) {
return fmt . Errorf ( "policy can only deal with the metadata field of the resource if" +
" the rule does not match an kind" )
}
}
2019-09-27 19:03:55 -07:00
}
2020-01-25 14:53:12 +05:30
2020-03-04 19:16:26 +05:30
if err := openapi . ValidatePolicyMutation ( p ) ; err != nil {
return err
}
2019-09-27 19:03:55 -07:00
return nil
}
2020-03-20 20:23:34 +05:30
func validateMatchExcludeConflict ( rule kyverno . Rule ) error {
excludeRoles := make ( map [ string ] bool )
for _ , role := range rule . ExcludeResources . UserInfo . Roles {
excludeRoles [ role ] = true
}
excludeClusterRoles := make ( map [ string ] bool )
for _ , clusterRoles := range rule . ExcludeResources . UserInfo . ClusterRoles {
excludeClusterRoles [ clusterRoles ] = true
}
excludeSubjects := make ( map [ string ] bool )
for _ , subject := range rule . ExcludeResources . UserInfo . Subjects {
subjectRaw , _ := json . Marshal ( subject )
excludeSubjects [ string ( subjectRaw ) ] = true
}
excludeKinds := make ( map [ string ] bool )
for _ , kind := range rule . ExcludeResources . ResourceDescription . Kinds {
excludeKinds [ kind ] = true
}
excludeNamespaces := make ( map [ string ] bool )
for _ , namespace := range rule . ExcludeResources . ResourceDescription . Namespaces {
excludeNamespaces [ namespace ] = true
}
excludeMatchExpressions := make ( map [ string ] bool )
if rule . ExcludeResources . ResourceDescription . Selector != nil {
for _ , matchExpression := range rule . ExcludeResources . ResourceDescription . Selector . MatchExpressions {
matchExpressionRaw , _ := json . Marshal ( matchExpression )
excludeMatchExpressions [ string ( matchExpressionRaw ) ] = true
}
}
for _ , role := range rule . MatchResources . UserInfo . Roles {
if excludeRoles [ role ] {
return errors . New ( fmt . Sprintf ( "excluding role '%v' while also matching it - please remove from both match and exclude" , role ) )
}
}
for _ , clusterRole := range rule . MatchResources . UserInfo . ClusterRoles {
if excludeClusterRoles [ clusterRole ] {
return errors . New ( fmt . Sprintf ( "excluding cluster role '%v' while also matching it - please remove from both match and exclude" , clusterRole ) )
}
}
for _ , subject := range rule . MatchResources . UserInfo . Subjects {
subjectRaw , _ := json . Marshal ( subject )
if excludeSubjects [ string ( subjectRaw ) ] {
return errors . New ( fmt . Sprintf ( "excluding subject '%v' while also matching it - please remove from both match and exclude" , string ( subjectRaw ) ) )
}
}
if rule . MatchResources . ResourceDescription . Name != "" {
if rule . MatchResources . ResourceDescription . Name == rule . ExcludeResources . ResourceDescription . Name {
return errors . New ( fmt . Sprintf ( "excluding resource name '%v' while also matching it - please remove from both match and exclude" , rule . MatchResources . ResourceDescription . Name ) )
}
}
for _ , namespace := range rule . MatchResources . ResourceDescription . Namespaces {
if excludeNamespaces [ namespace ] {
return errors . New ( fmt . Sprintf ( "excluding resource namespace '%v' while also matching it - please remove from both match and exclude" , namespace ) )
}
}
for _ , kind := range rule . MatchResources . ResourceDescription . Kinds {
if excludeKinds [ kind ] {
return errors . New ( fmt . Sprintf ( "excluding resource kind '%v' while also matching it - please remove from both match and exclude" , kind ) )
}
}
if rule . MatchResources . ResourceDescription . Selector != nil && rule . ExcludeResources . ResourceDescription . Selector != nil {
for _ , matchExpression := range rule . MatchResources . ResourceDescription . Selector . MatchExpressions {
matchExpressionRaw , _ := json . Marshal ( matchExpression )
if excludeMatchExpressions [ string ( matchExpressionRaw ) ] {
return errors . New ( fmt . Sprintf ( "excluding resource match expression '%v' while also matching it - please remove from both match and exclude" , string ( matchExpressionRaw ) ) )
}
}
for label , value := range rule . MatchResources . ResourceDescription . Selector . MatchLabels {
if rule . ExcludeResources . ResourceDescription . Selector . MatchLabels [ label ] == value {
return errors . New ( fmt . Sprintf ( "excluding resource label '%v' while also matching it - please remove from both match and exclude" , label ) )
}
}
}
return nil
}
2020-02-26 16:08:56 +05:30
func ruleOnlyDealsWithResourceMetaData ( rule kyverno . Rule ) bool {
overlayMap , _ := rule . Mutation . Overlay . ( map [ string ] interface { } )
for k := range overlayMap {
if k != "metadata" {
return false
}
}
for _ , patch := range rule . Mutation . Patches {
if ! strings . HasPrefix ( patch . Path , "/metadata" ) {
return false
}
}
patternMap , _ := rule . Validation . Pattern . ( map [ string ] interface { } )
for k := range patternMap {
if k != "metadata" {
return false
}
}
for _ , pattern := range rule . Validation . AnyPattern {
patternMap , _ := pattern . ( map [ string ] interface { } )
for k := range patternMap {
if k != "metadata" {
return false
}
}
}
return true
}
2019-10-21 14:22:31 -07:00
func validateResources ( rule kyverno . Rule ) ( string , error ) {
2019-12-04 18:50:51 -08:00
// validate userInfo in match and exclude
if path , err := validateUserInfo ( rule ) ; err != nil {
return fmt . Sprintf ( "resources.%s" , path ) , err
}
2019-10-21 14:22:31 -07:00
// matched resources
if path , err := validateMatchedResourceDescription ( rule . MatchResources . ResourceDescription ) ; err != nil {
return fmt . Sprintf ( "resources.%s" , path ) , err
2019-09-27 16:31:27 -07:00
}
2019-10-21 14:22:31 -07:00
// exclude resources
if path , err := validateExcludeResourceDescription ( rule . ExcludeResources . ResourceDescription ) ; err != nil {
return fmt . Sprintf ( "resources.%s" , path ) , err
2019-09-27 16:31:27 -07:00
}
2019-10-21 14:22:31 -07:00
return "" , nil
}
2019-09-27 16:31:27 -07:00
2019-10-21 14:22:31 -07:00
// ValidateUniqueRuleName checks if the rule names are unique across a policy
func validateUniqueRuleName ( p kyverno . ClusterPolicy ) ( string , error ) {
var ruleNames [ ] string
2019-09-27 19:03:55 -07:00
2019-10-21 14:22:31 -07:00
for i , rule := range p . Spec . Rules {
if containString ( ruleNames , rule . Name ) {
return fmt . Sprintf ( "rule[%d]" , i ) , fmt . Errorf ( ` duplicate rule name: '%s' ` , rule . Name )
}
ruleNames = append ( ruleNames , rule . Name )
2019-10-03 14:47:50 -07:00
}
2019-10-21 14:22:31 -07:00
return "" , nil
2019-09-27 16:31:27 -07:00
}
// validateRuleType checks only one type of rule is defined per rule
2019-10-18 17:45:24 -07:00
func validateRuleType ( r kyverno . Rule ) error {
ruleTypes := [ ] bool { r . HasMutate ( ) , r . HasValidate ( ) , r . HasGenerate ( ) }
2019-09-27 16:31:27 -07:00
2019-10-03 16:49:41 -07:00
operationCount := func ( ) int {
count := 0
for _ , v := range ruleTypes {
if v {
count ++
}
}
return count
} ( )
2019-09-27 16:31:27 -07:00
2019-10-03 16:49:41 -07:00
if operationCount == 0 {
2019-10-21 14:22:31 -07:00
return fmt . Errorf ( "no operation defined in the rule '%s'.(supported operations: mutation,validation,generation)" , r . Name )
2019-10-03 16:49:41 -07:00
} else if operationCount != 1 {
return fmt . Errorf ( "multiple operations defined in the rule '%s', only one type of operation is allowed per rule" , r . Name )
2019-09-27 16:31:27 -07:00
}
2019-10-03 16:49:41 -07:00
return nil
2019-09-27 16:31:27 -07:00
}
2019-10-03 18:19:47 -07:00
// validateResourceDescription checks if all necesarry fields are present and have values. Also checks a Selector.
2019-09-27 16:31:27 -07:00
// field type is checked through openapi
// Returns error if
2019-10-01 15:01:24 -07:00
// - kinds is empty array in matched resource block, i.e. kinds: []
2019-09-27 16:31:27 -07:00
// - selector is invalid
2019-10-21 14:22:31 -07:00
func validateMatchedResourceDescription ( rd kyverno . ResourceDescription ) ( string , error ) {
2019-10-18 17:45:24 -07:00
if reflect . DeepEqual ( rd , kyverno . ResourceDescription { } ) {
2019-10-21 14:22:31 -07:00
return "" , fmt . Errorf ( "match resources not specified" )
2019-09-27 16:31:27 -07:00
}
2019-10-21 14:22:31 -07:00
if err := validateResourceDescription ( rd ) ; err != nil {
return "match" , err
}
return "" , nil
}
2019-12-04 18:50:51 -08:00
func validateUserInfo ( rule kyverno . Rule ) ( string , error ) {
if err := validateRoles ( rule . MatchResources . Roles ) ; err != nil {
return "match.roles" , err
}
if err := validateSubjects ( rule . MatchResources . Subjects ) ; err != nil {
return "match.subjects" , err
}
if err := validateRoles ( rule . ExcludeResources . Roles ) ; err != nil {
return "exclude.roles" , err
}
if err := validateSubjects ( rule . ExcludeResources . Subjects ) ; err != nil {
return "exclude.subjects" , err
}
return "" , nil
}
// a role must in format namespace:name
func validateRoles ( roles [ ] string ) error {
if len ( roles ) == 0 {
return nil
}
for _ , r := range roles {
role := strings . Split ( r , ":" )
if len ( role ) != 2 {
return fmt . Errorf ( "invalid role %s, expect namespace:name" , r )
}
}
return nil
}
2019-12-05 11:55:00 -08:00
// a namespace should be set in kind ServiceAccount of a subject
2019-12-04 18:50:51 -08:00
func validateSubjects ( subjects [ ] rbacv1 . Subject ) error {
if len ( subjects ) == 0 {
return nil
}
for _ , subject := range subjects {
2019-12-05 11:55:00 -08:00
if subject . Kind == "ServiceAccount" {
2019-12-04 18:50:51 -08:00
if subject . Namespace == "" {
2019-12-05 11:57:34 -08:00
return fmt . Errorf ( "service account %s in subject expects a namespace" , subject . Name )
2019-12-04 18:50:51 -08:00
}
}
}
return nil
}
2019-10-21 14:22:31 -07:00
func validateExcludeResourceDescription ( rd kyverno . ResourceDescription ) ( string , error ) {
if reflect . DeepEqual ( rd , kyverno . ResourceDescription { } ) {
// exclude is not mandatory
return "" , nil
}
if err := validateResourceDescription ( rd ) ; err != nil {
return "exclude" , err
}
return "" , nil
2019-10-03 18:19:47 -07:00
}
// validateResourceDescription returns error if selector is invalid
// field type is checked through openapi
2019-10-18 17:45:24 -07:00
func validateResourceDescription ( rd kyverno . ResourceDescription ) error {
2019-09-27 16:31:27 -07:00
if rd . Selector != nil {
selector , err := metav1 . LabelSelectorAsSelector ( rd . Selector )
if err != nil {
return err
}
requirements , _ := selector . Requirements ( )
if len ( requirements ) == 0 {
return errors . New ( "the requirements are not specified in selector" )
}
}
return nil
}
2019-10-21 14:22:31 -07:00
func validateMutation ( m kyverno . Mutation ) ( string , error ) {
// JSON Patches
2019-10-03 12:52:58 -07:00
if len ( m . Patches ) != 0 {
2019-10-21 14:22:31 -07:00
for i , patch := range m . Patches {
if err := validatePatch ( patch ) ; err != nil {
return fmt . Sprintf ( "patch[%d]" , i ) , err
}
2019-10-03 12:52:58 -07:00
}
}
2019-10-21 14:22:31 -07:00
// Overlay
2019-10-03 12:52:58 -07:00
if m . Overlay != nil {
2019-10-21 14:22:31 -07:00
path , err := validatePattern ( m . Overlay , "/" , [ ] anchor . IsAnchor { anchor . IsConditionAnchor , anchor . IsAddingAnchor } )
2019-10-03 12:52:58 -07:00
if err != nil {
2019-10-21 14:22:31 -07:00
return path , err
2019-10-03 12:52:58 -07:00
}
}
2019-10-21 14:22:31 -07:00
return "" , nil
2019-10-03 12:52:58 -07:00
}
2019-09-27 16:31:27 -07:00
// Validate if all mandatory PolicyPatch fields are set
2019-10-18 17:45:24 -07:00
func validatePatch ( pp kyverno . Patch ) error {
2019-09-27 16:31:27 -07:00
if pp . Path == "" {
return errors . New ( "JSONPatch field 'path' is mandatory" )
}
if pp . Operation == "add" || pp . Operation == "replace" {
if pp . Value == nil {
return fmt . Errorf ( "JSONPatch field 'value' is mandatory for operation '%s'" , pp . Operation )
}
return nil
} else if pp . Operation == "remove" {
return nil
}
return fmt . Errorf ( "Unsupported JSONPatch operation '%s'" , pp . Operation )
}
2019-10-21 14:22:31 -07:00
func validateValidation ( v kyverno . Validation ) ( string , error ) {
2019-10-18 17:45:24 -07:00
if err := validateOverlayPattern ( v ) ; err != nil {
2019-10-21 14:22:31 -07:00
// no need to proceed ahead
return "" , err
2019-10-03 12:52:58 -07:00
}
if v . Pattern != nil {
2020-01-24 12:05:53 -08:00
if path , err := validatePattern ( v . Pattern , "/" , [ ] anchor . IsAnchor { anchor . IsConditionAnchor , anchor . IsExistenceAnchor , anchor . IsEqualityAnchor , anchor . IsNegationAnchor } ) ; err != nil {
2019-10-21 14:22:31 -07:00
return fmt . Sprintf ( "pattern.%s" , path ) , err
2019-09-27 19:03:55 -07:00
}
}
2019-10-03 12:52:58 -07:00
if len ( v . AnyPattern ) != 0 {
2019-10-21 14:22:31 -07:00
for i , pattern := range v . AnyPattern {
2020-01-24 12:05:53 -08:00
if path , err := validatePattern ( pattern , "/" , [ ] anchor . IsAnchor { anchor . IsConditionAnchor , anchor . IsExistenceAnchor , anchor . IsEqualityAnchor , anchor . IsNegationAnchor } ) ; err != nil {
2019-10-21 14:22:31 -07:00
return fmt . Sprintf ( "anyPattern[%d].%s" , i , path ) , err
2019-09-27 19:03:55 -07:00
}
}
}
2019-10-21 14:22:31 -07:00
return "" , nil
2019-09-27 19:03:55 -07:00
}
2019-10-03 12:52:58 -07:00
// validateOverlayPattern checks one of pattern/anyPattern must exist
2019-10-18 17:45:24 -07:00
func validateOverlayPattern ( v kyverno . Validation ) error {
2019-10-03 12:52:58 -07:00
if v . Pattern == nil && len ( v . AnyPattern ) == 0 {
2019-10-21 14:22:31 -07:00
return fmt . Errorf ( "a pattern or anyPattern must be specified" )
2019-10-03 12:52:58 -07:00
}
if v . Pattern != nil && len ( v . AnyPattern ) != 0 {
2019-10-21 14:22:31 -07:00
return fmt . Errorf ( "only one operation allowed per validation rule(pattern or anyPattern)" )
2019-10-03 12:52:58 -07:00
}
return nil
}
// Validate returns error if generator is configured incompletely
2019-10-21 14:22:31 -07:00
func validateGeneration ( gen kyverno . Generation ) ( string , error ) {
2019-10-03 14:47:50 -07:00
2019-10-18 17:45:24 -07:00
if gen . Data == nil && gen . Clone == ( kyverno . CloneFrom { } ) {
2019-10-21 14:22:31 -07:00
return "" , fmt . Errorf ( "clone or data are required" )
2019-10-03 12:52:58 -07:00
}
2019-10-18 17:45:24 -07:00
if gen . Data != nil && gen . Clone != ( kyverno . CloneFrom { } ) {
2019-10-21 14:22:31 -07:00
return "" , fmt . Errorf ( "only one operation allowed per generate rule(data or clone)" )
2019-10-03 12:52:58 -07:00
}
2019-10-21 14:22:31 -07:00
// check kind is non empty
// check name is non empty
if gen . Name == "" {
return "name" , fmt . Errorf ( "name cannot be empty" )
}
if gen . Kind == "" {
return "kind" , fmt . Errorf ( "kind cannot be empty" )
2019-10-03 14:47:50 -07:00
}
2019-10-18 17:45:24 -07:00
if ! reflect . DeepEqual ( gen . Clone , kyverno . CloneFrom { } ) {
2019-10-21 14:22:31 -07:00
if path , err := validateClone ( gen . Clone ) ; err != nil {
return fmt . Sprintf ( "clone.%s" , path ) , err
}
}
if gen . Data != nil {
//TODO: is this required ?? as anchors can only be on pattern and not resource
// we can add this check by not sure if its needed here
if path , err := validatePattern ( gen . Data , "/" , [ ] anchor . IsAnchor { } ) ; err != nil {
2020-01-24 12:05:53 -08:00
return fmt . Sprintf ( "data.%s" , path ) , fmt . Errorf ( "anchors not supported on generate resources: %v" , err )
2019-10-03 14:47:50 -07:00
}
}
2019-10-21 14:22:31 -07:00
return "" , nil
}
2019-10-03 14:47:50 -07:00
2019-10-21 14:22:31 -07:00
func validateClone ( c kyverno . CloneFrom ) ( string , error ) {
if c . Name == "" {
return "name" , fmt . Errorf ( "name cannot be empty" )
}
if c . Namespace == "" {
return "namespace" , fmt . Errorf ( "namespace cannot be empty" )
}
return "" , nil
2019-10-03 12:52:58 -07:00
}
2019-10-21 14:22:31 -07:00
func validatePattern ( patternElement interface { } , path string , supportedAnchors [ ] anchor . IsAnchor ) ( string , error ) {
switch typedPatternElement := patternElement . ( type ) {
2019-09-27 19:03:55 -07:00
case map [ string ] interface { } :
2019-10-21 14:22:31 -07:00
return validateMap ( typedPatternElement , path , supportedAnchors )
2019-09-27 19:03:55 -07:00
case [ ] interface { } :
2019-10-21 14:22:31 -07:00
return validateArray ( typedPatternElement , path , supportedAnchors )
2019-09-27 19:03:55 -07:00
case string , float64 , int , int64 , bool , nil :
2019-10-21 14:22:31 -07:00
//TODO? check operator
2019-10-03 16:49:41 -07:00
return "" , nil
2019-09-27 19:03:55 -07:00
default :
2019-10-21 14:22:31 -07:00
return path , fmt . Errorf ( "Validation rule failed at '%s', pattern contains unknown type" , path )
2019-09-27 19:03:55 -07:00
}
}
2019-10-21 14:22:31 -07:00
func validateMap ( patternMap map [ string ] interface { } , path string , supportedAnchors [ ] anchor . IsAnchor ) ( string , error ) {
// check if anchors are defined
for key , value := range patternMap {
// if key is anchor
// check regex () -> this is anchor
// ()
// single char ()
2020-01-27 08:58:53 -08:00
re , err := regexp . Compile ( ` ^.?\(.+\)$ ` )
2019-10-21 14:22:31 -07:00
if err != nil {
return path + "/" + key , fmt . Errorf ( "Unable to parse the field %s: %v" , key , err )
2019-10-03 12:52:58 -07:00
}
2020-01-27 08:58:53 -08:00
matched := re . MatchString ( key )
2019-10-21 14:22:31 -07:00
// check the type of anchor
if matched {
// some type of anchor
// check if valid anchor
if ! checkAnchors ( key , supportedAnchors ) {
return path + "/" + key , fmt . Errorf ( "Unsupported anchor %s" , key )
2019-09-27 19:03:55 -07:00
}
2020-01-24 12:05:53 -08:00
// addition check for existence anchor
2019-10-21 14:22:31 -07:00
// value must be of type list
2020-01-24 12:05:53 -08:00
if anchor . IsExistenceAnchor ( key ) {
2019-10-21 14:22:31 -07:00
typedValue , ok := value . ( [ ] interface { } )
if ! ok {
2020-01-24 12:05:53 -08:00
return path + "/" + key , fmt . Errorf ( "Existence anchor should have value of type list" )
2019-10-21 14:22:31 -07:00
}
// validate there is only one entry in the list
if len ( typedValue ) == 0 || len ( typedValue ) > 1 {
2020-01-24 12:05:53 -08:00
return path + "/" + key , fmt . Errorf ( "Existence anchor: single value expected, multiple specified" )
2019-10-21 14:22:31 -07:00
}
}
}
// lets validate the values now :)
if errPath , err := validatePattern ( value , path + "/" + key , supportedAnchors ) ; err != nil {
return errPath , err
2019-09-27 19:03:55 -07:00
}
}
return "" , nil
}
2019-10-21 14:22:31 -07:00
func validateArray ( patternArray [ ] interface { } , path string , supportedAnchors [ ] anchor . IsAnchor ) ( string , error ) {
for i , patternElement := range patternArray {
2019-09-27 19:03:55 -07:00
currentPath := path + strconv . Itoa ( i ) + "/"
2019-10-21 14:22:31 -07:00
// lets validate the values now :)
if errPath , err := validatePattern ( patternElement , currentPath , supportedAnchors ) ; err != nil {
return errPath , err
2019-09-27 19:03:55 -07:00
}
}
return "" , nil
}
2019-10-21 14:22:31 -07:00
func checkAnchors ( key string , supportedAnchors [ ] anchor . IsAnchor ) bool {
for _ , f := range supportedAnchors {
if f ( key ) {
return true
}
}
return false
}