Files
capsule/pkg/api/users_list.go
Oliver Bähler 730151cb44 feat: add dynamic capsule user evaluation (#1811)
* chore: improve dev targets

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(controller): implement deterministic rolebinding reflection

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(controller): capsule users are determined from configuration status

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(tenantowners): added agreggate option - tenantowners are always considered capsule users

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(tenantowner): add implicit aggregation for tenants

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: remove helm flags

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix(config): remove usergroups default

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-12-31 11:37:30 +01:00

139 lines
2.5 KiB
Go

// Copyright 2020-2025 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package api
import (
"sort"
)
// +kubebuilder:object:generate=true
type UserListSpec []UserSpec
func (o *UserListSpec) Upsert(newUser UserSpec) {
users := *o
// Comparator consistent with ByKindName
less := func(a, b UserSpec) bool {
ak, bk := a.Kind.String(), b.Kind.String()
if ak != bk {
return ak < bk
}
return a.Name < b.Name
}
// Ensure sorted before binary search
sort.Sort(ByKindName(users))
// Find first index where users[i] >= newUser
idx := sort.Search(len(users), func(i int) bool {
return !less(users[i], newUser)
})
// In this case merging for duplicates makes little sense as the values are identical
if idx < len(users) && !less(newUser, users[idx]) && !less(users[idx], newUser) {
return
}
users = append(users, newUser)
sort.Sort(ByKindName(users))
*o = users
}
func (u UserListSpec) IsPresent(name string, groups []string) bool {
groupSet := make(map[string]struct{}, len(groups))
for _, g := range groups {
groupSet[g] = struct{}{}
}
for _, user := range u {
switch user.Kind {
case UserOwner, ServiceAccountOwner:
if user.Name == name {
return true
}
case GroupOwner:
if _, ok := groupSet[user.Name]; ok {
return true
}
}
}
return false
}
//nolint:dupl
func (o UserListSpec) FindUser(name string, kind OwnerKind) (UserSpec, bool) {
sort.Sort(ByKindName(o))
targetKind := kind.String()
n := len(o)
idx := sort.Search(n, func(i int) bool {
ki := o[i].Kind.String()
switch {
case ki > targetKind:
return true
case ki < targetKind:
return false
default:
return o[i].Name >= name
}
})
if idx < n &&
o[idx].Kind.String() == targetKind &&
o[idx].Name == name {
return o[idx], true
}
return UserSpec{}, false
}
func (o UserListSpec) GetByKinds(kinds []OwnerKind) []string {
if len(o) == 0 || len(kinds) == 0 {
return nil
}
kindSet := make(map[OwnerKind]struct{}, len(kinds))
for _, k := range kinds {
kindSet[k] = struct{}{}
}
names := make([]string, 0, len(o))
for _, u := range o {
if _, ok := kindSet[u.Kind]; ok {
names = append(names, u.Name)
}
}
if len(names) == 0 {
return nil
}
sort.Strings(names)
return names
}
type ByKindName UserListSpec
func (b ByKindName) Len() int {
return len(b)
}
func (b ByKindName) Less(i, j int) bool {
if b[i].Kind.String() != b[j].Kind.String() {
return b[i].Kind.String() < b[j].Kind.String()
}
return b[i].Name < b[j].Name
}
func (b ByKindName) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}