...

Source file src/k8s.io/kubernetes/pkg/apis/rbac/helpers.go

Documentation: k8s.io/kubernetes/pkg/apis/rbac

     1  /*
     2  Copyright 2016 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  	"fmt"
    21  	"strings"
    22  
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"k8s.io/apimachinery/pkg/util/sets"
    25  )
    26  
    27  // ResourceMatches returns the result of the rule.Resources matching.
    28  func ResourceMatches(rule *PolicyRule, combinedRequestedResource, requestedSubresource string) bool {
    29  	for _, ruleResource := range rule.Resources {
    30  		// if everything is allowed, we match
    31  		if ruleResource == ResourceAll {
    32  			return true
    33  		}
    34  		// if we have an exact match, we match
    35  		if ruleResource == combinedRequestedResource {
    36  			return true
    37  		}
    38  
    39  		// We can also match a */subresource.
    40  		// if there isn't a subresource, then continue
    41  		if len(requestedSubresource) == 0 {
    42  			continue
    43  		}
    44  		// if the rule isn't in the format */subresource, then we don't match, continue
    45  		if len(ruleResource) == len(requestedSubresource)+2 &&
    46  			strings.HasPrefix(ruleResource, "*/") &&
    47  			strings.HasSuffix(ruleResource, requestedSubresource) {
    48  			return true
    49  
    50  		}
    51  	}
    52  
    53  	return false
    54  }
    55  
    56  // SubjectsStrings returns users, groups, serviceaccounts, unknown for display purposes.
    57  func SubjectsStrings(subjects []Subject) ([]string, []string, []string, []string) {
    58  	users := []string{}
    59  	groups := []string{}
    60  	sas := []string{}
    61  	others := []string{}
    62  
    63  	for _, subject := range subjects {
    64  		switch subject.Kind {
    65  		case ServiceAccountKind:
    66  			sas = append(sas, fmt.Sprintf("%s/%s", subject.Namespace, subject.Name))
    67  
    68  		case UserKind:
    69  			users = append(users, subject.Name)
    70  
    71  		case GroupKind:
    72  			groups = append(groups, subject.Name)
    73  
    74  		default:
    75  			others = append(others, fmt.Sprintf("%s/%s/%s", subject.Kind, subject.Namespace, subject.Name))
    76  		}
    77  	}
    78  
    79  	return users, groups, sas, others
    80  }
    81  
    82  func (r PolicyRule) String() string {
    83  	return "PolicyRule" + r.CompactString()
    84  }
    85  
    86  // CompactString exposes a compact string representation for use in escalation error messages
    87  func (r PolicyRule) CompactString() string {
    88  	formatStringParts := []string{}
    89  	formatArgs := []interface{}{}
    90  	if len(r.APIGroups) > 0 {
    91  		formatStringParts = append(formatStringParts, "APIGroups:%q")
    92  		formatArgs = append(formatArgs, r.APIGroups)
    93  	}
    94  	if len(r.Resources) > 0 {
    95  		formatStringParts = append(formatStringParts, "Resources:%q")
    96  		formatArgs = append(formatArgs, r.Resources)
    97  	}
    98  	if len(r.NonResourceURLs) > 0 {
    99  		formatStringParts = append(formatStringParts, "NonResourceURLs:%q")
   100  		formatArgs = append(formatArgs, r.NonResourceURLs)
   101  	}
   102  	if len(r.ResourceNames) > 0 {
   103  		formatStringParts = append(formatStringParts, "ResourceNames:%q")
   104  		formatArgs = append(formatArgs, r.ResourceNames)
   105  	}
   106  	if len(r.Verbs) > 0 {
   107  		formatStringParts = append(formatStringParts, "Verbs:%q")
   108  		formatArgs = append(formatArgs, r.Verbs)
   109  	}
   110  	formatString := "{" + strings.Join(formatStringParts, ", ") + "}"
   111  	return fmt.Sprintf(formatString, formatArgs...)
   112  }
   113  
   114  // PolicyRuleBuilder let's us attach methods.  A no-no for API types.
   115  // We use it to construct rules in code.  It's more compact than trying to write them
   116  // out in a literal and allows us to perform some basic checking during construction
   117  // +k8s:deepcopy-gen=false
   118  type PolicyRuleBuilder struct {
   119  	PolicyRule PolicyRule
   120  }
   121  
   122  // NewRule returns new PolicyRule made by input verbs.
   123  func NewRule(verbs ...string) *PolicyRuleBuilder {
   124  	return &PolicyRuleBuilder{
   125  		PolicyRule: PolicyRule{Verbs: sets.NewString(verbs...).List()},
   126  	}
   127  }
   128  
   129  // Groups combines the PolicyRule.APIGroups and input groups.
   130  func (r *PolicyRuleBuilder) Groups(groups ...string) *PolicyRuleBuilder {
   131  	r.PolicyRule.APIGroups = combine(r.PolicyRule.APIGroups, groups)
   132  	return r
   133  }
   134  
   135  // Resources combines the PolicyRule.Rule and input resources.
   136  func (r *PolicyRuleBuilder) Resources(resources ...string) *PolicyRuleBuilder {
   137  	r.PolicyRule.Resources = combine(r.PolicyRule.Resources, resources)
   138  	return r
   139  }
   140  
   141  // Names combines the PolicyRule.ResourceNames and input names.
   142  func (r *PolicyRuleBuilder) Names(names ...string) *PolicyRuleBuilder {
   143  	r.PolicyRule.ResourceNames = combine(r.PolicyRule.ResourceNames, names)
   144  	return r
   145  }
   146  
   147  // URLs combines the PolicyRule.NonResourceURLs and input urls.
   148  func (r *PolicyRuleBuilder) URLs(urls ...string) *PolicyRuleBuilder {
   149  	r.PolicyRule.NonResourceURLs = combine(r.PolicyRule.NonResourceURLs, urls)
   150  	return r
   151  }
   152  
   153  // RuleOrDie calls the binding method and panics if there is an error.
   154  func (r *PolicyRuleBuilder) RuleOrDie() PolicyRule {
   155  	ret, err := r.Rule()
   156  	if err != nil {
   157  		panic(err)
   158  	}
   159  	return ret
   160  }
   161  
   162  func combine(s1, s2 []string) []string {
   163  	s := sets.NewString(s1...)
   164  	s.Insert(s2...)
   165  	return s.List()
   166  }
   167  
   168  // Rule returns PolicyRule and error.
   169  func (r *PolicyRuleBuilder) Rule() (PolicyRule, error) {
   170  	if len(r.PolicyRule.Verbs) == 0 {
   171  		return PolicyRule{}, fmt.Errorf("verbs are required: %#v", r.PolicyRule)
   172  	}
   173  
   174  	switch {
   175  	case len(r.PolicyRule.NonResourceURLs) > 0:
   176  		if len(r.PolicyRule.APIGroups) != 0 || len(r.PolicyRule.Resources) != 0 || len(r.PolicyRule.ResourceNames) != 0 {
   177  			return PolicyRule{}, fmt.Errorf("non-resource rule may not have apiGroups, resources, or resourceNames: %#v", r.PolicyRule)
   178  		}
   179  	case len(r.PolicyRule.Resources) > 0:
   180  		// resource rule may not have nonResourceURLs
   181  
   182  		if len(r.PolicyRule.APIGroups) == 0 {
   183  			// this a common bug
   184  			return PolicyRule{}, fmt.Errorf("resource rule must have apiGroups: %#v", r.PolicyRule)
   185  		}
   186  		// if resource names are set, then the verb must not be list, watch, create, or deletecollection
   187  		// since verbs are largely opaque, we don't want to accidentally prevent things like "impersonate", so
   188  		// we will backlist common mistakes, not whitelist acceptable options.
   189  		if len(r.PolicyRule.ResourceNames) != 0 {
   190  			illegalVerbs := []string{}
   191  			for _, verb := range r.PolicyRule.Verbs {
   192  				switch verb {
   193  				case "list", "watch", "create", "deletecollection":
   194  					illegalVerbs = append(illegalVerbs, verb)
   195  				}
   196  			}
   197  			if len(illegalVerbs) > 0 {
   198  				return PolicyRule{}, fmt.Errorf("verbs %v do not have names available: %#v", illegalVerbs, r.PolicyRule)
   199  			}
   200  		}
   201  
   202  	default:
   203  		return PolicyRule{}, fmt.Errorf("a rule must have either nonResourceURLs or resources: %#v", r.PolicyRule)
   204  	}
   205  
   206  	return r.PolicyRule, nil
   207  }
   208  
   209  // ClusterRoleBindingBuilder let's us attach methods.  A no-no for API types.
   210  // We use it to construct bindings in code.  It's more compact than trying to write them
   211  // out in a literal.
   212  // +k8s:deepcopy-gen=false
   213  type ClusterRoleBindingBuilder struct {
   214  	ClusterRoleBinding ClusterRoleBinding
   215  }
   216  
   217  // NewClusterBinding creates a ClusterRoleBinding builder that can be used
   218  // to define the subjects of a cluster role binding. At least one of
   219  // the `Groups`, `Users` or `SAs` method must be called before
   220  // calling the `Binding*` methods.
   221  func NewClusterBinding(clusterRoleName string) *ClusterRoleBindingBuilder {
   222  	return &ClusterRoleBindingBuilder{
   223  		ClusterRoleBinding: ClusterRoleBinding{
   224  			ObjectMeta: metav1.ObjectMeta{Name: clusterRoleName},
   225  			RoleRef: RoleRef{
   226  				APIGroup: GroupName,
   227  				Kind:     "ClusterRole",
   228  				Name:     clusterRoleName,
   229  			},
   230  		},
   231  	}
   232  }
   233  
   234  // Groups adds the specified groups as the subjects of the ClusterRoleBinding.
   235  func (r *ClusterRoleBindingBuilder) Groups(groups ...string) *ClusterRoleBindingBuilder {
   236  	for _, group := range groups {
   237  		r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, Subject{Kind: GroupKind, APIGroup: GroupName, Name: group})
   238  	}
   239  	return r
   240  }
   241  
   242  // Users adds the specified users as the subjects of the ClusterRoleBinding.
   243  func (r *ClusterRoleBindingBuilder) Users(users ...string) *ClusterRoleBindingBuilder {
   244  	for _, user := range users {
   245  		r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, Subject{Kind: UserKind, APIGroup: GroupName, Name: user})
   246  	}
   247  	return r
   248  }
   249  
   250  // SAs adds the specified sas as the subjects of the ClusterRoleBinding.
   251  func (r *ClusterRoleBindingBuilder) SAs(namespace string, serviceAccountNames ...string) *ClusterRoleBindingBuilder {
   252  	for _, saName := range serviceAccountNames {
   253  		r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, Subject{Kind: ServiceAccountKind, Namespace: namespace, Name: saName})
   254  	}
   255  	return r
   256  }
   257  
   258  // BindingOrDie calls the binding method and panics if there is an error.
   259  func (r *ClusterRoleBindingBuilder) BindingOrDie() ClusterRoleBinding {
   260  	ret, err := r.Binding()
   261  	if err != nil {
   262  		panic(err)
   263  	}
   264  	return ret
   265  }
   266  
   267  // Binding builds and returns the ClusterRoleBinding API object from the builder
   268  // object.
   269  func (r *ClusterRoleBindingBuilder) Binding() (ClusterRoleBinding, error) {
   270  	if len(r.ClusterRoleBinding.Subjects) == 0 {
   271  		return ClusterRoleBinding{}, fmt.Errorf("subjects are required: %#v", r.ClusterRoleBinding)
   272  	}
   273  
   274  	return r.ClusterRoleBinding, nil
   275  }
   276  
   277  // RoleBindingBuilder let's us attach methods. It is similar to
   278  // ClusterRoleBindingBuilder above.
   279  // +k8s:deepcopy-gen=false
   280  type RoleBindingBuilder struct {
   281  	RoleBinding RoleBinding
   282  }
   283  
   284  // NewRoleBinding creates a RoleBinding builder that can be used
   285  // to define the subjects of a role binding. At least one of
   286  // the `Groups`, `Users` or `SAs` method must be called before
   287  // calling the `Binding*` methods.
   288  func NewRoleBinding(roleName, namespace string) *RoleBindingBuilder {
   289  	return &RoleBindingBuilder{
   290  		RoleBinding: RoleBinding{
   291  			ObjectMeta: metav1.ObjectMeta{
   292  				Name:      roleName,
   293  				Namespace: namespace,
   294  			},
   295  			RoleRef: RoleRef{
   296  				APIGroup: GroupName,
   297  				Kind:     "Role",
   298  				Name:     roleName,
   299  			},
   300  		},
   301  	}
   302  }
   303  
   304  // NewRoleBindingForClusterRole creates a RoleBinding builder that can be used
   305  // to define the subjects of a cluster role binding. At least one of
   306  // the `Groups`, `Users` or `SAs` method must be called before
   307  // calling the `Binding*` methods.
   308  func NewRoleBindingForClusterRole(roleName, namespace string) *RoleBindingBuilder {
   309  	return &RoleBindingBuilder{
   310  		RoleBinding: RoleBinding{
   311  			ObjectMeta: metav1.ObjectMeta{
   312  				Name:      roleName,
   313  				Namespace: namespace,
   314  			},
   315  			RoleRef: RoleRef{
   316  				APIGroup: GroupName,
   317  				Kind:     "ClusterRole",
   318  				Name:     roleName,
   319  			},
   320  		},
   321  	}
   322  }
   323  
   324  // Groups adds the specified groups as the subjects of the RoleBinding.
   325  func (r *RoleBindingBuilder) Groups(groups ...string) *RoleBindingBuilder {
   326  	for _, group := range groups {
   327  		r.RoleBinding.Subjects = append(r.RoleBinding.Subjects, Subject{Kind: GroupKind, APIGroup: GroupName, Name: group})
   328  	}
   329  	return r
   330  }
   331  
   332  // Users adds the specified users as the subjects of the RoleBinding.
   333  func (r *RoleBindingBuilder) Users(users ...string) *RoleBindingBuilder {
   334  	for _, user := range users {
   335  		r.RoleBinding.Subjects = append(r.RoleBinding.Subjects, Subject{Kind: UserKind, APIGroup: GroupName, Name: user})
   336  	}
   337  	return r
   338  }
   339  
   340  // SAs adds the specified service accounts as the subjects of the
   341  // RoleBinding.
   342  func (r *RoleBindingBuilder) SAs(namespace string, serviceAccountNames ...string) *RoleBindingBuilder {
   343  	for _, saName := range serviceAccountNames {
   344  		r.RoleBinding.Subjects = append(r.RoleBinding.Subjects, Subject{Kind: ServiceAccountKind, Namespace: namespace, Name: saName})
   345  	}
   346  	return r
   347  }
   348  
   349  // BindingOrDie calls the binding method and panics if there is an error.
   350  func (r *RoleBindingBuilder) BindingOrDie() RoleBinding {
   351  	ret, err := r.Binding()
   352  	if err != nil {
   353  		panic(err)
   354  	}
   355  	return ret
   356  }
   357  
   358  // Binding builds and returns the RoleBinding API object from the builder
   359  // object.
   360  func (r *RoleBindingBuilder) Binding() (RoleBinding, error) {
   361  	if len(r.RoleBinding.Subjects) == 0 {
   362  		return RoleBinding{}, fmt.Errorf("subjects are required: %#v", r.RoleBinding)
   363  	}
   364  
   365  	return r.RoleBinding, nil
   366  }
   367  
   368  // SortableRuleSlice is the slice of PolicyRule.
   369  type SortableRuleSlice []PolicyRule
   370  
   371  func (s SortableRuleSlice) Len() int      { return len(s) }
   372  func (s SortableRuleSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   373  func (s SortableRuleSlice) Less(i, j int) bool {
   374  	return strings.Compare(s[i].String(), s[j].String()) < 0
   375  }
   376  

View as plain text