mirror of
https://github.com/bitwarden/server
synced 2025-12-30 23:23:37 +00:00
[PM-19145] refactor organization service.import async (#5800)
* initial lift and shift * extract function RemoveExistingExternalUsers * Extract function RemoveExistingUsers() * extract function OverwriteExisting() * create new model for sync data * extract add users to function, rename * rename OrganizatinUserInvite for command, implement command * implement command * refactor groups logic * fix imports * remove old tests, fix imports * fix namespace * fix CommandResult useage * tests wip * wip * wip * remove redundant code, remove looping db call, refactor tests * clean up * remove looping db call with bulk method * clean up * remove orgId param to use id already in request * change param * cleanup params * remove IReferenceEventService * fix test * fix tests * cr feedback * remove _timeProvider * add xmldoc, refactor to make InviteOrganizationUsersCommand vNext instead of default * switch back to command * re-add old ImportAsync impl * fix test * add feature flag * cleanup * clean up * fix tests * wip * wip * add api integration tests for users WIP * groups integration tests * cleanup * fix error from merging main * fix tests * cr feedback * fix test * fix test
This commit is contained in:
@@ -19,4 +19,14 @@ public interface IInviteOrganizationUsersCommand
|
||||
/// </param>
|
||||
/// <returns>Response from InviteScimOrganiation<see cref="ScimInviteOrganizationUsersResponse"/></returns>
|
||||
Task<CommandResult<ScimInviteOrganizationUsersResponse>> InviteScimOrganizationUserAsync(InviteOrganizationUsersRequest request);
|
||||
/// <summary>
|
||||
/// Sends invitations to add imported organization users via the public API.
|
||||
/// This can be a Success or a Failure. Failure will contain the Error along with a representation of the errored value.
|
||||
/// Success will be the successful return object.
|
||||
/// </summary>
|
||||
/// <param name="request">
|
||||
/// Contains the details for inviting the imported organization users.
|
||||
/// </param>
|
||||
/// <returns>Response from InviteOrganiationUsersAsync<see cref="InviteOrganizationUsersResponse"/></returns>
|
||||
Task<CommandResult<InviteOrganizationUsersResponse>> InviteImportedOrganizationUsersAsync(InviteOrganizationUsersRequest request);
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@ using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OrganizationUserInvite = Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.OrganizationUserInvite;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||
|
||||
@@ -74,6 +74,40 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<CommandResult<InviteOrganizationUsersResponse>> InviteImportedOrganizationUsersAsync(InviteOrganizationUsersRequest request)
|
||||
{
|
||||
var result = await InviteOrganizationUsersAsync(request);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case Failure<InviteOrganizationUsersResponse> failure:
|
||||
return new Failure<InviteOrganizationUsersResponse>(
|
||||
new Error<InviteOrganizationUsersResponse>(
|
||||
failure.Error.Message,
|
||||
new InviteOrganizationUsersResponse(failure.Error.ErroredValue.InvitedUsers, request.InviteOrganization.OrganizationId)
|
||||
)
|
||||
);
|
||||
|
||||
case Success<InviteOrganizationUsersResponse> success when success.Value.InvitedUsers.Any():
|
||||
|
||||
List<(OrganizationUser, EventType, EventSystemUser, DateTime?)> events = new List<(OrganizationUser, EventType, EventSystemUser, DateTime?)>();
|
||||
foreach (var user in success.Value.InvitedUsers)
|
||||
{
|
||||
events.Add((user, EventType.OrganizationUser_Invited, EventSystemUser.PublicApi, request.PerformedAt.UtcDateTime));
|
||||
}
|
||||
|
||||
await eventService.LogOrganizationUserEventsAsync(events);
|
||||
|
||||
return new Success<InviteOrganizationUsersResponse>(new InviteOrganizationUsersResponse(success.Value.InvitedUsers, request.InviteOrganization.OrganizationId)
|
||||
);
|
||||
|
||||
default:
|
||||
return new Failure<InviteOrganizationUsersResponse>(
|
||||
new InvalidResultTypeError<InviteOrganizationUsersResponse>(
|
||||
new InviteOrganizationUsersResponse(request.InviteOrganization.OrganizationId)));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<CommandResult<InviteOrganizationUsersResponse>> InviteOrganizationUsersAsync(InviteOrganizationUsersRequest request)
|
||||
{
|
||||
var invitesToSend = (await FilterExistingUsersAsync(request)).ToArray();
|
||||
@@ -141,7 +175,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
|
||||
organizationId: organization!.Id));
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<OrganizationUserInvite>> FilterExistingUsersAsync(InviteOrganizationUsersRequest request)
|
||||
private async Task<IEnumerable<OrganizationUserInviteCommandModel>> FilterExistingUsersAsync(InviteOrganizationUsersRequest request)
|
||||
{
|
||||
var existingEmails = new HashSet<string>(await organizationUserRepository.SelectKnownEmailsAsync(
|
||||
request.InviteOrganization.OrganizationId, request.Invites.Select(i => i.Email), false),
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -32,7 +32,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; }
|
||||
|
||||
@@ -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<CollectionAccessSelection> assignedCollections,
|
||||
IEnumerable<Guid> groups,
|
||||
OrganizationUserType type,
|
||||
@@ -9,7 +9,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;
|
||||
|
||||
@@ -41,7 +40,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()
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user