Add roles to project

[ZITADOPER-1]
This commit is contained in:
Haim Kortovich
2024-05-13 20:08:27 -05:00
parent 43ed843ca6
commit 258d2d1e02
5 changed files with 138 additions and 1 deletions

View File

@@ -79,6 +79,21 @@ spec:
type: boolean
projectRoleCheck:
type: boolean
roles:
items:
properties:
displayName:
type: string
group:
type: string
key:
type: string
required:
- displayName
- group
- key
type: object
type: array
required:
- organizationRef
type: object

View File

@@ -26,6 +26,12 @@ import (
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
type Role struct {
Key string `json:"key"`
DisplayName string `json:"displayName"`
Group string `json:"group"`
}
// ProjectSpec defines the desired state of Project
type ProjectSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
@@ -35,6 +41,8 @@ type ProjectSpec struct {
// +operator-sdk:csv:customresourcedefinitions:type=spec
OrganizationRef OrganizationRef `json:"organizationRef"`
// +optional
Roles []Role `json:"roles"`
// +optional
ProjectRoleAssertion bool `json:"projectRoleAssertion,omitempty"`
// +optional
ProjectRoleCheck bool `json:"projectRoleCheck,omitempty"`

View File

@@ -399,7 +399,7 @@ func (in *Project) DeepCopyInto(out *Project) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
@@ -473,6 +473,11 @@ func (in *ProjectRef) DeepCopy() *ProjectRef {
func (in *ProjectSpec) DeepCopyInto(out *ProjectSpec) {
*out = *in
out.OrganizationRef = in.OrganizationRef
if in.Roles != nil {
in, out := &in.Roles, &out.Roles
*out = make([]Role, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectSpec.
@@ -507,6 +512,21 @@ func (in *ProjectStatus) DeepCopy() *ProjectStatus {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Role) DeepCopyInto(out *Role) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Role.
func (in *Role) DeepCopy() *Role {
if in == nil {
return nil
}
out := new(Role)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ZitadelCluster) DeepCopyInto(out *ZitadelCluster) {
*out = *in

View File

@@ -80,6 +80,21 @@ spec:
type: boolean
projectRoleCheck:
type: boolean
roles:
items:
properties:
displayName:
type: string
group:
type: string
key:
type: string
required:
- displayName
- group
- key
type: object
type: array
required:
- organizationRef
type: object

View File

@@ -28,6 +28,7 @@ import (
"github.com/zitadel/zitadel-go/v2/pkg/client/management"
"github.com/zitadel/zitadel-go/v2/pkg/client/middleware"
pb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
"golang.org/x/exp/maps"
"k8s.io/client-go/util/workqueue"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -91,7 +92,32 @@ func newWrappedProjectReconciler(client client.Client, refResolver *zitadelv1alp
}
}
type projectReconcilePhase struct {
Name string
Reconcile func(context.Context, *management.Client) error
}
func (wr *wrappedProjectReconciler) Reconcile(ctx context.Context, ztdClient *management.Client) error {
phases := []projectReconcilePhase{
{
Name: "project",
Reconcile: wr.reconcileProject,
},
{
Name: "roles",
Reconcile: wr.reconcileRoles,
},
}
for _, p := range phases {
err := p.Reconcile(ctx, ztdClient)
if err != nil {
return err
}
}
return nil
}
func (wr *wrappedProjectReconciler) reconcileProject(ctx context.Context, ztdClient *management.Client) error {
org, err := wr.refResolver.OrganizationRef(ctx, &wr.project.Spec.OrganizationRef, wr.project.Namespace)
if err != nil {
return err
@@ -138,6 +164,59 @@ func (wr *wrappedProjectReconciler) Reconcile(ctx context.Context, ztdClient *ma
return wr.Client.Status().Patch(ctx, wr.project, patch)
}
func (wr *wrappedProjectReconciler) reconcileRoles(ctx context.Context, ztdClient *management.Client) error {
org, err := wr.refResolver.OrganizationRef(ctx, &wr.project.Spec.OrganizationRef, wr.project.Namespace)
if err != nil {
return err
}
resp, err := ztdClient.ListProjectRoles(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.ListProjectRolesRequest{
ProjectId: wr.project.Status.ProjectId,
})
if err != nil {
return fmt.Errorf("Could not list project roles: %v", err)
}
roles := map[string]*pb.BulkAddProjectRolesRequest_Role{}
deleteRoles := []*pb.BulkAddProjectRolesRequest_Role{}
for _, role := range wr.project.Spec.Roles {
roles[role.Key] = &pb.BulkAddProjectRolesRequest_Role{
Key: role.Key,
DisplayName: role.DisplayName,
Group: role.Group,
}
}
for _, role := range resp.Result {
if r, ok := roles[role.Key]; ok {
if r.DisplayName != role.DisplayName || r.Group != role.Group {
deleteRoles = append(deleteRoles, r)
} else {
delete(roles, role.Key)
}
} else {
delete(roles, role.Key)
}
}
for _, dRole := range deleteRoles {
if _, err = ztdClient.RemoveProjectRole(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.RemoveProjectRoleRequest{
ProjectId: wr.project.Status.ProjectId,
RoleKey: dRole.Key,
}); err != nil {
return fmt.Errorf("Error removing project role: %v", err)
}
}
if len(roles) > 0 {
_, err = ztdClient.BulkAddProjectRoles(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.BulkAddProjectRolesRequest{
ProjectId: wr.project.Status.ProjectId,
Roles: maps.Values(roles)})
if err != nil {
return fmt.Errorf("Could not add roles to project: %v", err)
}
}
return nil
}
func (wr *wrappedProjectReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
patch := client.MergeFrom(wr.project.DeepCopy())
patcher(&wr.project.Status)