Add grants to project

[ZITADOPER-1]
This commit is contained in:
Haim Kortovich
2024-05-17 13:51:08 -05:00
parent 822a2a22ef
commit 626e33a773
5 changed files with 190 additions and 0 deletions

View File

@@ -34,6 +34,55 @@ spec:
spec:
description: ProjectSpec defines the desired state of Project
properties:
grants:
items:
properties:
organizationRef:
properties:
apiVersion:
description: API version of the referent.
type: string
fieldPath:
description: 'If referring to a piece of an object instead
of an entire object, this string should contain a valid
JSON/Go field access statement, such as desiredState.manifest.containers[2].
For example, if the object reference is to a container
within a pod, this would take on a value like: "spec.containers{name}"
(where "name" refers to the name of the container that
triggered the event) or if no container name is specified
"spec.containers[2]" (container with index 2 in this pod).
This syntax is chosen only to have some well-defined way
of referencing a part of an object. TODO: this design
is not final and this field is subject to change in the
future.'
type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
resourceVersion:
description: 'Specific resourceVersion to which this reference
is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
type: object
x-kubernetes-map-type: atomic
roleKeys:
items:
type: string
type: array
required:
- organizationRef
- roleKeys
type: object
type: array
hasProjectCheck:
type: boolean
organizationRef:

View File

@@ -32,6 +32,10 @@ type Role struct {
DisplayName string `json:"displayName"`
Group string `json:"group"`
}
type Grant struct {
OrganizationRef OrganizationRef `json:"organizationRef"`
RoleKeys []string `json:"roleKeys"`
}
// ProjectSpec defines the desired state of Project
type ProjectSpec struct {
@@ -44,6 +48,8 @@ type ProjectSpec struct {
// +optional
Roles []Role `json:"roles"`
// +optional
Grants []Grant `json:"grants"`
// +optional
ProjectRoleAssertion bool `json:"projectRoleAssertion,omitempty"`
// +optional
ProjectRoleCheck bool `json:"projectRoleCheck,omitempty"`

View File

@@ -154,6 +154,27 @@ func (in *DomainSettings) DeepCopy() *DomainSettings {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Grant) DeepCopyInto(out *Grant) {
*out = *in
out.OrganizationRef = in.OrganizationRef
if in.RoleKeys != nil {
in, out := &in.RoleKeys, &out.RoleKeys
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Grant.
func (in *Grant) DeepCopy() *Grant {
if in == nil {
return nil
}
out := new(Grant)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Image) DeepCopyInto(out *Image) {
*out = *in
@@ -606,6 +627,13 @@ func (in *ProjectSpec) DeepCopyInto(out *ProjectSpec) {
*out = make([]Role, len(*in))
copy(*out, *in)
}
if in.Grants != nil {
in, out := &in.Grants, &out.Grants
*out = make([]Grant, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectSpec.

View File

@@ -35,6 +35,55 @@ spec:
spec:
description: ProjectSpec defines the desired state of Project
properties:
grants:
items:
properties:
organizationRef:
properties:
apiVersion:
description: API version of the referent.
type: string
fieldPath:
description: 'If referring to a piece of an object instead
of an entire object, this string should contain a valid
JSON/Go field access statement, such as desiredState.manifest.containers[2].
For example, if the object reference is to a container
within a pod, this would take on a value like: "spec.containers{name}"
(where "name" refers to the name of the container that
triggered the event) or if no container name is specified
"spec.containers[2]" (container with index 2 in this pod).
This syntax is chosen only to have some well-defined way
of referencing a part of an object. TODO: this design
is not final and this field is subject to change in the
future.'
type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
resourceVersion:
description: 'Specific resourceVersion to which this reference
is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
type: object
x-kubernetes-map-type: atomic
roleKeys:
items:
type: string
type: array
required:
- organizationRef
- roleKeys
type: object
type: array
hasProjectCheck:
type: boolean
organizationRef:

View File

@@ -19,6 +19,8 @@ package controller
import (
"context"
"fmt"
"reflect"
"sort"
"strings"
"time"
@@ -28,6 +30,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"
"github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/project"
"golang.org/x/exp/maps"
"k8s.io/client-go/util/workqueue"
ctrl "sigs.k8s.io/controller-runtime"
@@ -107,6 +110,10 @@ func (wr *wrappedProjectReconciler) Reconcile(ctx context.Context, ztdClient *ma
Name: "roles",
Reconcile: wr.reconcileRoles,
},
{
Name: "grants",
Reconcile: wr.reconcileGrants,
},
}
for _, p := range phases {
err := p.Reconcile(ctx, ztdClient)
@@ -219,6 +226,57 @@ func (wr *wrappedProjectReconciler) reconcileRoles(ctx context.Context, ztdClien
return nil
}
func (wr *wrappedProjectReconciler) reconcileGrants(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
}
existingGrants, err := ztdClient.ListProjectGrants(ctx, &pb.ListProjectGrantsRequest{
ProjectId: wr.project.Status.ProjectId,
})
if err != nil {
return fmt.Errorf("Error listing project grants: %v", err)
}
ctx = middleware.SetOrgID(ctx, org.Status.OrgId)
for _, grant := range wr.project.DeepCopy().Spec.Grants {
grantedOrg, err := wr.refResolver.OrganizationRef(ctx, &grant.OrganizationRef, wr.project.Namespace)
if err != nil {
return err
}
var existingGrant *project.GrantedProject
for _, eGrant := range existingGrants.Result {
if eGrant.GrantedOrgId == grantedOrg.Status.OrgId {
existingGrant = eGrant
break
}
}
if existingGrant == nil {
_, err := ztdClient.AddProjectGrant(ctx, &pb.AddProjectGrantRequest{
ProjectId: wr.project.Status.ProjectId,
GrantedOrgId: grantedOrg.Status.OrgId,
RoleKeys: grant.RoleKeys,
})
if err != nil {
return fmt.Errorf("Error Adding project grant: %v", err)
}
} else {
sort.Strings(existingGrant.GrantedRoleKeys)
sort.Strings(grant.RoleKeys)
if !reflect.DeepEqual(existingGrant.GrantedRoleKeys, grant.RoleKeys) {
_, err := ztdClient.UpdateProjectGrant(ctx, &pb.UpdateProjectGrantRequest{
ProjectId: wr.project.Status.ProjectId,
GrantId: existingGrant.GrantId,
RoleKeys: grant.RoleKeys,
})
if err != nil {
return fmt.Errorf("Error Updating project grant: %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)