-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #52 from appuio/feat/member-rbac
Add option to bind ClusterRoles to all organization members
- Loading branch information
Showing
5 changed files
with
356 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package controllers | ||
|
||
import ( | ||
"context" | ||
|
||
"go.uber.org/multierr" | ||
rbacv1 "k8s.io/api/rbac/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/client-go/tools/record" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/log" | ||
|
||
controlv1 "github.com/appuio/control-api/apis/v1" | ||
) | ||
|
||
// OrganizationMembersReconciler reconciles OrganizationMembers resources | ||
type OrganizationMembersReconciler struct { | ||
client.Client | ||
Recorder record.EventRecorder | ||
Scheme *runtime.Scheme | ||
|
||
// UserPrefix is the prefix applied to the user in the RoleBinding.subjects.name. | ||
UserPrefix string | ||
MemberRoles []string | ||
} | ||
|
||
//+kubebuilder:rbac:groups=appuio.io,resources=organizationmembers,verbs=get;list;watch;update;patch | ||
//+kubebuilder:rbac:groups=appuio.io,resources=organizationmembers/status,verbs=get;update;patch | ||
//+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=get;list;watch;create;update;patch;delete | ||
//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch | ||
|
||
// Needed so that we are allowed to delegate common member roles | ||
//+kubebuilder:rbac:groups="rbac.appuio.io",resources=organizations,verbs=get;list;watch;create;delete;patch;update | ||
//+kubebuilder:rbac:groups="organization.appuio.io",resources=organizations,verbs=get;list;watch;create;delete;patch;update | ||
//+kubebuilder:rbac:groups="appuio.io",resources=teams,verbs=get;list;watch;create;delete;patch;update | ||
//+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=get;list;watch;create;update;patch;delete | ||
|
||
// Reconcile reacts on changes of users and mirrors these changes to Keycloak | ||
func (r *OrganizationMembersReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { | ||
log := log.FromContext(ctx) | ||
log.V(4).WithValues("request", req).Info("Reconciling") | ||
|
||
memb := controlv1.OrganizationMembers{} | ||
if err := r.Get(ctx, req.NamespacedName, &memb); err != nil { | ||
return ctrl.Result{}, client.IgnoreNotFound(err) | ||
} | ||
|
||
if !memb.ObjectMeta.DeletionTimestamp.IsZero() { | ||
return ctrl.Result{}, nil | ||
} | ||
|
||
var errGroup error | ||
for _, role := range r.MemberRoles { | ||
err := r.putRoleBinding(ctx, memb, role) | ||
if err != nil { | ||
errGroup = multierr.Append(errGroup, err) | ||
r.Recorder.Event(&memb, "Warning", "RBACUpdateFailed", "Failed to set RBAC for Organization members") | ||
} | ||
} | ||
|
||
return ctrl.Result{}, errGroup | ||
} | ||
|
||
func (r *OrganizationMembersReconciler) putRoleBinding(ctx context.Context, memb controlv1.OrganizationMembers, role string) error { | ||
rb := rbacv1.RoleBinding{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: role, | ||
Namespace: memb.Namespace, | ||
}, | ||
} | ||
op, err := ctrl.CreateOrUpdate(ctx, r.Client, &rb, func() error { | ||
sub := make([]rbacv1.Subject, len(memb.Spec.UserRefs)) | ||
for i, ur := range memb.Spec.UserRefs { | ||
sub[i] = rbacv1.Subject{ | ||
APIGroup: rbacv1.GroupName, | ||
Kind: "User", | ||
Name: r.UserPrefix + ur.Name, | ||
} | ||
} | ||
rb.Subjects = sub | ||
rb.RoleRef = rbacv1.RoleRef{ | ||
APIGroup: rbacv1.GroupName, | ||
Kind: "ClusterRole", | ||
Name: role, | ||
} | ||
return ctrl.SetControllerReference(&memb, &rb, r.Scheme) | ||
}) | ||
log.FromContext(ctx).V(4).Info("reconcile RoleBinding", "operation", op) | ||
return err | ||
} | ||
|
||
// SetupWithManager sets up the controller with the Manager. | ||
func (r *OrganizationMembersReconciler) SetupWithManager(mgr ctrl.Manager) error { | ||
return ctrl.NewControllerManagedBy(mgr). | ||
For(&controlv1.OrganizationMembers{}). | ||
Owns(&rbacv1.RoleBinding{}). | ||
Complete(r) | ||
} |
Oops, something went wrong.