From f7695e13980ce89cdcdc08bae9e8bab27d9226ec Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Fri, 20 Feb 2026 15:15:38 -0300 Subject: [PATCH 1/2] feat: Implement automatic platform access approval creation for new users and update waitlist email templates and variables. --- .../controller-manager/controllermanager.go | 2 +- internal/controllers/iam/user_controller.go | 51 +++++++++++++++++++ .../iam/user_waitlist_controller.go | 6 +-- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/cmd/milo/controller-manager/controllermanager.go b/cmd/milo/controller-manager/controllermanager.go index 7de15bb9..3cd896a8 100644 --- a/cmd/milo/controller-manager/controllermanager.go +++ b/cmd/milo/controller-manager/controllermanager.go @@ -292,7 +292,7 @@ func NewCommand() *cobra.Command { fs.StringVar(&AcceptInvitationRoleName, "accept-invitation-role-name", "iam.miloapis.com-acceptinvitation", "The name of the role that will be used to grant accept invitation permissions.") fs.StringVar(&UserInvitationEmailTemplate, "user-invitation-email-template", "emailtemplates.notification.miloapis.com-userinvitationemailtemplate", "The name of the template that will be used to send the user invitation email.") fs.StringVar(&UserWaitlistPendingEmailTemplate, "user-waitlist-pending-email-template", "emailtemplates.notification.miloapis.com-userwaitlistemailtemplate", "The name of the template that will be used to send the waitlist pending email.") - fs.StringVar(&UserWaitlistApprovedEmailTemplate, "user-waitlist-approved-email-template", "emailtemplates.notification.miloapis.com-userapprovedemailtemplate", "The name of the template that will be used to send the waitlist approved email.") + fs.StringVar(&UserWaitlistApprovedEmailTemplate, "user-waitlist-approved-email-template", "emailtemplates.notification.miloapis.com-userwelcomeemailtemplate", "The name of the template that will be used to send the waitlist approved email.") fs.StringVar(&UserWaitlistRejectedEmailTemplate, "user-waitlist-rejected-email-template", "emailtemplates.notification.miloapis.com-userrejectedemailtemplate", "The name of the template that will be used to send the waitlist rejected email.") fs.StringVar(&PlatformInvitationEmailTemplate, "platform-invitation-email-template", "emailtemplates.notification.miloapis.com-platforminvitationemailtemplate", "The name of the template that will be used to send the platform invitation email.") fs.StringVar(&WaitlistRelatedResourcesNamespace, "waitlist-related-resources-namespace", "milo-system", "The namespace that contains the waitlist related resources.") diff --git a/internal/controllers/iam/user_controller.go b/internal/controllers/iam/user_controller.go index 2dcd8d4c..2dd066f2 100644 --- a/internal/controllers/iam/user_controller.go +++ b/internal/controllers/iam/user_controller.go @@ -81,6 +81,11 @@ func (r *UserController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. // Capture the current status to detect changes later oldUserStatus := user.Status.DeepCopy() + // Ensure platform access approval exists for new users + if err := r.ensurePlatformAccessApprovalToNewUsers(ctx, user); err != nil { + log.Error(err, "failed to ensure platform access approval to new users") + return ctrl.Result{}, fmt.Errorf("failed to ensure platform access approval to new users: %w", err) + } // Get the user access approval status registrationApproval, err := r.getUserAccessApprovalStatus(ctx, user) if err != nil { @@ -357,3 +362,49 @@ func (r *UserController) getUserAccessApprovalStatus(ctx context.Context, user * return iamv1alpha1.RegistrationApprovalStatePending, nil } + +// ensurePlatformAccessApprovalToNewUsers ensures that a PlatformAccessApproval exists for new users +func (r *UserController) ensurePlatformAccessApprovalToNewUsers(ctx context.Context, user *iamv1alpha1.User) error { + log := log.FromContext(ctx).WithName("ensure-platform-access-approval-to-new-users") + + paName := fmt.Sprintf("new-user-access-approval-%s", user.Name) + + // Check if it has a PlatformAccessApproval related to email address or user reference + userReferences := []string{user.Spec.Email, user.Name} + for _, reference := range userReferences { + paas := &iamv1alpha1.PlatformAccessApprovalList{} + if err := r.Client.List(ctx, paas, client.MatchingFields{platformAccessApprovalIndexKey: reference}); err != nil { + log.Error(err, "failed to list platformaccessapprovals", "reference", reference) + return fmt.Errorf("failed to list platformaccessapprovals: %w", err) + } + if len(paas.Items) > 0 { + return nil + } + } + + if user.Status.RegistrationApproval == "" { + // If here, this is a new user with not approval yet + platformAccessApproval := &iamv1alpha1.PlatformAccessApproval{ + ObjectMeta: metav1.ObjectMeta{ + Name: paName, + }, + Spec: iamv1alpha1.PlatformAccessApprovalSpec{ + SubjectRef: iamv1alpha1.SubjectReference{ + UserRef: &iamv1alpha1.UserReference{ + Name: user.Name, + }, + }, + }, + } + if err := r.Client.Create(ctx, platformAccessApproval); err != nil { + if apierrors.IsAlreadyExists(err) { + log.Info("PlatformAccessApproval already exists", "platformAccessApproval", platformAccessApproval.Name) + return nil + } + log.Error(err, "Failed to create PlatformAccessApproval") + return fmt.Errorf("failed to create PlatformAccessApproval: %w", err) + } + } + + return nil +} diff --git a/internal/controllers/iam/user_waitlist_controller.go b/internal/controllers/iam/user_waitlist_controller.go index 8de43b96..a15f16cc 100644 --- a/internal/controllers/iam/user_waitlist_controller.go +++ b/internal/controllers/iam/user_waitlist_controller.go @@ -203,7 +203,7 @@ func (r *UserWaitlistController) getEmailTemplateName(condition iamv1alpha1.User } func (r *UserWaitlistController) getEmailVariables(condition iamv1alpha1.UserWaitlistEmailSentCondition, user *iamv1alpha1.User) []notificationv1alpha1.EmailVariable { - userName := fmt.Sprintf("%s %s", user.Spec.GivenName, user.Spec.FamilyName) + userName := user.Spec.GivenName if userName == "" { userName = user.Spec.Email } @@ -211,10 +211,6 @@ func (r *UserWaitlistController) getEmailVariables(condition iamv1alpha1.UserWai switch condition { case iamv1alpha1.UserWaitlistApprovedEmailSentCondition: return []notificationv1alpha1.EmailVariable{ - { - Name: "ActionUrl", - Value: "https://cloud.datum.net", - }, { Name: "UserName", Value: userName, From 762d1e804fe11dd0dad2c9a83e4532930a8da64d Mon Sep 17 00:00:00 2001 From: Jose Szychowski Date: Fri, 20 Feb 2026 15:22:45 -0300 Subject: [PATCH 2/2] refactor: conditionally check for existing platform access approvals only for users without registration approval. --- internal/controllers/iam/user_controller.go | 28 ++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/controllers/iam/user_controller.go b/internal/controllers/iam/user_controller.go index 2dd066f2..8ab501a3 100644 --- a/internal/controllers/iam/user_controller.go +++ b/internal/controllers/iam/user_controller.go @@ -367,22 +367,22 @@ func (r *UserController) getUserAccessApprovalStatus(ctx context.Context, user * func (r *UserController) ensurePlatformAccessApprovalToNewUsers(ctx context.Context, user *iamv1alpha1.User) error { log := log.FromContext(ctx).WithName("ensure-platform-access-approval-to-new-users") - paName := fmt.Sprintf("new-user-access-approval-%s", user.Name) - - // Check if it has a PlatformAccessApproval related to email address or user reference - userReferences := []string{user.Spec.Email, user.Name} - for _, reference := range userReferences { - paas := &iamv1alpha1.PlatformAccessApprovalList{} - if err := r.Client.List(ctx, paas, client.MatchingFields{platformAccessApprovalIndexKey: reference}); err != nil { - log.Error(err, "failed to list platformaccessapprovals", "reference", reference) - return fmt.Errorf("failed to list platformaccessapprovals: %w", err) - } - if len(paas.Items) > 0 { - return nil + if user.Status.RegistrationApproval == "" { + paName := fmt.Sprintf("new-user-access-approval-%s", user.Name) + + // Check if it has a PlatformAccessApproval related to email address or user reference + userReferences := []string{user.Spec.Email, user.Name} + for _, reference := range userReferences { + paas := &iamv1alpha1.PlatformAccessApprovalList{} + if err := r.Client.List(ctx, paas, client.MatchingFields{platformAccessApprovalIndexKey: reference}); err != nil { + log.Error(err, "failed to list platformaccessapprovals", "reference", reference) + return fmt.Errorf("failed to list platformaccessapprovals: %w", err) + } + if len(paas.Items) > 0 { + return nil + } } - } - if user.Status.RegistrationApproval == "" { // If here, this is a new user with not approval yet platformAccessApproval := &iamv1alpha1.PlatformAccessApproval{ ObjectMeta: metav1.ObjectMeta{