Add useer grants
[ZITADOPER-1]
This commit is contained in:
@@ -77,6 +77,54 @@ spec:
|
|||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
x-kubernetes-map-type: atomic
|
x-kubernetes-map-type: atomic
|
||||||
|
userGrants:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
projectRef:
|
||||||
|
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:
|
||||||
|
- projectRef
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
required:
|
required:
|
||||||
- accessTokenType
|
- accessTokenType
|
||||||
- organizationRef
|
- organizationRef
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ 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 UserGrant struct {
|
||||||
|
ProjectRef ProjectRef `json:"projectRef"`
|
||||||
|
RoleKeys []string `json:"roleKeys,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// MachineUserSpec defines the desired state of MachineUser
|
// MachineUserSpec defines the desired state of MachineUser
|
||||||
type MachineUserSpec struct {
|
type MachineUserSpec struct {
|
||||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
@@ -35,7 +40,8 @@ type MachineUserSpec struct {
|
|||||||
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||||
OrganizationRef OrganizationRef `json:"organizationRef" webhook:"inmutable"`
|
OrganizationRef OrganizationRef `json:"organizationRef" webhook:"inmutable"`
|
||||||
// +kubebuilder:validation:Enum=ACCESS_TOKEN_TYPE_BEARER;ACCESS_TOKEN_TYPE_JWT
|
// +kubebuilder:validation:Enum=ACCESS_TOKEN_TYPE_BEARER;ACCESS_TOKEN_TYPE_JWT
|
||||||
AccessTokenType string `json:"accessTokenType"`
|
AccessTokenType string `json:"accessTokenType"`
|
||||||
|
UserGrants []UserGrant `json:"userGrants,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MachineUserStatus defines the observed state of MachineUser
|
// MachineUserStatus defines the observed state of MachineUser
|
||||||
|
|||||||
@@ -78,6 +78,54 @@ spec:
|
|||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
x-kubernetes-map-type: atomic
|
x-kubernetes-map-type: atomic
|
||||||
|
userGrants:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
projectRef:
|
||||||
|
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:
|
||||||
|
- projectRef
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
required:
|
required:
|
||||||
- accessTokenType
|
- accessTokenType
|
||||||
- organizationRef
|
- organizationRef
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -14,6 +16,8 @@ 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"
|
||||||
|
object "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/object"
|
||||||
|
project "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/project"
|
||||||
user "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/user"
|
user "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/user"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
@@ -103,6 +107,10 @@ func (wr *wrappedMachineUserReconciler) Reconcile(ctx context.Context, ztdClient
|
|||||||
Name: "jwt",
|
Name: "jwt",
|
||||||
Reconcile: wr.reconcileJWT,
|
Reconcile: wr.reconcileJWT,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "usergrants",
|
||||||
|
Reconcile: wr.reconcileUserGrants,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, p := range phases {
|
for _, p := range phases {
|
||||||
err := p.Reconcile(ctx, ztdClient)
|
err := p.Reconcile(ctx, ztdClient)
|
||||||
@@ -275,6 +283,92 @@ func (wr *wrappedMachineUserReconciler) reconcileJWT(ctx context.Context, ztdCli
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedMachineUserReconciler) reconcileUserGrants(ctx context.Context, ztdClient *management.Client) error {
|
||||||
|
org, err := wr.refResolver.OrganizationRef(ctx, &wr.MachineUser.Spec.OrganizationRef, wr.MachineUser.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
existingUserGrants, err := ztdClient.ListUserGrants(ctx, &pb.ListUserGrantRequest{
|
||||||
|
Queries: []*user.UserGrantQuery{
|
||||||
|
{
|
||||||
|
Query: &user.UserGrantQuery_UserIdQuery{
|
||||||
|
UserIdQuery: &user.UserGrantUserIDQuery{
|
||||||
|
UserId: wr.MachineUser.Status.UserId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error listing MachineUser grants: %v", err)
|
||||||
|
}
|
||||||
|
ctx = middleware.SetOrgID(ctx, org.Status.OrgId)
|
||||||
|
for _, userGrant := range wr.MachineUser.DeepCopy().Spec.UserGrants {
|
||||||
|
userGrantedProject, err := wr.refResolver.ProjectRef(ctx, &userGrant.ProjectRef, wr.MachineUser.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var existingUserGrant *user.UserGrant
|
||||||
|
for _, eGrant := range existingUserGrants.Result {
|
||||||
|
if eGrant.ProjectId == userGrantedProject.Status.ProjectId {
|
||||||
|
existingUserGrant = eGrant
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if existingUserGrant == nil {
|
||||||
|
grantedProjects, err := ztdClient.ListGrantedProjects(ctx, &pb.ListGrantedProjectsRequest{
|
||||||
|
Queries: []*project.ProjectQuery{
|
||||||
|
{
|
||||||
|
Query: &project.ProjectQuery_NameQuery{
|
||||||
|
NameQuery: &project.ProjectNameQuery{
|
||||||
|
Name: userGrantedProject.Name,
|
||||||
|
Method: object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error listing granted projects: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingProjectGrant *project.GrantedProject
|
||||||
|
for _, existingGrantedProject := range grantedProjects.Result {
|
||||||
|
if existingGrantedProject.ProjectId == userGrantedProject.Status.ProjectId {
|
||||||
|
existingGrantedProject = existingGrantedProject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if existingProjectGrant == nil {
|
||||||
|
return fmt.Errorf("Error no project granted to user organization: %v", err)
|
||||||
|
}
|
||||||
|
_, err = ztdClient.AddUserGrant(ctx, &pb.AddUserGrantRequest{
|
||||||
|
UserId: wr.MachineUser.Status.UserId,
|
||||||
|
RoleKeys: userGrant.RoleKeys,
|
||||||
|
ProjectId: existingProjectGrant.ProjectId,
|
||||||
|
ProjectGrantId: existingProjectGrant.GrantId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error Adding MachineUser grant: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
sort.Strings(existingUserGrant.RoleKeys)
|
||||||
|
sort.Strings(userGrant.RoleKeys)
|
||||||
|
if !reflect.DeepEqual(existingUserGrant.RoleKeys, userGrant.RoleKeys) {
|
||||||
|
_, err := ztdClient.UpdateUserGrant(ctx, &pb.UpdateUserGrantRequest{
|
||||||
|
UserId: wr.MachineUser.Status.UserId,
|
||||||
|
GrantId: existingUserGrant.Id,
|
||||||
|
RoleKeys: userGrant.RoleKeys,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error Updating MachineUser grant: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (wr *wrappedMachineUserReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
func (wr *wrappedMachineUserReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
||||||
patch := client.MergeFrom(wr.MachineUser.DeepCopy())
|
patch := client.MergeFrom(wr.MachineUser.DeepCopy())
|
||||||
patcher(&wr.MachineUser.Status)
|
patcher(&wr.MachineUser.Status)
|
||||||
|
|||||||
Reference in New Issue
Block a user