From fecb5d7a6ecf0285424816bdc48c9614037225e4 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Mon, 30 Jun 2025 16:25:44 +0200 Subject: [PATCH 1/2] chore: refactor auth Signed-off-by: Miguel Martinez --- app/controlplane/pkg/biz/user.go | 63 +++++++++++++------------------- 1 file changed, 26 insertions(+), 37 deletions(-) diff --git a/app/controlplane/pkg/biz/user.go b/app/controlplane/pkg/biz/user.go index a3eb5cc93..e22f9befb 100644 --- a/app/controlplane/pkg/biz/user.go +++ b/app/controlplane/pkg/biz/user.go @@ -131,59 +131,45 @@ func (uc *UserUseCase) UpsertByEmail(ctx context.Context, email string, opts *Up } // emails are case-insensitive email = strings.ToLower(email) - u, err := uc.userRepo.FindByEmail(ctx, email) if err != nil { return nil, err - } else if u != nil { - uuid, err := uuid.Parse(u.ID) + } + + // Signup case + if u == nil { + u, err = uc.userRepo.CreateByEmail(ctx, email, opts.FirstName, opts.LastName) if err != nil { - return nil, NewErrInvalidUUID(err) + return nil, fmt.Errorf("failed to create user: %w", err) } - // set the context user so it can be used in the auditor - ctx = entities.WithCurrentUser(ctx, &entities.User{Email: u.Email, ID: u.ID}) + uc.auditorUC.Dispatch(ctx, &events.UserSignedUp{ + UserBase: &events.UserBase{ + UserID: ToPtr(uuid.MustParse(u.ID)), + Email: u.Email, + }, + }, nil) + } else { + // Login case uc.auditorUC.Dispatch(ctx, &events.UserLoggedIn{ UserBase: &events.UserBase{ - UserID: &uuid, + UserID: ToPtr(uuid.MustParse(u.ID)), Email: u.Email, }, LoggedIn: time.Now(), }, nil) - - if (opts.FirstName != nil && u.FirstName != *opts.FirstName) || - (opts.LastName != nil && u.LastName != *opts.LastName) { - // Update the user's first and last name if they differ from the provided options - u, err = uc.userRepo.UpdateNameAndLastName(ctx, uuid, opts.FirstName, opts.LastName) - if err != nil { - return nil, fmt.Errorf("failed to update user name: %w", err) - } - } - - return u, nil } - u, err = uc.userRepo.CreateByEmail(ctx, email, opts.FirstName, opts.LastName) - if err != nil { - return nil, fmt.Errorf("failed to create user: %w", err) - } - - // set the context user so it can be used in the auditor - ctx = entities.WithCurrentUser(ctx, &entities.User{Email: u.Email, ID: u.ID}) - uuid, err := uuid.Parse(u.ID) - if err != nil { - return nil, NewErrInvalidUUID(err) + // Update the user's first and last name if they differ from the provided options + if (opts.FirstName != nil && u.FirstName != *opts.FirstName) || (opts.LastName != nil && u.LastName != *opts.LastName) { + u, err = uc.userRepo.UpdateNameAndLastName(ctx, uuid.MustParse(u.ID), opts.FirstName, opts.LastName) + if err != nil { + return nil, fmt.Errorf("failed to update user name: %w", err) + } } - uc.auditorUC.Dispatch(ctx, &events.UserSignedUp{ - UserBase: &events.UserBase{ - UserID: &uuid, - Email: u.Email, - }, - }, nil) - - // Check if we should auto-onboard the user - if opts.DisableAutoOnboarding == nil || (opts.DisableAutoOnboarding != nil && !*opts.DisableAutoOnboarding) { + // Auto-onboard the user to the organizations defined in the configuration + if opts.DisableAutoOnboarding == nil || !*opts.DisableAutoOnboarding { if err := uc.organizationUseCase.AutoOnboardOrganizations(ctx, u.ID); err != nil { return nil, fmt.Errorf("failed to auto-onboard user: %w", err) } @@ -195,6 +181,9 @@ func (uc *UserUseCase) UpsertByEmail(ctx context.Context, email string, opts *Up return nil, fmt.Errorf("failed to update user access: %w", err) } + // set the context user so it can be used in the auditor + ctx = entities.WithCurrentUser(ctx, &entities.User{Email: u.Email, ID: u.ID}) + return u, nil } From 911718772f33a02b79fc80f0d7d8a5906e9d8540 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Tue, 1 Jul 2025 17:58:47 +0200 Subject: [PATCH 2/2] feat: update auto-onboarding mode Signed-off-by: Miguel Martinez --- app/controlplane/pkg/biz/organization.go | 29 +++++++++++++++--------- app/controlplane/pkg/biz/user.go | 6 ++--- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/app/controlplane/pkg/biz/organization.go b/app/controlplane/pkg/biz/organization.go index 96b04871c..abe51f680 100644 --- a/app/controlplane/pkg/biz/organization.go +++ b/app/controlplane/pkg/biz/organization.go @@ -61,7 +61,7 @@ func NewOrganizationUseCase(repo OrganizationRepo, repoUC *CASBackendUseCase, au l = log.NewStdLogger(io.Discard) } - return &OrganizationUseCase{orgRepo: repo, + uc := &OrganizationUseCase{orgRepo: repo, logger: servicelogger.ScopedHelper(l, "biz/organization"), casBackendUseCase: repoUC, integrationUC: iUC, @@ -69,6 +69,14 @@ func NewOrganizationUseCase(repo OrganizationRepo, repoUC *CASBackendUseCase, au onboardingConfig: onboardingConfig, auditor: auditor, } + + if len(onboardingConfig) > 0 { + for _, spec := range onboardingConfig { + uc.logger.Infow("msg", "Auto-onboarding enabled", "org", spec.GetName(), "role", spec.GetRole()) + } + } + + return uc } const RandomNameMaxTries = 10 @@ -294,18 +302,12 @@ func (uc *OrganizationUseCase) AutoOnboardOrganizations(ctx context.Context, use if err != nil { return fmt.Errorf("failed to find organization: %w", err) } else if org == nil { - uc.logger.Infow("msg", "Organization not found", "name", spec.GetName()) + uc.logger.Infow("msg", "can't auto-onboard to organization, not found", "name", spec.GetName()) continue } - // Parse organization UUID - orgUUID, err := uuid.Parse(org.ID) - if err != nil { - return NewErrInvalidUUID(err) - } - // Ensure user membership - if err := uc.ensureUserMembership(ctx, orgUUID, userUUID, PbRoleToBiz(spec.GetRole())); err != nil { + if err := uc.ensureUserMembership(ctx, org, userUUID, PbRoleToBiz(spec.GetRole())); err != nil { return fmt.Errorf("failed to ensure user membership: %w", err) } } @@ -315,7 +317,12 @@ func (uc *OrganizationUseCase) AutoOnboardOrganizations(ctx context.Context, use // ensureUserMembership ensures that a user is a member of the specified organization with the appropriate role. // If the membership does not exist, it creates it. -func (uc *OrganizationUseCase) ensureUserMembership(ctx context.Context, orgUUID, userUUID uuid.UUID, role authz.Role) error { +func (uc *OrganizationUseCase) ensureUserMembership(ctx context.Context, org *Organization, userUUID uuid.UUID, role authz.Role) error { + orgUUID, err := uuid.Parse(org.ID) + if err != nil { + return NewErrInvalidUUID(err) + } + m, err := uc.membershipRepo.FindByOrgAndUser(ctx, orgUUID, userUUID) if err != nil { return fmt.Errorf("failed to find membership: %w", err) @@ -332,6 +339,6 @@ func (uc *OrganizationUseCase) ensureUserMembership(ctx context.Context, orgUUID return fmt.Errorf("failed to create membership: %w", err) } - uc.logger.Infow("msg", "User auto-onboarded to organization", "org", orgUUID, "user", userUUID, "role", role) + uc.logger.Infow("msg", "User auto-onboarded to organization", "orgName", org.Name, "orgID", orgUUID, "user", userUUID, "role", role) return nil } diff --git a/app/controlplane/pkg/biz/user.go b/app/controlplane/pkg/biz/user.go index e22f9befb..12bb36a7f 100644 --- a/app/controlplane/pkg/biz/user.go +++ b/app/controlplane/pkg/biz/user.go @@ -143,6 +143,8 @@ func (uc *UserUseCase) UpsertByEmail(ctx context.Context, email string, opts *Up return nil, fmt.Errorf("failed to create user: %w", err) } + // set the context user so it can be used in the auditor + ctx = entities.WithCurrentUser(ctx, &entities.User{Email: u.Email, ID: u.ID}) uc.auditorUC.Dispatch(ctx, &events.UserSignedUp{ UserBase: &events.UserBase{ UserID: ToPtr(uuid.MustParse(u.ID)), @@ -151,6 +153,7 @@ func (uc *UserUseCase) UpsertByEmail(ctx context.Context, email string, opts *Up }, nil) } else { // Login case + ctx = entities.WithCurrentUser(ctx, &entities.User{Email: u.Email, ID: u.ID}) uc.auditorUC.Dispatch(ctx, &events.UserLoggedIn{ UserBase: &events.UserBase{ UserID: ToPtr(uuid.MustParse(u.ID)), @@ -181,9 +184,6 @@ func (uc *UserUseCase) UpsertByEmail(ctx context.Context, email string, opts *Up return nil, fmt.Errorf("failed to update user access: %w", err) } - // set the context user so it can be used in the auditor - ctx = entities.WithCurrentUser(ctx, &entities.User{Email: u.Email, ID: u.ID}) - return u, nil }