...

Source file src/edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/approval/parse.go

Documentation: edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/approval

     1  // Copyright 2018 Palantir Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package approval
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"github.com/pkg/errors"
    21  
    22  	"edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/common"
    23  )
    24  
    25  type Policy []interface{}
    26  
    27  func (p Policy) Parse(rules map[string]*Rule) (common.Evaluator, error) {
    28  	eval := &evaluator{}
    29  
    30  	if len(p) == 0 {
    31  		return eval, nil
    32  	}
    33  
    34  	// assume "and" for the list of rules
    35  	root := map[interface{}]interface{}{
    36  		"and": []interface{}(p),
    37  	}
    38  
    39  	and, err := parsePolicyR(root, rules, 0)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	eval.root = and
    45  	return eval, nil
    46  }
    47  
    48  func parsePolicyR(policy interface{}, rules map[string]*Rule, depth int) (common.Evaluator, error) {
    49  	if depth > 10 {
    50  		return nil, errors.New("reached maximum recursive depth while processing policy")
    51  	}
    52  
    53  	// Base case
    54  	if ruleName, ok := policy.(string); ok {
    55  		if rule, ok := rules[ruleName]; ok {
    56  			req := &RuleRequirement{
    57  				rule: rule,
    58  			}
    59  			return req, nil
    60  		}
    61  		var keys []string
    62  		for k := range rules {
    63  			keys = append(keys, k)
    64  		}
    65  		return nil, errors.Errorf("policy references undefined rule '%s', allowed values: %v", ruleName, keys)
    66  	}
    67  
    68  	// Recursive case, we have "or:", "and:", etc.
    69  	if conjunction, ok := policy.(map[interface{}]interface{}); ok {
    70  		var ops []string
    71  		for k := range conjunction {
    72  			ops = append(ops, k.(string))
    73  		}
    74  		if len(ops) != 1 {
    75  			return nil, errors.Errorf("multiple keys found when one was expected: %v", ops)
    76  		}
    77  
    78  		op := ops[0]
    79  		values, ok := conjunction[op].([]interface{})
    80  		if !ok {
    81  			return nil, errors.Errorf("expected list of subconditions, but got %T", conjunction[op])
    82  		}
    83  		if len(values) == 0 {
    84  			return nil, errors.Errorf("empty list of subconditions is not allowed")
    85  		}
    86  
    87  		var subrequirements []common.Evaluator
    88  		for _, subpolicy := range values {
    89  			subreq, err := parsePolicyR(subpolicy, rules, depth+1)
    90  			if err != nil {
    91  				return nil, errors.WithMessage(err, fmt.Sprintf("failed to parse subpolicies for '%s'", op))
    92  			}
    93  			subrequirements = append(subrequirements, subreq)
    94  		}
    95  
    96  		switch op {
    97  		case "or":
    98  			return &OrRequirement{requirements: subrequirements}, nil
    99  		case "and":
   100  			return &AndRequirement{requirements: subrequirements}, nil
   101  		default:
   102  			return nil, errors.Errorf("invalid conjunction '%s', allowed values: [or, and]", op)
   103  		}
   104  	}
   105  
   106  	return nil, errors.Errorf("malformed policy, expected string or map, but encountered %T", policy)
   107  }
   108  

View as plain text