Ctrl K
ComunidadGuías…

internal/rbac/ask.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package rbac

import (
	"slices"
	"strings"
)

type Ask struct {
	Verb      string
	Group     string
	Resource  string
	Namespace string
}

func (g Grant) Reaches(ask Ask) bool {
	if !g.covers(ask.Namespace) {
		return false
	}
	for _, rule := range g.Rules {
		if rule.Allows(ask) {
			return true
		}
	}
	return false
}

func (g Grant) covers(namespace string) bool {
	if g.Everywhere() {
		return true
	}
	if namespace == "" {
		return true
	}
	return g.Namespace == namespace
}

func (r Rule) Allows(ask Ask) bool {
	if len(r.Resources) == 0 {
		return false
	}
	if len(r.Names) > 0 {
		return false
	}
	if !covers(r.Verbs, ask.Verb) {
		return false
	}
	if !covers(r.Groups, ask.Group) {
		return false
	}
	return coversResource(r.Resources, ask.Resource)
}

func coversResource(held []string, wanted string) bool {
	if slices.Contains(held, Anything) {
		return true
	}
	if slices.Contains(held, wanted) {
		return true
	}
	base, sub, split := strings.Cut(wanted, "/")
	if !split {
		return false
	}
	return slices.Contains(held, base+"/"+Anything) || slices.Contains(held, Anything+"/"+sub)
}

func covers(held []string, wanted string) bool {
	return slices.Contains(held, Anything) || slices.Contains(held, wanted)
}

func (i Index) Who(ask Ask) []Holder {
	out := []Holder{}
	for _, holder := range i.Holders {
		kept := []Grant{}
		for _, grant := range holder.Grants {
			if grant.Reaches(ask) {
				kept = append(kept, grant)
			}
		}
		if len(kept) == 0 {
			continue
		}
		holder.Grants = kept
		out = append(out, holder)
	}
	return out
}

var powers = []struct {
	label string
	ask   Ask
}{
	{"cluster-admin", Ask{Verb: Anything, Group: Anything, Resource: Anything}},
	{"reads secrets", Ask{Verb: "get", Resource: "secrets"}},
	{"lists secrets", Ask{Verb: "list", Resource: "secrets"}},
	{"execs into pods", Ask{Verb: "create", Resource: "pods/exec"}},
	{"creates pods", Ask{Verb: "create", Resource: "pods"}},
	{"impersonates", Ask{Verb: "impersonate", Resource: "users"}},
	{"writes RBAC", Ask{Verb: "create", Group: Group, Resource: "clusterrolebindings"}},
	{"escalates", Ask{Verb: "escalate", Group: Group, Resource: "clusterroles"}},
	{"reads every kind", Ask{Verb: "get", Group: Anything, Resource: Anything}},
}

func powersOf(holder Holder) []string {
	out := []string{}
	for _, power := range powers {
		if !reaches(holder, power.ask) {
			continue
		}
		if power.label == "cluster-admin" {
			return []string{"cluster-admin"}
		}
		out = append(out, power.label)
	}
	return out
}

func reaches(holder Holder, ask Ask) bool {
	for _, grant := range holder.Grants {
		if grant.Reaches(ask) {
			return true
		}
	}
	return false
}

func (h Holder) Namespaces() []string {
	held := map[string]struct{}{}
	for _, grant := range h.Grants {
		if grant.Everywhere() {
			return nil
		}
		held[grant.Namespace] = struct{}{}
	}
	return sortedKeys(held)
}