Add grants to project
[ZITADOPER-1]
This commit is contained in:
@@ -34,6 +34,55 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
description: ProjectSpec defines the desired state of Project
|
description: ProjectSpec defines the desired state of Project
|
||||||
properties:
|
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:
|
hasProjectCheck:
|
||||||
type: boolean
|
type: boolean
|
||||||
organizationRef:
|
organizationRef:
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ type Role struct {
|
|||||||
DisplayName string `json:"displayName"`
|
DisplayName string `json:"displayName"`
|
||||||
Group string `json:"group"`
|
Group string `json:"group"`
|
||||||
}
|
}
|
||||||
|
type Grant struct {
|
||||||
|
OrganizationRef OrganizationRef `json:"organizationRef"`
|
||||||
|
RoleKeys []string `json:"roleKeys"`
|
||||||
|
}
|
||||||
|
|
||||||
// ProjectSpec defines the desired state of Project
|
// ProjectSpec defines the desired state of Project
|
||||||
type ProjectSpec struct {
|
type ProjectSpec struct {
|
||||||
@@ -44,6 +48,8 @@ type ProjectSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
Roles []Role `json:"roles"`
|
Roles []Role `json:"roles"`
|
||||||
// +optional
|
// +optional
|
||||||
|
Grants []Grant `json:"grants"`
|
||||||
|
// +optional
|
||||||
ProjectRoleAssertion bool `json:"projectRoleAssertion,omitempty"`
|
ProjectRoleAssertion bool `json:"projectRoleAssertion,omitempty"`
|
||||||
// +optional
|
// +optional
|
||||||
ProjectRoleCheck bool `json:"projectRoleCheck,omitempty"`
|
ProjectRoleCheck bool `json:"projectRoleCheck,omitempty"`
|
||||||
|
|||||||
@@ -154,6 +154,27 @@ func (in *DomainSettings) DeepCopy() *DomainSettings {
|
|||||||
return out
|
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.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Image) DeepCopyInto(out *Image) {
|
func (in *Image) DeepCopyInto(out *Image) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -606,6 +627,13 @@ func (in *ProjectSpec) DeepCopyInto(out *ProjectSpec) {
|
|||||||
*out = make([]Role, len(*in))
|
*out = make([]Role, len(*in))
|
||||||
copy(*out, *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.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectSpec.
|
||||||
|
|||||||
@@ -35,6 +35,55 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
description: ProjectSpec defines the desired state of Project
|
description: ProjectSpec defines the desired state of Project
|
||||||
properties:
|
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:
|
hasProjectCheck:
|
||||||
type: boolean
|
type: boolean
|
||||||
organizationRef:
|
organizationRef:
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -28,6 +30,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"
|
||||||
|
"github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/project"
|
||||||
"golang.org/x/exp/maps"
|
"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"
|
||||||
@@ -107,6 +110,10 @@ func (wr *wrappedProjectReconciler) Reconcile(ctx context.Context, ztdClient *ma
|
|||||||
Name: "roles",
|
Name: "roles",
|
||||||
Reconcile: wr.reconcileRoles,
|
Reconcile: wr.reconcileRoles,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "grants",
|
||||||
|
Reconcile: wr.reconcileGrants,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, p := range phases {
|
for _, p := range phases {
|
||||||
err := p.Reconcile(ctx, ztdClient)
|
err := p.Reconcile(ctx, ztdClient)
|
||||||
@@ -219,6 +226,57 @@ func (wr *wrappedProjectReconciler) reconcileRoles(ctx context.Context, ztdClien
|
|||||||
return nil
|
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 {
|
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