From f47a65cfaddea9b3ceab068762ac5aae498d606c Mon Sep 17 00:00:00 2001 From: Brandon Date: Thu, 8 May 2025 13:26:52 -0400 Subject: [PATCH] rename OrganizatinUserInvite for command, implement command --- .../Controllers/OrganizationController.cs | 12 ++--- .../ImportOrganizationUserCommand.cs | 49 ++++++++++++------- .../InviteOrganizationUsersCommand.cs | 3 +- .../CreateOrganizationUserExtensions.cs | 2 +- .../Models/InviteOrganizationUsersRequest.cs | 4 +- ...nviteOrganizationUsersValidationRequest.cs | 2 +- ... => OrganizationUserInviteCommandModel.cs} | 8 +-- .../InviteOrganizationUserValidator.cs | 3 +- .../Services/IOrganizationService.cs | 4 -- .../Implementations/OrganizationService.cs | 24 +-------- 10 files changed, 48 insertions(+), 63 deletions(-) rename src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/{OrganizationUserInvite.cs => OrganizationUserInviteCommandModel.cs} (88%) diff --git a/src/Api/AdminConsole/Public/Controllers/OrganizationController.cs b/src/Api/AdminConsole/Public/Controllers/OrganizationController.cs index c1715f471c..25f5ea2172 100644 --- a/src/Api/AdminConsole/Public/Controllers/OrganizationController.cs +++ b/src/Api/AdminConsole/Public/Controllers/OrganizationController.cs @@ -4,7 +4,7 @@ using Bit.Api.Models.Public.Response; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Exceptions; -using Bit.Core.Services; +using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.Settings; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -15,18 +15,18 @@ namespace Bit.Api.AdminConsole.Public.Controllers; [Authorize("Organization")] public class OrganizationController : Controller { - private readonly IOrganizationService _organizationService; private readonly ICurrentContext _currentContext; private readonly GlobalSettings _globalSettings; + private readonly IImportOrganizationUserCommand _importOrganizationUserCommand; public OrganizationController( - IOrganizationService organizationService, ICurrentContext currentContext, - GlobalSettings globalSettings) + GlobalSettings globalSettings, + IImportOrganizationUserCommand importOrganizationUserCommand) { - _organizationService = organizationService; _currentContext = currentContext; _globalSettings = globalSettings; + _importOrganizationUserCommand = importOrganizationUserCommand; } /// @@ -47,7 +47,7 @@ public class OrganizationController : Controller throw new BadRequestException("You cannot import this much data at once."); } - await _organizationService.ImportAsync( + await _importOrganizationUserCommand.ImportAsync( _currentContext.OrganizationId.Value, model.Groups.Select(g => g.ToImportedGroup(_currentContext.OrganizationId.Value)), model.Members.Where(u => !u.Deleted).Select(u => u.ToImportedOrganizationUser()), diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ImportOrganizationUserCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ImportOrganizationUserCommand.cs index 755786789d..5d7ea80470 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ImportOrganizationUserCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ImportOrganizationUserCommand.cs @@ -1,12 +1,15 @@ using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Models.Business; +using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; +using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Billing.Pricing; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Business; -using Bit.Core.Models.Data; +using Bit.Core.Models.Commands; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.Repositories; @@ -14,7 +17,6 @@ using Bit.Core.Services; using Bit.Core.Tools.Enums; using Bit.Core.Tools.Models.Business; using Bit.Core.Tools.Services; -using OrganizationUserInvite = Bit.Core.Models.Business.OrganizationUserInvite; public class ImportOrganizationUserCommand : IImportOrganizationUserCommand { @@ -26,6 +28,8 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand private readonly IReferenceEventService _referenceEventService; private readonly ICurrentContext _currentContext; private readonly IOrganizationService _organizationService; + private readonly IInviteOrganizationUsersCommand _inviteOrganizationUsersCommand; + private readonly IPricingClient _pricingClient; public ImportOrganizationUserCommand(IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, @@ -34,7 +38,9 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand IEventService eventService, IReferenceEventService referenceEventService, ICurrentContext currentContext, - IOrganizationService organizationService + IOrganizationService organizationService, + IInviteOrganizationUsersCommand inviteOrganizationUsersCommand, + IPricingClient pricingClient ) { _organizationRepository = organizationRepository; @@ -45,6 +51,8 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand _referenceEventService = referenceEventService; _currentContext = currentContext; _organizationService = organizationService; + _inviteOrganizationUsersCommand = inviteOrganizationUsersCommand; + _pricingClient = pricingClient; } public async Task ImportAsync(Guid organizationId, @@ -187,7 +195,7 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand var hasStandaloneSecretsManager = await _paymentService.HasSecretsManagerStandalone(organization); - var userInvites = new List<(OrganizationUserInvite, string)>(); + var userInvites = new List(); foreach (var user in newUsers) { if (!usersToAdd.Contains(user.ExternalId) || string.IsNullOrWhiteSpace(user.Email)) @@ -197,14 +205,8 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand try { - var invite = new OrganizationUserInvite - { - Emails = new List { user.Email }, - Type = OrganizationUserType.User, - Collections = new List(), - AccessSecretsManager = hasStandaloneSecretsManager - }; - userInvites.Add((invite, user.ExternalId)); + var invite = new OrganizationUserInviteCommandModel(user.Email, user.ExternalId); + userInvites.Add(new OrganizationUserInviteCommandModel(invite, hasStandaloneSecretsManager)); } catch (BadRequestException) { @@ -213,12 +215,23 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand } } - //@TODO: replace with command - var invitedUsers = await _organizationService.InviteUsersAsync(organization.Id, invitingUserId: null, systemUser: eventSystemUser, userInvites); - foreach (var invitedUser in invitedUsers) - { - importData.ExistingExternalUsersIdDict.Add(invitedUser.ExternalId, invitedUser.Id); - } + var commandResult = await InviteUsersAsync(userInvites, organization); + + //@TODO: replace with command, add invited users from commandResult to importData + //var invitedUsers = await _organizationService.InviteUsersAsync(organization.Id, invitingUserId: null, systemUser: eventSystemUser, userInvites); + //foreach (var invitedUser in invitedUsers) + //{ + // importData.ExistingExternalUsersIdDict.Add(invitedUser.ExternalId, invitedUser.Id); + //} + } + + private async Task> InviteUsersAsync(List invites, Organization organization) + { + var plan = await _pricingClient.GetPlanOrThrow(organization.PlanType); + var inviteOrganization = new InviteOrganization(organization, plan); + var request = new InviteOrganizationUsersRequest(invites.ToArray(), inviteOrganization, Guid.Empty, DateTimeOffset.UtcNow); + + return await _inviteOrganizationUsersCommand.InviteScimOrganizationUserAsync(request); } private async Task OverwriteExisting( diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/InviteOrganizationUsersCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/InviteOrganizationUsersCommand.cs index 662ed314ce..7ce3848661 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/InviteOrganizationUsersCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/InviteOrganizationUsersCommand.cs @@ -19,7 +19,6 @@ using Bit.Core.Tools.Enums; using Bit.Core.Tools.Models.Business; using Bit.Core.Tools.Services; using Microsoft.Extensions.Logging; -using OrganizationUserInvite = Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.OrganizationUserInvite; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; @@ -146,7 +145,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService, organizationId: organization!.Id)); } - private async Task> FilterExistingUsersAsync(InviteOrganizationUsersRequest request) + private async Task> FilterExistingUsersAsync(InviteOrganizationUsersRequest request) { var existingEmails = new HashSet(await organizationUserRepository.SelectKnownEmailsAsync( request.InviteOrganization.OrganizationId, request.Invites.Select(i => i.Email), false), diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/CreateOrganizationUserExtensions.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/CreateOrganizationUserExtensions.cs index 23c38a51cb..b0f81bd92a 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/CreateOrganizationUserExtensions.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/CreateOrganizationUserExtensions.cs @@ -7,7 +7,7 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUse public static class CreateOrganizationUserExtensions { - public static CreateOrganizationUser MapToDataModel(this OrganizationUserInvite organizationUserInvite, + public static CreateOrganizationUser MapToDataModel(this OrganizationUserInviteCommandModel organizationUserInvite, DateTimeOffset performedAt, InviteOrganization organization) => new() diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/InviteOrganizationUsersRequest.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/InviteOrganizationUsersRequest.cs index 84b350c551..2a54f26eb8 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/InviteOrganizationUsersRequest.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/InviteOrganizationUsersRequest.cs @@ -4,12 +4,12 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUse public class InviteOrganizationUsersRequest { - public OrganizationUserInvite[] Invites { get; } = []; + public OrganizationUserInviteCommandModel[] Invites { get; } = []; public InviteOrganization InviteOrganization { get; } public Guid PerformedBy { get; } public DateTimeOffset PerformedAt { get; } - public InviteOrganizationUsersRequest(OrganizationUserInvite[] invites, + public InviteOrganizationUsersRequest(OrganizationUserInviteCommandModel[] invites, InviteOrganization inviteOrganization, Guid performedBy, DateTimeOffset performedAt) diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/InviteOrganizationUsersValidationRequest.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/InviteOrganizationUsersValidationRequest.cs index f45c705cab..479e2d75ac 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/InviteOrganizationUsersValidationRequest.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/InviteOrganizationUsersValidationRequest.cs @@ -29,7 +29,7 @@ public class InviteOrganizationUsersValidationRequest SecretsManagerSubscriptionUpdate = smSubscriptionUpdate; } - public OrganizationUserInvite[] Invites { get; init; } = []; + public OrganizationUserInviteCommandModel[] Invites { get; init; } = []; public InviteOrganization InviteOrganization { get; init; } public Guid PerformedBy { get; init; } public DateTimeOffset PerformedAt { get; init; } diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/OrganizationUserInvite.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/OrganizationUserInviteCommandModel.cs similarity index 88% rename from src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/OrganizationUserInvite.cs rename to src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/OrganizationUserInviteCommandModel.cs index 0b83680aa5..4d0f56efe4 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/OrganizationUserInvite.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Models/OrganizationUserInviteCommandModel.cs @@ -7,7 +7,7 @@ using static Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Invite namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; -public class OrganizationUserInvite +public class OrganizationUserInviteCommandModel { public string Email { get; private init; } public CollectionAccessSelection[] AssignedCollections { get; private init; } @@ -17,7 +17,7 @@ public class OrganizationUserInvite public bool AccessSecretsManager { get; private init; } public Guid[] Groups { get; private init; } - public OrganizationUserInvite(string email, string externalId) : + public OrganizationUserInviteCommandModel(string email, string externalId) : this( email: email, assignedCollections: [], @@ -29,7 +29,7 @@ public class OrganizationUserInvite { } - public OrganizationUserInvite(OrganizationUserInvite invite, bool accessSecretsManager) : + public OrganizationUserInviteCommandModel(OrganizationUserInviteCommandModel invite, bool accessSecretsManager) : this(invite.Email, invite.AssignedCollections, invite.Groups, @@ -41,7 +41,7 @@ public class OrganizationUserInvite } - public OrganizationUserInvite(string email, + public OrganizationUserInviteCommandModel(string email, IEnumerable assignedCollections, IEnumerable groups, OrganizationUserType type, diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Validation/InviteOrganizationUserValidator.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Validation/InviteOrganizationUserValidator.cs index 79a3487d19..9ea0cbf33d 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Validation/InviteOrganizationUserValidator.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/Validation/InviteOrganizationUserValidator.cs @@ -6,7 +6,6 @@ using Bit.Core.Models.Business; using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; using Bit.Core.Repositories; using Bit.Core.Services; -using OrganizationUserInvite = Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.OrganizationUserInvite; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation; @@ -38,7 +37,7 @@ public class InviteOrganizationUsersValidator( request = new InviteOrganizationUsersValidationRequest(request) { Invites = request.Invites - .Select(x => new OrganizationUserInvite(x, accessSecretsManager: true)) + .Select(x => new OrganizationUserInviteCommandModel(x, accessSecretsManager: true)) .ToArray() }; } diff --git a/src/Core/AdminConsole/Services/IOrganizationService.cs b/src/Core/AdminConsole/Services/IOrganizationService.cs index 1e53be734e..d10094db05 100644 --- a/src/Core/AdminConsole/Services/IOrganizationService.cs +++ b/src/Core/AdminConsole/Services/IOrganizationService.cs @@ -1,6 +1,5 @@ using System.Security.Claims; using Bit.Core.AdminConsole.Entities; -using Bit.Core.AdminConsole.Models.Business; using Bit.Core.Auth.Enums; using Bit.Core.Entities; using Bit.Core.Enums; @@ -39,9 +38,6 @@ public interface IOrganizationService Task>> ResendInvitesAsync(Guid organizationId, Guid? invitingUserId, IEnumerable organizationUsersId); Task ResendInviteAsync(Guid organizationId, Guid? invitingUserId, Guid organizationUserId, bool initOrganization = false); Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId); - Task ImportAsync(Guid organizationId, IEnumerable groups, - IEnumerable newUsers, IEnumerable removeUserExternalIds, - bool overwriteExisting, EventSystemUser eventSystemUser); Task DeleteSsoUserAsync(Guid userId, Guid? organizationId); Task RevokeUserAsync(OrganizationUser organizationUser, Guid? revokingUserId); Task RevokeUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser); diff --git a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs index 41c45ef7b9..875f7ce29c 100644 --- a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs +++ b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs @@ -3,7 +3,6 @@ using System.Text.Json; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums.Provider; -using Bit.Core.AdminConsole.Models.Business; using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; @@ -26,7 +25,6 @@ using Bit.Core.Exceptions; using Bit.Core.Models.Business; using Bit.Core.Models.Data; using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; -using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.Platform.Push; using Bit.Core.Repositories; using Bit.Core.Settings; @@ -74,7 +72,6 @@ public class OrganizationService : IOrganizationService private readonly IPricingClient _pricingClient; private readonly IPolicyRequirementQuery _policyRequirementQuery; private readonly ISendOrganizationInvitesCommand _sendOrganizationInvitesCommand; - private readonly IImportOrganizationUserCommand _importOrganizationUserCommand; public OrganizationService( IOrganizationRepository organizationRepository, @@ -108,8 +105,7 @@ public class OrganizationService : IOrganizationService IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery, IPricingClient pricingClient, IPolicyRequirementQuery policyRequirementQuery, - ISendOrganizationInvitesCommand sendOrganizationInvitesCommand, - IImportOrganizationUserCommand importOrganizationUserCommand + ISendOrganizationInvitesCommand sendOrganizationInvitesCommand ) { _organizationRepository = organizationRepository; @@ -144,7 +140,6 @@ public class OrganizationService : IOrganizationService _pricingClient = pricingClient; _policyRequirementQuery = policyRequirementQuery; _sendOrganizationInvitesCommand = sendOrganizationInvitesCommand; - _importOrganizationUserCommand = importOrganizationUserCommand; } public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken, @@ -1203,23 +1198,6 @@ public class OrganizationService : IOrganizationService EventType.OrganizationUser_ResetPassword_Enroll : EventType.OrganizationUser_ResetPassword_Withdraw); } - public async Task ImportAsync(Guid organizationId, - IEnumerable groups, - IEnumerable newUsers, - IEnumerable removeUserExternalIds, - bool overwriteExisting, - EventSystemUser eventSystemUser - ) - { - // @TODO DEVELOPMENT FLAG FOR TESTING ---- REVERT THIS LATER - await _importOrganizationUserCommand.ImportAsync(organizationId, - groups, - newUsers, - removeUserExternalIds, - overwriteExisting, - eventSystemUser); - } - public async Task DeleteSsoUserAsync(Guid userId, Guid? organizationId) { await _ssoUserRepository.DeleteAsync(userId, organizationId);