...

Source file src/k8s.io/kubectl/pkg/util/rbac/rbac.go

Documentation: k8s.io/kubectl/pkg/util/rbac

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package rbac
    18  
    19  import (
    20  	rbacv1 "k8s.io/api/rbac/v1"
    21  	"k8s.io/apimachinery/pkg/util/sets"
    22  	"reflect"
    23  	"strings"
    24  )
    25  
    26  type simpleResource struct {
    27  	Group             string
    28  	Resource          string
    29  	ResourceNameExist bool
    30  	ResourceName      string
    31  }
    32  
    33  // CompactRules combines rules that contain a single APIGroup/Resource, differ only by verb, and contain no other attributes.
    34  // this is a fast check, and works well with the decomposed "missing rules" list from a Covers check.
    35  func CompactRules(rules []rbacv1.PolicyRule) ([]rbacv1.PolicyRule, error) {
    36  	compacted := make([]rbacv1.PolicyRule, 0, len(rules))
    37  
    38  	simpleRules := map[simpleResource]*rbacv1.PolicyRule{}
    39  	for _, rule := range rules {
    40  		if resource, isSimple := isSimpleResourceRule(&rule); isSimple {
    41  			if existingRule, ok := simpleRules[resource]; ok {
    42  				// Add the new verbs to the existing simple resource rule
    43  				if existingRule.Verbs == nil {
    44  					existingRule.Verbs = []string{}
    45  				}
    46  				existingVerbs := sets.NewString(existingRule.Verbs...)
    47  				for _, verb := range rule.Verbs {
    48  					if !existingVerbs.Has(verb) {
    49  						existingRule.Verbs = append(existingRule.Verbs, verb)
    50  					}
    51  				}
    52  
    53  			} else {
    54  				// Copy the rule to accumulate matching simple resource rules into
    55  				simpleRules[resource] = rule.DeepCopy()
    56  			}
    57  		} else {
    58  			compacted = append(compacted, rule)
    59  		}
    60  	}
    61  
    62  	// Once we've consolidated the simple resource rules, add them to the compacted list
    63  	for _, simpleRule := range simpleRules {
    64  		compacted = append(compacted, *simpleRule)
    65  	}
    66  
    67  	return compacted, nil
    68  }
    69  
    70  // isSimpleResourceRule returns true if the given rule contains verbs, a single resource, a single API group, at most one Resource Name, and no other values
    71  func isSimpleResourceRule(rule *rbacv1.PolicyRule) (simpleResource, bool) {
    72  	resource := simpleResource{}
    73  
    74  	// If we have "complex" rule attributes, return early without allocations or expensive comparisons
    75  	if len(rule.ResourceNames) > 1 || len(rule.NonResourceURLs) > 0 {
    76  		return resource, false
    77  	}
    78  	// If we have multiple api groups or resources, return early
    79  	if len(rule.APIGroups) != 1 || len(rule.Resources) != 1 {
    80  		return resource, false
    81  	}
    82  
    83  	// Test if this rule only contains APIGroups/Resources/Verbs/ResourceNames
    84  	simpleRule := &rbacv1.PolicyRule{APIGroups: rule.APIGroups, Resources: rule.Resources, Verbs: rule.Verbs, ResourceNames: rule.ResourceNames}
    85  	if !reflect.DeepEqual(simpleRule, rule) {
    86  		return resource, false
    87  	}
    88  
    89  	if len(rule.ResourceNames) == 0 {
    90  		resource = simpleResource{Group: rule.APIGroups[0], Resource: rule.Resources[0], ResourceNameExist: false}
    91  	} else {
    92  		resource = simpleResource{Group: rule.APIGroups[0], Resource: rule.Resources[0], ResourceNameExist: true, ResourceName: rule.ResourceNames[0]}
    93  	}
    94  
    95  	return resource, true
    96  }
    97  
    98  // BreakdownRule takes a rule and builds an equivalent list of rules that each have at most one verb, one
    99  // resource, and one resource name
   100  func BreakdownRule(rule rbacv1.PolicyRule) []rbacv1.PolicyRule {
   101  	subrules := []rbacv1.PolicyRule{}
   102  	for _, group := range rule.APIGroups {
   103  		for _, resource := range rule.Resources {
   104  			for _, verb := range rule.Verbs {
   105  				if len(rule.ResourceNames) > 0 {
   106  					for _, resourceName := range rule.ResourceNames {
   107  						subrules = append(subrules, rbacv1.PolicyRule{APIGroups: []string{group}, Resources: []string{resource}, Verbs: []string{verb}, ResourceNames: []string{resourceName}})
   108  					}
   109  
   110  				} else {
   111  					subrules = append(subrules, rbacv1.PolicyRule{APIGroups: []string{group}, Resources: []string{resource}, Verbs: []string{verb}})
   112  				}
   113  
   114  			}
   115  		}
   116  	}
   117  
   118  	// Non-resource URLs are unique because they only combine with verbs.
   119  	for _, nonResourceURL := range rule.NonResourceURLs {
   120  		for _, verb := range rule.Verbs {
   121  			subrules = append(subrules, rbacv1.PolicyRule{NonResourceURLs: []string{nonResourceURL}, Verbs: []string{verb}})
   122  		}
   123  	}
   124  
   125  	return subrules
   126  }
   127  
   128  // SortableRuleSlice is used to sort rule slice
   129  type SortableRuleSlice []rbacv1.PolicyRule
   130  
   131  func (s SortableRuleSlice) Len() int      { return len(s) }
   132  func (s SortableRuleSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   133  func (s SortableRuleSlice) Less(i, j int) bool {
   134  	return strings.Compare(s[i].String(), s[j].String()) < 0
   135  }
   136  

View as plain text