mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-25 16:07:24 +00:00
feat: add rolebindings to rules api (#2032)
* feat: add rolebindings to rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add rolebindings to rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add rolebindings to rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add rolebindings to rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add rolebindings to rules api Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: implement review Signed-off-by: Oliver Baehler <oliver@sudo-i.net> --------- Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
This commit is contained in:
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
type AdditionalRoleBindingsSpec struct {
|
||||
ClusterRoleName string `json:"clusterRoleName"`
|
||||
// kubebuilder:validation:Minimum=1
|
||||
// +kubebuilder:validation:MinItems=1
|
||||
Subjects []rbacv1.Subject `json:"subjects"`
|
||||
// Additional Labels for the synchronized rolebindings
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
@@ -5,10 +5,15 @@ package rules
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api/rbac"
|
||||
)
|
||||
|
||||
// +kubebuilder:object:generate=true
|
||||
type NamespaceRulePermissionBody struct {
|
||||
// Bindings defines additional RoleBindings for namespaces selected by this rule.
|
||||
Bindings []rbac.AdditionalRoleBindingsSpec `json:"bindings,omitempty"`
|
||||
|
||||
// Define Promotion Rules which distributed additional ClusterRoles across the Tenant
|
||||
// for promoted ServiceAccounts.
|
||||
Promotions []*NamespaceRulePromotionRule `json:"promotions,omitempty"`
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"github.com/projectcapsule/capsule/pkg/api/rbac"
|
||||
"github.com/projectcapsule/capsule/pkg/api/runtime"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -212,6 +213,13 @@ func (in *NamespaceRuleEnforceWorkloadsBody) DeepCopy() *NamespaceRuleEnforceWor
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NamespaceRulePermissionBody) DeepCopyInto(out *NamespaceRulePermissionBody) {
|
||||
*out = *in
|
||||
if in.Bindings != nil {
|
||||
in, out := &in.Bindings, &out.Bindings
|
||||
*out = make([]rbac.AdditionalRoleBindingsSpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Promotions != nil {
|
||||
in, out := &in.Promotions, &out.Promotions
|
||||
*out = make([]*NamespaceRulePromotionRule, len(*in))
|
||||
|
||||
+13
-2
@@ -4,6 +4,7 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
|
||||
@@ -12,11 +13,21 @@ import (
|
||||
|
||||
func RoleBindingHashFunc(binding rbac.AdditionalRoleBindingsSpec) string {
|
||||
h := fnv.New64a()
|
||||
writeField := func(value string) {
|
||||
var length [8]byte
|
||||
|
||||
_, _ = h.Write([]byte(binding.ClusterRoleName))
|
||||
binary.LittleEndian.PutUint64(length[:], uint64(len(value)))
|
||||
_, _ = h.Write(length[:])
|
||||
_, _ = h.Write([]byte(value))
|
||||
}
|
||||
|
||||
writeField(binding.ClusterRoleName)
|
||||
|
||||
for _, sub := range binding.Subjects {
|
||||
_, _ = h.Write([]byte(sub.Kind + sub.Name))
|
||||
writeField(sub.APIGroup)
|
||||
writeField(sub.Kind)
|
||||
writeField(sub.Namespace)
|
||||
writeField(sub.Name)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", h.Sum64())
|
||||
|
||||
@@ -86,6 +86,76 @@ func TestRoleBindingHashFunc_ChangesWhenSubjectNameChanges(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleBindingHashFunc_ChangesWhenSubjectNamespaceChanges(t *testing.T) {
|
||||
b1 := rbac.AdditionalRoleBindingsSpec{
|
||||
ClusterRoleName: "admin",
|
||||
Subjects: []rbacv1.Subject{{
|
||||
Kind: rbacv1.ServiceAccountKind,
|
||||
Namespace: "team-a",
|
||||
Name: "deployer",
|
||||
}},
|
||||
}
|
||||
b2 := rbac.AdditionalRoleBindingsSpec{
|
||||
ClusterRoleName: "admin",
|
||||
Subjects: []rbacv1.Subject{{
|
||||
Kind: rbacv1.ServiceAccountKind,
|
||||
Namespace: "team-b",
|
||||
Name: "deployer",
|
||||
}},
|
||||
}
|
||||
|
||||
h1 := utils.RoleBindingHashFunc(b1)
|
||||
h2 := utils.RoleBindingHashFunc(b2)
|
||||
|
||||
if h1 == h2 {
|
||||
t.Fatalf("expected different hashes when subject Namespace changes, got %q", h1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleBindingHashFunc_ChangesWhenSubjectAPIGroupChanges(t *testing.T) {
|
||||
b1 := rbac.AdditionalRoleBindingsSpec{
|
||||
ClusterRoleName: "admin",
|
||||
Subjects: []rbacv1.Subject{{
|
||||
APIGroup: rbacv1.GroupName,
|
||||
Kind: rbacv1.UserKind,
|
||||
Name: "alice",
|
||||
}},
|
||||
}
|
||||
b2 := rbac.AdditionalRoleBindingsSpec{
|
||||
ClusterRoleName: "admin",
|
||||
Subjects: []rbacv1.Subject{{
|
||||
APIGroup: "example.com",
|
||||
Kind: rbacv1.UserKind,
|
||||
Name: "alice",
|
||||
}},
|
||||
}
|
||||
|
||||
h1 := utils.RoleBindingHashFunc(b1)
|
||||
h2 := utils.RoleBindingHashFunc(b2)
|
||||
|
||||
if h1 == h2 {
|
||||
t.Fatalf("expected different hashes when subject APIGroup changes, got %q", h1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleBindingHashFunc_UsesUnambiguousFieldBoundaries(t *testing.T) {
|
||||
b1 := rbac.AdditionalRoleBindingsSpec{
|
||||
ClusterRoleName: "ab",
|
||||
Subjects: []rbacv1.Subject{{Kind: "c", Name: "d"}},
|
||||
}
|
||||
b2 := rbac.AdditionalRoleBindingsSpec{
|
||||
ClusterRoleName: "a",
|
||||
Subjects: []rbacv1.Subject{{Kind: "bc", Name: "d"}},
|
||||
}
|
||||
|
||||
h1 := utils.RoleBindingHashFunc(b1)
|
||||
h2 := utils.RoleBindingHashFunc(b2)
|
||||
|
||||
if h1 == h2 {
|
||||
t.Fatalf("expected different hashes for differently bounded fields, got %q", h1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleBindingHashFunc_EmptyInputsStillProduceHash(t *testing.T) {
|
||||
b := rbac.AdditionalRoleBindingsSpec{
|
||||
ClusterRoleName: "",
|
||||
@@ -98,9 +168,7 @@ func TestRoleBindingHashFunc_EmptyInputsStillProduceHash(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleBindingHashFunc_SubjectOrderMatters_CurrentBehavior(t *testing.T) {
|
||||
// This test documents the CURRENT behavior:
|
||||
// the hash is order-dependent because subjects are written in slice order.
|
||||
func TestRoleBindingHashFunc_SubjectOrderMatters(t *testing.T) {
|
||||
b1 := rbac.AdditionalRoleBindingsSpec{
|
||||
ClusterRoleName: "admin",
|
||||
Subjects: []rbacv1.Subject{
|
||||
@@ -120,6 +188,6 @@ func TestRoleBindingHashFunc_SubjectOrderMatters_CurrentBehavior(t *testing.T) {
|
||||
h2 := utils.RoleBindingHashFunc(b2)
|
||||
|
||||
if h1 == h2 {
|
||||
t.Fatalf("expected different hashes when subject order changes (current behavior), got %q", h1)
|
||||
t.Fatalf("expected different hashes when subject order changes, got %q", h1)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user