From 9e7af107d16aa7e3296acd37244d4c6bf509e2e3 Mon Sep 17 00:00:00 2001 From: Haim Kortovich Date: Mon, 20 May 2024 18:12:26 -0500 Subject: [PATCH] Reissue PAT if roles changed [ZITADOPER-1] --- src/api/v1alpha1/condition_types.go | 9 +- src/api/v1alpha1/machineuser_types.go | 7 ++ .../controller/machineuser_controller.go | 51 +++++++- src/pkg/condition/complete.go | 111 ------------------ src/pkg/condition/pat.go | 25 ++++ 5 files changed, 83 insertions(+), 120 deletions(-) delete mode 100644 src/pkg/condition/complete.go create mode 100644 src/pkg/condition/pat.go diff --git a/src/api/v1alpha1/condition_types.go b/src/api/v1alpha1/condition_types.go index 226b00a..a16473e 100644 --- a/src/api/v1alpha1/condition_types.go +++ b/src/api/v1alpha1/condition_types.go @@ -1,10 +1,11 @@ package v1alpha1 const ( - ConditionTypeReady string = "Ready" - ConditionTypeBackupRestored string = "BackupRestored" - ConditionTypeReplicationConfigured string = "ReplicationConfigured" - ConditionTypeComplete string = "Complete" + ConditionTypeReady string = "Ready" + ConditionTypePATUpToDate string = "PATUpToDate" + + ConditionReasonRolesChanged string = "RolesChanged" + ConditionReasonPATUpToDate string = "UpToDate" ConditionReasonDeploymentNotReady string = "DeploymentNotReady" ConditionReasonDeploymentReady string = "DeploymentReady" diff --git a/src/api/v1alpha1/machineuser_types.go b/src/api/v1alpha1/machineuser_types.go index 4a64889..a4bc10a 100644 --- a/src/api/v1alpha1/machineuser_types.go +++ b/src/api/v1alpha1/machineuser_types.go @@ -66,6 +66,13 @@ func (d *MachineUserStatus) SetCondition(condition metav1.Condition) { meta.SetStatusCondition(&d.Conditions, condition) } +func (d *MachineUserStatus) GetConditionStatus(conditionType string) bool { + if d.Conditions == nil { + d.Conditions = make([]metav1.Condition, 0) + } + return meta.IsStatusConditionTrue(d.Conditions, conditionType) +} + //+kubebuilder:object:root=true //+kubebuilder:subresource:status diff --git a/src/internal/controller/machineuser_controller.go b/src/internal/controller/machineuser_controller.go index f7c4bf8..ca2e28f 100644 --- a/src/internal/controller/machineuser_controller.go +++ b/src/internal/controller/machineuser_controller.go @@ -21,10 +21,12 @@ import ( project "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/project" user "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/user" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/util/workqueue" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + clientpkg "sigs.k8s.io/controller-runtime/pkg/client" ctrlClient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" ) @@ -192,7 +194,15 @@ func (wr *wrappedMachineUserReconciler) reconcilePAT(ctx context.Context, ztdCli } } - if token == nil { + if token == nil || !wr.MachineUser.Status.GetConditionStatus(zitadelv1alpha1.ConditionTypePATUpToDate) { + if token != nil { + if _, err = ztdClient.RemovePersonalAccessToken(ctx, &pb.RemovePersonalAccessTokenRequest{ + UserId: wr.MachineUser.Status.UserId, + TokenId: wr.MachineUser.Status.PATId, + }); err != nil { + return fmt.Errorf("Error removing PAT: %v", err) + } + } resp, err := ztdClient.AddPersonalAccessToken(ctx, &pb.AddPersonalAccessTokenRequest{ UserId: wr.MachineUser.Status.UserId, }) @@ -203,7 +213,7 @@ func (wr *wrappedMachineUserReconciler) reconcilePAT(ctx context.Context, ztdCli Name: wr.MachineUser.PatSecretName(), Namespace: wr.MachineUser.Namespace, } - patSecret, err := wr.Builder.BuildSecret(builder.SecretOpts{ + desiredPatSecret, err := wr.Builder.BuildSecret(builder.SecretOpts{ Key: key, Immutable: true, Data: map[string][]byte{ @@ -214,9 +224,30 @@ func (wr *wrappedMachineUserReconciler) reconcilePAT(ctx context.Context, ztdCli if err != nil { return fmt.Errorf("error building PAT Secret: %v", err) } - if err := wr.Create(ctx, patSecret); err != nil { - return fmt.Errorf("error creating pat-secret Secret: %v", err) + + { + var existingPatSecret corev1.Secret + if err := wr.Get(ctx, key, &existingPatSecret); err != nil { + if !errors.IsNotFound(err) { + return fmt.Errorf("error getting PAT Secret: %v", err) + } + if err := wr.Create(ctx, desiredPatSecret); err != nil { + return fmt.Errorf("error creating PAT Secret: %v", err) + } + } + + patch := clientpkg.MergeFrom(existingPatSecret.DeepCopy()) + existingPatSecret.Data = desiredPatSecret.Data + if err = wr.Patch(ctx, &existingPatSecret, patch); err != nil { + return err + } + } + + if err = wr.PatchStatus(ctx, condition.SetPatUpToDate); err != nil { + return err + } + patch := ctrlClient.MergeFrom(wr.MachineUser.DeepCopy()) wr.MachineUser.Status.PATId = resp.TokenId return wr.Client.Status().Patch(ctx, wr.MachineUser, patch) @@ -268,7 +299,7 @@ func (wr *wrappedMachineUserReconciler) reconcileJWT(ctx context.Context, ztdCli } patSecret, err := wr.Builder.BuildSecret(builder.SecretOpts{ Key: key, - Immutable: true, + Immutable: false, Data: secretData, }, wr.MachineUser) @@ -343,6 +374,11 @@ func (wr *wrappedMachineUserReconciler) reconcileUserGrants(ctx context.Context, if existingProjectGrant == nil { return fmt.Errorf("Error no project granted to user organization: %v", err) } + + if err = wr.PatchStatus(ctx, condition.SetPatOutOfDate); err != nil { + return err + } + _, err = ztdClient.AddUserGrant(ctx, &pb.AddUserGrantRequest{ UserId: wr.MachineUser.Status.UserId, RoleKeys: userGrant.RoleKeys, @@ -357,6 +393,11 @@ func (wr *wrappedMachineUserReconciler) reconcileUserGrants(ctx context.Context, sort.Strings(existingUserGrant.RoleKeys) sort.Strings(userGrant.RoleKeys) if !reflect.DeepEqual(existingUserGrant.RoleKeys, userGrant.RoleKeys) { + + if err = wr.PatchStatus(ctx, condition.SetPatOutOfDate); err != nil { + return err + } + _, err := ztdClient.UpdateUserGrant(ctx, &pb.UpdateUserGrantRequest{ UserId: wr.MachineUser.Status.UserId, GrantId: existingUserGrant.Id, diff --git a/src/pkg/condition/complete.go b/src/pkg/condition/complete.go deleted file mode 100644 index 3685bd6..0000000 --- a/src/pkg/condition/complete.go +++ /dev/null @@ -1,111 +0,0 @@ -package conditions - -import ( - zitadelv1alpha1 "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1" - batchv1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func SetCompleteWithCronJob(c Conditioner, cronJob *batchv1.CronJob) { - setScheduled := func() { - c.SetCondition(metav1.Condition{ - Type: zitadelv1alpha1.ConditionTypeComplete, - Status: metav1.ConditionFalse, - Reason: zitadelv1alpha1.ConditionReasonCronJobScheduled, - Message: "Scheduled", - }) - } - - if cronJob.Status.LastScheduleTime == nil || cronJob.Status.LastSuccessfulTime == nil { - setScheduled() - return - } - if cronJob.Status.LastSuccessfulTime.Before(cronJob.Status.LastScheduleTime) { - if len(cronJob.Status.Active) > 0 { - c.SetCondition(metav1.Condition{ - Type: zitadelv1alpha1.ConditionTypeComplete, - Status: metav1.ConditionFalse, - Reason: zitadelv1alpha1.ConditionReasonCronJobRunning, - Message: "Running", - }) - } else { - c.SetCondition(metav1.Condition{ - Type: zitadelv1alpha1.ConditionTypeComplete, - Status: metav1.ConditionFalse, - Reason: zitadelv1alpha1.ConditionReasonCronJobFailed, - Message: "Failed", - }) - } - return - } - - if cronJob.Status.LastScheduleTime.Equal(cronJob.Status.LastSuccessfulTime) || - cronJob.Status.LastScheduleTime.Before(cronJob.Status.LastSuccessfulTime) { - c.SetCondition(metav1.Condition{ - Type: zitadelv1alpha1.ConditionTypeComplete, - Status: metav1.ConditionTrue, - Reason: zitadelv1alpha1.ConditionReasonCronJobSuccess, - Message: "Success", - }) - return - } - - setScheduled() -} - -func SetCompleteWithJob(c Conditioner, job *batchv1.Job) { - switch getJobConditionType(job) { - case batchv1.JobFailed: - c.SetCondition(metav1.Condition{ - Type: zitadelv1alpha1.ConditionTypeComplete, - Status: metav1.ConditionTrue, - Reason: zitadelv1alpha1.ConditionReasonJobFailed, - Message: "Failed", - }) - case batchv1.JobComplete: - c.SetCondition(metav1.Condition{ - Type: zitadelv1alpha1.ConditionTypeComplete, - Status: metav1.ConditionTrue, - Reason: zitadelv1alpha1.ConditionReasonJobComplete, - Message: "Success", - }) - case batchv1.JobSuspended: - c.SetCondition(metav1.Condition{ - Type: zitadelv1alpha1.ConditionTypeComplete, - Status: metav1.ConditionFalse, - Reason: zitadelv1alpha1.ConditionReasonJobSuspended, - Message: "Suspended", - }) - default: - c.SetCondition(metav1.Condition{ - Type: zitadelv1alpha1.ConditionTypeComplete, - Status: metav1.ConditionFalse, - Reason: zitadelv1alpha1.ConditionReasonJobRunning, - Message: "Running", - }) - } -} - -func SetCompleteFailedWithMessage(c Conditioner, message string) { - c.SetCondition(metav1.Condition{ - Type: zitadelv1alpha1.ConditionTypeComplete, - Status: metav1.ConditionFalse, - Reason: zitadelv1alpha1.ConditionReasonFailed, - Message: message, - }) -} - -func SetCompleteFailed(c Conditioner) { - SetCompleteFailedWithMessage(c, "Failed") -} - -func getJobConditionType(job *batchv1.Job) batchv1.JobConditionType { - for _, c := range job.Status.Conditions { - if c.Status == corev1.ConditionFalse { - continue - } - return c.Type - } - return "" -} diff --git a/src/pkg/condition/pat.go b/src/pkg/condition/pat.go new file mode 100644 index 0000000..18ab0bc --- /dev/null +++ b/src/pkg/condition/pat.go @@ -0,0 +1,25 @@ +package conditions + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + zitadelv1alpha1 "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1" +) + +func SetPatOutOfDate(c Conditioner) { + c.SetCondition(metav1.Condition{ + Type: zitadelv1alpha1.ConditionTypePATUpToDate, + Status: metav1.ConditionFalse, + Reason: zitadelv1alpha1.ConditionReasonRolesChanged, + Message: "PAT out of date", + }) +} + +func SetPatUpToDate(c Conditioner) { + c.SetCondition(metav1.Condition{ + Type: zitadelv1alpha1.ConditionTypePATUpToDate, + Status: metav1.ConditionTrue, + Reason: zitadelv1alpha1.ConditionReasonPATUpToDate, + Message: "PAT up to date", + }) +}