Add roles to project
[ZITADOPER-1]
This commit is contained in:
@@ -79,6 +79,21 @@ spec:
|
|||||||
type: boolean
|
type: boolean
|
||||||
projectRoleCheck:
|
projectRoleCheck:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
roles:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
displayName:
|
||||||
|
type: string
|
||||||
|
group:
|
||||||
|
type: string
|
||||||
|
key:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- displayName
|
||||||
|
- group
|
||||||
|
- key
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
required:
|
required:
|
||||||
- organizationRef
|
- organizationRef
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ import (
|
|||||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
// 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.
|
// 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
|
// ProjectSpec defines the desired state of Project
|
||||||
type ProjectSpec struct {
|
type ProjectSpec struct {
|
||||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
@@ -35,6 +41,8 @@ type ProjectSpec struct {
|
|||||||
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||||
OrganizationRef OrganizationRef `json:"organizationRef"`
|
OrganizationRef OrganizationRef `json:"organizationRef"`
|
||||||
// +optional
|
// +optional
|
||||||
|
Roles []Role `json:"roles"`
|
||||||
|
// +optional
|
||||||
ProjectRoleAssertion bool `json:"projectRoleAssertion,omitempty"`
|
ProjectRoleAssertion bool `json:"projectRoleAssertion,omitempty"`
|
||||||
// +optional
|
// +optional
|
||||||
ProjectRoleCheck bool `json:"projectRoleCheck,omitempty"`
|
ProjectRoleCheck bool `json:"projectRoleCheck,omitempty"`
|
||||||
|
|||||||
@@ -399,7 +399,7 @@ func (in *Project) DeepCopyInto(out *Project) {
|
|||||||
*out = *in
|
*out = *in
|
||||||
out.TypeMeta = in.TypeMeta
|
out.TypeMeta = in.TypeMeta
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
out.Spec = in.Spec
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
in.Status.DeepCopyInto(&out.Status)
|
in.Status.DeepCopyInto(&out.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,6 +473,11 @@ func (in *ProjectRef) DeepCopy() *ProjectRef {
|
|||||||
func (in *ProjectSpec) DeepCopyInto(out *ProjectSpec) {
|
func (in *ProjectSpec) DeepCopyInto(out *ProjectSpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
out.OrganizationRef = in.OrganizationRef
|
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.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectSpec.
|
||||||
@@ -507,6 +512,21 @@ func (in *ProjectStatus) DeepCopy() *ProjectStatus {
|
|||||||
return out
|
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.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ZitadelCluster) DeepCopyInto(out *ZitadelCluster) {
|
func (in *ZitadelCluster) DeepCopyInto(out *ZitadelCluster) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|||||||
@@ -80,6 +80,21 @@ spec:
|
|||||||
type: boolean
|
type: boolean
|
||||||
projectRoleCheck:
|
projectRoleCheck:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
roles:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
displayName:
|
||||||
|
type: string
|
||||||
|
group:
|
||||||
|
type: string
|
||||||
|
key:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- displayName
|
||||||
|
- group
|
||||||
|
- key
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
required:
|
required:
|
||||||
- organizationRef
|
- organizationRef
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/zitadel/zitadel-go/v2/pkg/client/management"
|
"github.com/zitadel/zitadel-go/v2/pkg/client/management"
|
||||||
"github.com/zitadel/zitadel-go/v2/pkg/client/middleware"
|
"github.com/zitadel/zitadel-go/v2/pkg/client/middleware"
|
||||||
pb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
|
pb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
"k8s.io/client-go/util/workqueue"
|
"k8s.io/client-go/util/workqueue"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"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 {
|
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)
|
org, err := wr.refResolver.OrganizationRef(ctx, &wr.project.Spec.OrganizationRef, wr.project.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -138,6 +164,59 @@ func (wr *wrappedProjectReconciler) Reconcile(ctx context.Context, ztdClient *ma
|
|||||||
return wr.Client.Status().Patch(ctx, wr.project, patch)
|
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 {
|
func (wr *wrappedProjectReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
||||||
patch := client.MergeFrom(wr.project.DeepCopy())
|
patch := client.MergeFrom(wr.project.DeepCopy())
|
||||||
patcher(&wr.project.Status)
|
patcher(&wr.project.Status)
|
||||||
|
|||||||
Reference in New Issue
Block a user