383 lines
9.2 KiB
Go
383 lines
9.2 KiB
Go
package view
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/derailed/k9s/internal/dao"
|
|
"github.com/derailed/k9s/internal/render"
|
|
"github.com/derailed/k9s/internal/ui"
|
|
"github.com/gdamore/tcell"
|
|
)
|
|
|
|
const (
|
|
policyTitle = "Policy"
|
|
group = "Group"
|
|
user = "User"
|
|
sa = "ServiceAccount"
|
|
allVerbs = "*"
|
|
)
|
|
|
|
type (
|
|
namespacedRole struct {
|
|
ns, role string
|
|
}
|
|
|
|
// Policy presents a RBAC policy viewer.
|
|
Policy struct {
|
|
ResourceViewer
|
|
}
|
|
)
|
|
|
|
// NewPolicy returns a new viewer.
|
|
func NewPolicy(gvr dao.GVR) *Policy {
|
|
p := Policy{
|
|
ResourceViewer: NewBrowser(gvr),
|
|
}
|
|
p.GetTable().SetColorerFn(render.Policy{}.ColorerFunc())
|
|
p.SetBindKeysFn(p.bindKeys)
|
|
p.GetTable().SetSortCol(1, len(render.Policy{}.Header(render.AllNamespaces)), false)
|
|
|
|
return &p
|
|
}
|
|
|
|
func (p *Policy) Name() string {
|
|
return "policy"
|
|
}
|
|
|
|
// func (p *Policy) Start() {
|
|
// p.Stop()
|
|
// ctx, cancel := context.WithCancel(context.Background())
|
|
// p.cancel = cancel
|
|
// go func(ctx context.Context) {
|
|
// for {
|
|
// select {
|
|
// case <-ctx.Done():
|
|
// return
|
|
// case <-time.After(time.Duration(p.app.Config.K9s.GetRefreshRate()) * time.Second):
|
|
// p.refresh()
|
|
// }
|
|
// }
|
|
// }(ctx)
|
|
// }
|
|
|
|
func (p *Policy) bindKeys(aa ui.KeyActions) {
|
|
aa.Delete(ui.KeyShiftA, tcell.KeyCtrlSpace, ui.KeySpace)
|
|
aa.Add(ui.KeyActions{
|
|
// tcell.KeyEscape: ui.NewKeyAction("Back", p.resetCmd, false),
|
|
// ui.KeySlash: ui.NewKeyAction("Filter", p.activateCmd, false),
|
|
ui.KeyShiftP: ui.NewKeyAction("Sort Namespace", p.GetTable().SortColCmd(0, true), false),
|
|
ui.KeyShiftN: ui.NewKeyAction("Sort Name", p.GetTable().SortColCmd(1, true), false),
|
|
ui.KeyShiftO: ui.NewKeyAction("Sort Group", p.GetTable().SortColCmd(2, true), false),
|
|
ui.KeyShiftB: ui.NewKeyAction("Sort Binding", p.GetTable().SortColCmd(3, true), false),
|
|
})
|
|
}
|
|
|
|
func (p *Policy) getTitle() string {
|
|
// BOZO!!
|
|
// return fmt.Sprintf(rbacTitleFmt, policyTitle, p.subjectKind+":"+p.subjectName, p.GetRowCount())
|
|
return ""
|
|
}
|
|
|
|
// func (p *Policy) refresh() {
|
|
// log.Debug().Msgf(">>>>>>>>>>>>>>> Refreshing Policies")
|
|
// // BOZO!!
|
|
// defer func(t time.Time) {
|
|
// log.Debug().Msgf("Policy Refresh elapsed %v", time.Since(t))
|
|
// }(time.Now())
|
|
|
|
// data, err := p.reconcile()
|
|
// if err != nil {
|
|
// log.Error().Err(err).Msgf("Refresh for %s:%s", p.subjectKind, p.subjectName)
|
|
// p.app.Flash().Err(err)
|
|
// }
|
|
// p.app.QueueUpdateDraw(func() {
|
|
// p.Update(data)
|
|
// })
|
|
// }
|
|
|
|
// func (p *Policy) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|
// if !p.GetTable().SearchBuff().Empty() {
|
|
// p.GetTable().SearchBuff().Reset()
|
|
// return nil
|
|
// }
|
|
|
|
// return p.backCmd(evt)
|
|
// }
|
|
|
|
// func (p *Policy) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|
// if p.cancel != nil {
|
|
// p.cancel()
|
|
// }
|
|
|
|
// if p.SearchBuff().IsActive() {
|
|
// p.SearchBuff().Reset()
|
|
// return nil
|
|
// }
|
|
|
|
// return p.app.PrevCmd(evt)
|
|
// }
|
|
|
|
// func (p *Policy) reconcile() (render.TableData, error) {
|
|
// // BOZO!!
|
|
// defer func(t time.Time) {
|
|
// log.Debug().Msgf("Policy Reconcile elapsed %v", time.Since(t))
|
|
// }(time.Now())
|
|
|
|
// var table render.TableData
|
|
|
|
// evts, errs := p.fetchClusterRoleBindings()
|
|
// if len(errs) > 0 {
|
|
// for _, err := range errs {
|
|
// log.Error().Err(err).Msg("Unable to find cluster policies")
|
|
// }
|
|
// return table, errs[0]
|
|
// }
|
|
|
|
// nevts, errs := p.namespacedPolicies()
|
|
// if len(errs) > 0 {
|
|
// for _, err := range errs {
|
|
// log.Error().Err(err).Msg("Unable to find cluster policies")
|
|
// }
|
|
// return table, errs[0]
|
|
// }
|
|
|
|
// for _, v := range nevts {
|
|
// evts = append(evts, v)
|
|
// }
|
|
|
|
// return buildTable(p, evts), nil
|
|
// }
|
|
|
|
// Protocol...
|
|
|
|
// func (p *Policy) Header() render.HeaderRow {
|
|
// return render.Policy{}.Header(render.AllNamespaces)
|
|
// }
|
|
|
|
// func (p *Policy) GetCache() render.RowEvents {
|
|
// return p.cache
|
|
// }
|
|
|
|
// func (p *Policy) SetCache(evts render.RowEvents) {
|
|
// p.cache = evts
|
|
// }
|
|
|
|
// func (p *Policy) fetchClusterRoleBindings() (render.Rows, []error) {
|
|
// var errs []error
|
|
// oo, err := p.app.factory.List(render.ClusterWide, "rbac.authorization.k8s.io/v1/clusterrolebindings", labels.Everything())
|
|
// if err != nil {
|
|
// return nil, append(errs, err)
|
|
// }
|
|
|
|
// roles := make([]string, 0, len(oo))
|
|
// for _, o := range oo {
|
|
// var crb rbacv1.ClusterRoleBinding
|
|
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &crb)
|
|
// if err != nil {
|
|
// errs = append(errs, err)
|
|
// continue
|
|
// }
|
|
// for _, s := range crb.Subjects {
|
|
// if s.Kind == p.subjectKind && s.Name == p.subjectName {
|
|
// roles = append(roles, crb.RoleRef.Name)
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// rows := make(render.Rows, 0, len(oo))
|
|
// for _, role := range roles {
|
|
// o, err := p.app.factory.Get(render.ClusterWide, "rbac.authorization.k8s.io/v1/clusterroles", role, labels.Everything())
|
|
// if err != nil {
|
|
// return nil, append(errs, err)
|
|
// }
|
|
// var cr rbacv1.ClusterRole
|
|
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &cr)
|
|
// if err != nil {
|
|
// errs = append(errs, err)
|
|
// continue
|
|
// }
|
|
|
|
// for _, v := range p.parseRules("*", "CR:"+role, cr.Rules) {
|
|
// rows = append(rows, v)
|
|
// }
|
|
// }
|
|
|
|
// return rows, errs
|
|
// }
|
|
|
|
// func (p *Policy) fetchRoleBindings() ([]namespacedRole, error) {
|
|
// oo, err := p.app.factory.List(render.AllNamespaces, "rbac.authorization.k8s.io/v1/rolebindings", labels.Everything())
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
|
|
// rr := make([]namespacedRole, 0, len(oo))
|
|
// for _, o := range oo {
|
|
// var rb rbacv1.RoleBinding
|
|
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &rb)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// for _, s := range rb.Subjects {
|
|
// if s.Kind == p.subjectKind && s.Name == p.subjectName {
|
|
// rr = append(rr, namespacedRole{rb.Namespace, rb.RoleRef.Name})
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// return rr, nil
|
|
// }
|
|
|
|
// func (p *Policy) fetchClusterRoles(errs []error, rr []namespacedRole) (render.Rows, []error) {
|
|
// rows := make(render.Rows, 0, len(rr))
|
|
// for _, r := range rr {
|
|
// o, err := p.app.factory.Get(r.ns, "rbac.authorization.k8s.io/v1/clusterroles", r.role, labels.Everything())
|
|
// if err != nil {
|
|
// return nil, append(errs, err)
|
|
// }
|
|
|
|
// var cr rbacv1.ClusterRole
|
|
// err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &cr)
|
|
// if err != nil {
|
|
// errs = append(errs, err)
|
|
// continue
|
|
// }
|
|
// rows = append(rows, p.parseRules(r.ns, "RO:"+r.role, cr.Rules)...)
|
|
// }
|
|
|
|
// return rows, errs
|
|
// }
|
|
|
|
// func (p *Policy) namespacedPolicies() (render.Rows, []error) {
|
|
// var errs []error
|
|
// roles, err := p.fetchRoleBindings()
|
|
// if err != nil {
|
|
// errs = append(errs, err)
|
|
// }
|
|
|
|
// return p.fetchClusterRoles(errs, roles)
|
|
// }
|
|
|
|
// func (p *Policy) parseRules(ns, binding string, rules []rbacv1.PolicyRule) render.Rows {
|
|
// m := make(render.Rows, 0, len(rules))
|
|
// for _, r := range rules {
|
|
// for _, grp := range r.APIGroups {
|
|
// for _, res := range r.Resources {
|
|
// k := res
|
|
// if grp != "" {
|
|
// k = res + "." + grp
|
|
// }
|
|
// for _, na := range r.ResourceNames {
|
|
// n := fqn(k, na)
|
|
// m = append(m, render.Row{
|
|
// ID: fqn(ns, n),
|
|
// Fields: append(policyRow(ns, n, grp, binding), asVerbs(r.Verbs)...),
|
|
// })
|
|
// }
|
|
// m = append(m, render.Row{
|
|
// ID: fqn(ns, k),
|
|
// Fields: append(policyRow(ns, k, grp, binding), asVerbs(r.Verbs)...),
|
|
// })
|
|
// }
|
|
// }
|
|
// for _, nres := range r.NonResourceURLs {
|
|
// if nres[0] != '/' {
|
|
// nres = "/" + nres
|
|
// }
|
|
// m = append(m, render.Row{
|
|
// ID: fqn(ns, nres),
|
|
// Fields: append(policyRow(ns, nres, "", binding), asVerbs(r.Verbs)...),
|
|
// })
|
|
// }
|
|
// }
|
|
|
|
// return m
|
|
// }
|
|
|
|
func policyRow(ns, res, grp, binding string) render.Fields {
|
|
if grp != "" {
|
|
grp = toGroup(grp)
|
|
}
|
|
|
|
r := make(render.Fields, 0, len(render.Policy{}.Header(render.AllNamespaces)))
|
|
return append(r, ns, res, grp, binding)
|
|
}
|
|
|
|
func mapSubject(subject string) string {
|
|
switch subject {
|
|
case "g":
|
|
return group
|
|
case "s":
|
|
return sa
|
|
default:
|
|
return user
|
|
}
|
|
}
|
|
|
|
// func showSAPolicy(app *App, _, _, selection string) {
|
|
// _, n := k8s.Namespaced(selection)
|
|
// subject, err := mapFuSubject("ServiceAccount")
|
|
// if err != nil {
|
|
// app.Flash().Err(err)
|
|
// return
|
|
// }
|
|
// app.inject(NewPolicy(app, subject, n))
|
|
// }
|
|
|
|
func toGroup(g string) string {
|
|
if g == "" {
|
|
return "v1"
|
|
}
|
|
return g
|
|
}
|
|
|
|
func hasVerb(verbs []string, verb string) bool {
|
|
if len(verbs) == 1 && verbs[0] == allVerbs {
|
|
return true
|
|
}
|
|
|
|
for _, v := range verbs {
|
|
if hv, ok := httpTok8sVerbs[v]; ok {
|
|
if hv == verb {
|
|
return true
|
|
}
|
|
}
|
|
if v == verb {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func toVerbIcon(ok bool) string {
|
|
if ok {
|
|
return "[green::b] ✓ [::]"
|
|
}
|
|
return "[orangered::b] 𐄂 [::]"
|
|
}
|
|
|
|
func asVerbs(verbs []string) []string {
|
|
const (
|
|
verbLen = 4
|
|
unknownLen = 30
|
|
)
|
|
|
|
r := make([]string, 0, len(k8sVerbs)+1)
|
|
for _, v := range k8sVerbs {
|
|
r = append(r, toVerbIcon(hasVerb(verbs, v)))
|
|
}
|
|
|
|
var unknowns []string
|
|
for _, v := range verbs {
|
|
if hv, ok := httpTok8sVerbs[v]; ok {
|
|
v = hv
|
|
}
|
|
if !hasVerb(k8sVerbs, v) && v != allVerbs {
|
|
unknowns = append(unknowns, v)
|
|
}
|
|
}
|
|
|
|
return append(r, render.Truncate(strings.Join(unknowns, ","), unknownLen))
|
|
}
|