mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 22:17:14 +00:00
support NonResourceURL for kubectl create clusterrole
This commit is contained in:
parent
749ac27e9a
commit
93e50b167e
@ -3125,6 +3125,9 @@ runTests() {
|
||||
kube::test::get_object_assert clusterrole/resourcename-reader "{{range.rules}}{{range.resources}}{{.}}:{{end}}{{end}}" 'pods:'
|
||||
kube::test::get_object_assert clusterrole/resourcename-reader "{{range.rules}}{{range.apiGroups}}{{.}}:{{end}}{{end}}" ':'
|
||||
kube::test::get_object_assert clusterrole/resourcename-reader "{{range.rules}}{{range.resourceNames}}{{.}}:{{end}}{{end}}" 'foo:'
|
||||
kubectl create "${kube_flags[@]}" clusterrole url-reader --verb=get --non-resource-url=/logs/* --non-resource-url=/healthz/*
|
||||
kube::test::get_object_assert clusterrole/url-reader "{{range.rules}}{{range.verbs}}{{.}}:{{end}}{{end}}" 'get:'
|
||||
kube::test::get_object_assert clusterrole/url-reader "{{range.rules}}{{range.nonResourceURLs}}{{.}}:{{end}}{{end}}" '/logs/\*:/healthz/\*:'
|
||||
|
||||
# test `kubectl create rolebinding/clusterrolebinding`
|
||||
# test `kubectl set subject rolebinding/clusterrolebinding`
|
||||
|
@ -508,6 +508,7 @@ node-sync-period
|
||||
no-headers
|
||||
no-headers
|
||||
non-masquerade-cidr
|
||||
non-resource-url
|
||||
no-suggestions
|
||||
no-suggestions
|
||||
num-nodes
|
||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@ -42,11 +43,18 @@ var (
|
||||
kubectl create clusterrole foo --verb=get,list,watch --resource=rs.extensions
|
||||
|
||||
# Create a ClusterRole named "foo" with SubResource specified
|
||||
kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status`))
|
||||
kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
|
||||
|
||||
# Create a ClusterRole name "foo" with NonResourceURL specified
|
||||
kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*`))
|
||||
|
||||
// Valid nonResource verb list for validation.
|
||||
validNonResourceVerbs = []string{"*", "get", "post", "put", "delete", "patch", "head", "options"}
|
||||
)
|
||||
|
||||
type CreateClusterRoleOptions struct {
|
||||
*CreateRoleOptions
|
||||
NonResourceURLs []string
|
||||
}
|
||||
|
||||
// ClusterRole is a command to ease creating ClusterRoles.
|
||||
@ -72,16 +80,69 @@ func NewCmdCreateClusterRole(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
cmd.Flags().StringSliceVar(&c.Verbs, "verb", []string{}, "verb that applies to the resources contained in the rule")
|
||||
cmd.Flags().StringSliceVar(&c.NonResourceURLs, "non-resource-url", []string{}, "a partial url that user should have access to.")
|
||||
cmd.Flags().StringSlice("resource", []string{}, "resource that the rule applies to")
|
||||
cmd.Flags().StringArrayVar(&c.ResourceNames, "resource-name", []string{}, "resource in the white list that the rule applies to, repeat this flag for multiple items")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *CreateClusterRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
// Remove duplicate nonResourceURLs
|
||||
nonResourceURLs := []string{}
|
||||
for _, n := range c.NonResourceURLs {
|
||||
if !arrayContains(nonResourceURLs, n) {
|
||||
nonResourceURLs = append(nonResourceURLs, n)
|
||||
}
|
||||
}
|
||||
c.NonResourceURLs = nonResourceURLs
|
||||
|
||||
return c.CreateRoleOptions.Complete(f, cmd, args)
|
||||
}
|
||||
|
||||
func (c *CreateClusterRoleOptions) Validate() error {
|
||||
if c.Name == "" {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
|
||||
// validate verbs.
|
||||
if len(c.Verbs) == 0 {
|
||||
return fmt.Errorf("at least one verb must be specified")
|
||||
}
|
||||
|
||||
if len(c.Resources) == 0 && len(c.NonResourceURLs) == 0 {
|
||||
return fmt.Errorf("one of resource or nonResourceURL must be specified")
|
||||
}
|
||||
|
||||
// validate resources
|
||||
if len(c.Resources) > 0 {
|
||||
for _, v := range c.Verbs {
|
||||
if !arrayContains(validResourceVerbs, v) {
|
||||
return fmt.Errorf("invalid verb: '%s'", v)
|
||||
}
|
||||
}
|
||||
if err := c.validateResource(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
//validate non-resource-url
|
||||
if len(c.NonResourceURLs) > 0 {
|
||||
for _, v := range c.Verbs {
|
||||
if !arrayContains(validNonResourceVerbs, v) {
|
||||
return fmt.Errorf("invalid verb: '%s' for nonResourceURL", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (c *CreateClusterRoleOptions) RunCreateRole() error {
|
||||
clusterRole := &rbac.ClusterRole{}
|
||||
clusterRole.Name = c.Name
|
||||
rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames)
|
||||
rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames, c.NonResourceURLs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -232,6 +232,10 @@ func (c *CreateRoleOptions) Validate() error {
|
||||
return fmt.Errorf("at least one resource must be specified")
|
||||
}
|
||||
|
||||
return c.validateResource()
|
||||
}
|
||||
|
||||
func (c *CreateRoleOptions) validateResource() error {
|
||||
for _, r := range c.Resources {
|
||||
if len(r.Resource) == 0 {
|
||||
return fmt.Errorf("resource must be specified if apiGroup/subresource specified")
|
||||
@ -263,14 +267,13 @@ func (c *CreateRoleOptions) Validate() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CreateRoleOptions) RunCreateRole() error {
|
||||
role := &rbac.Role{}
|
||||
role.Name = c.Name
|
||||
rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames)
|
||||
rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -301,7 +304,7 @@ func arrayContains(s []string, e string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func generateResourcePolicyRules(mapper meta.RESTMapper, verbs []string, resources []ResourceOptions, resourceNames []string) ([]rbac.PolicyRule, error) {
|
||||
func generateResourcePolicyRules(mapper meta.RESTMapper, verbs []string, resources []ResourceOptions, resourceNames []string, nonResourceURLs []string) ([]rbac.PolicyRule, error) {
|
||||
// groupResourceMapping is a apigroup-resource map. The key of this map is api group, while the value
|
||||
// is a string array of resources under this api group.
|
||||
// E.g. groupResourceMapping = {"extensions": ["replicasets", "deployments"], "batch":["jobs"]}
|
||||
@ -337,5 +340,12 @@ func generateResourcePolicyRules(mapper meta.RESTMapper, verbs []string, resourc
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
|
||||
if len(nonResourceURLs) > 0 {
|
||||
rule := rbac.PolicyRule{}
|
||||
rule.Verbs = verbs
|
||||
rule.NonResourceURLs = nonResourceURLs
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user