diff --git a/ops/chart/crds/machineuser-crd.yaml b/ops/chart/crds/machineuser-crd.yaml index 12327c2..4e7ad1c 100644 --- a/ops/chart/crds/machineuser-crd.yaml +++ b/ops/chart/crds/machineuser-crd.yaml @@ -158,11 +158,15 @@ spec: keyId: default: "" type: string + patId: + default: "" + type: string userId: default: "" type: string required: - keyId + - patId - userId type: object type: object diff --git a/src/api/v1alpha1/machineuser_types.go b/src/api/v1alpha1/machineuser_types.go index da1d743..83eefcd 100644 --- a/src/api/v1alpha1/machineuser_types.go +++ b/src/api/v1alpha1/machineuser_types.go @@ -49,6 +49,9 @@ type MachineUserStatus struct { UserId string `json:"userId"` // +kubebuilder:default="" KeyId string `json:"keyId"` + + // +kubebuilder:default="" + PATId string `json:"patId"` } func (d *MachineUserStatus) SetCondition(condition metav1.Condition) { diff --git a/src/config/crd/bases/zitadel.topmanage.com_machineusers.yaml b/src/config/crd/bases/zitadel.topmanage.com_machineusers.yaml index a4fbc88..53f1878 100644 --- a/src/config/crd/bases/zitadel.topmanage.com_machineusers.yaml +++ b/src/config/crd/bases/zitadel.topmanage.com_machineusers.yaml @@ -159,11 +159,15 @@ spec: keyId: default: "" type: string + patId: + default: "" + type: string userId: default: "" type: string required: - keyId + - patId - userId type: object type: object diff --git a/src/internal/controller/machineuser_controller.go b/src/internal/controller/machineuser_controller.go index 2130b07..1044d61 100644 --- a/src/internal/controller/machineuser_controller.go +++ b/src/internal/controller/machineuser_controller.go @@ -12,7 +12,6 @@ import ( "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/controller/zitadel" "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/zitadel/authn" pb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management" user "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/user" corev1 "k8s.io/api/core/v1" @@ -84,82 +83,130 @@ func newWrappedMachineUserReconciler(client client.Client, refResolver *zitadelv } } +type machineUserReconcilePhase struct { + Name string + Reconcile func(context.Context, *management.Client) error +} + func (wr *wrappedMachineUserReconciler) Reconcile(ctx context.Context, ztdClient *management.Client) error { - // TODO: update machine user + phases := []machineUserReconcilePhase{ + { + Name: "machineUser", + Reconcile: wr.reconcileMachineUser, + }, + { + Name: "pat", + Reconcile: wr.reconcilePAT, + }, + } + for _, p := range phases { + err := p.Reconcile(ctx, ztdClient) + if err != nil { + return err + } + } + return nil +} + +func (wr *wrappedMachineUserReconciler) reconcileMachineUser(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 } - if wr.MachineUser.Status.UserId != "" { + zitadel, err := wr.refResolver.ZitadelCluster(ctx, &org.Spec.ZitadelClusterRef, wr.MachineUser.Namespace) + if err != nil { + return err + } + machineUser, err := ztdClient.GetUserByLoginNameGlobal(ctx, &pb.GetUserByLoginNameGlobalRequest{ + LoginName: strings.ToLower(fmt.Sprintf("%s@%s.%s", wr.MachineUser.Name, org.Name, zitadel.Spec.Host)), + }) + if err != nil { + if !strings.Contains(err.Error(), "could not be found") { + return fmt.Errorf("Error getting machineuser: %v", err) + } + } + + var userid string + if machineUser == nil { + resp, err := ztdClient.AddMachineUser(middleware.SetOrgID(ctx, org.Status.OrgId), + &pb.AddMachineUserRequest{ + Name: wr.MachineUser.Name, + UserName: wr.MachineUser.Name, + Description: wr.MachineUser.Name, + AccessTokenType: user.AccessTokenType(user.AccessTokenType_value[wr.MachineUser.Spec.AccessTokenType]), + }, + ) + if err != nil { + return fmt.Errorf("error creating MachineUser in Zitadel: %v", err) + } + userid = resp.UserId + } else { _, err = ztdClient.UpdateMachine(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.UpdateMachineRequest{ - UserId: wr.MachineUser.Status.UserId, + UserId: machineUser.User.Id, Name: wr.MachineUser.Name, Description: wr.MachineUser.Name, AccessTokenType: user.AccessTokenType(user.AccessTokenType_value[wr.MachineUser.Spec.AccessTokenType]), }) if err != nil { if !strings.Contains(err.Error(), "No changes") { - return fmt.Errorf("Error updating OIDCApp: %v", err) + return fmt.Errorf("Error updating MchineUser: %v", err) } } - return nil - } - - resp, err := ztdClient.AddMachineUser(middleware.SetOrgID(ctx, org.Status.OrgId), - &pb.AddMachineUserRequest{ - Name: wr.MachineUser.Name, - UserName: wr.MachineUser.Name, - Description: wr.MachineUser.Name, - AccessTokenType: user.AccessTokenType(user.AccessTokenType_value[wr.MachineUser.Spec.AccessTokenType]), - }, - ) - if err != nil { - if strings.Contains(err.Error(), "AlreadyExists") { - return nil - } - return fmt.Errorf("error creating MachineUser in Zitadel: %v", err) + userid = machineUser.User.Id } patch := ctrlClient.MergeFrom(wr.MachineUser.DeepCopy()) - wr.MachineUser.Status.UserId = resp.UserId - if err := wr.Client.Status().Patch(ctx, wr.MachineUser, patch); err != nil { + wr.MachineUser.Status.UserId = userid + return wr.Client.Status().Patch(ctx, wr.MachineUser, patch) +} + +func (wr *wrappedMachineUserReconciler) reconcilePAT(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 } + ctx = middleware.SetOrgID(ctx, org.Status.OrgId) - if wr.MachineUser.Status.KeyId != "" { - respKey, err := ztdClient.AddMachineKey(ctx, &pb.AddMachineKeyRequest{ - UserId: resp.UserId, - Type: authn.KeyType_KEY_TYPE_JSON, - ExpirationDate: nil, - }) - if err != nil { - return fmt.Errorf("Error Adding MachineKey: %v", err) + token, err := ztdClient.GetPersonalAccessTokenByIDs(ctx, &pb.GetPersonalAccessTokenByIDsRequest{ + UserId: wr.MachineUser.Status.UserId, + TokenId: wr.MachineUser.Status.PATId, + }) + if err != nil { + if !strings.Contains(err.Error(), "not found") { + return fmt.Errorf("Error getting PAT: %v", err) } - key := types.NamespacedName{ - Name: wr.MachineUser.Name + "-machinekey-secret", - Namespace: wr.MachineUser.Namespace, - } - secret, err := wr.Builder.BuildSecret( - builder.SecretOpts{ - Key: key, - Immutable: true, - Data: map[string][]byte{ - "key.json": respKey.KeyDetails, - }, - }, wr.MachineUser) - - if err != nil { - return fmt.Errorf("error building Secret: %v", err) - } - if err := wr.Create(ctx, secret); err != nil { - return fmt.Errorf("error creating machine key Secret: %v", err) - } - patch = ctrlClient.MergeFrom(wr.MachineUser.DeepCopy()) - wr.MachineUser.Status.KeyId = respKey.KeyId - return wr.Client.Status().Patch(ctx, wr.MachineUser, patch) } + if token == nil { + resp, err := ztdClient.AddPersonalAccessToken(ctx, &pb.AddPersonalAccessTokenRequest{ + UserId: wr.MachineUser.Status.UserId, + }) + if err != nil { + return fmt.Errorf("Error adding PAT: %v", err) + } + key := types.NamespacedName{ + Name: wr.MachineUser.Name + "-pat-secret", + Namespace: wr.MachineUser.Namespace, + } + patSecret, err := wr.Builder.BuildSecret(builder.SecretOpts{ + Key: key, + Immutable: true, + Data: map[string][]byte{ + "pat": []byte(resp.Token), + }, + }, wr.MachineUser) + + 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) + } + patch := ctrlClient.MergeFrom(wr.MachineUser.DeepCopy()) + wr.MachineUser.Status.PATId = resp.TokenId + return wr.Client.Status().Patch(ctx, wr.MachineUser, patch) + } return nil }