mirror of
https://github.com/bitwarden/server.git
synced 2025-06-27 22:26:13 -05:00
wip
This commit is contained in:
parent
a5055fe120
commit
47cc09963e
@ -4,7 +4,6 @@ using Bit.Api.Models.Public.Response;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
@ -55,26 +54,25 @@ public class OrganizationController : Controller
|
||||
throw new BadRequestException("You cannot import this much data at once.");
|
||||
}
|
||||
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.ScimInviteUserOptimization))
|
||||
{
|
||||
await _importOrganizationUsersAndGroupsCommand.ImportAsync(
|
||||
string r = "";
|
||||
|
||||
r = await _importOrganizationUsersAndGroupsCommand.ImportAsync(
|
||||
_currentContext.OrganizationId.Value,
|
||||
model.Groups.Select(g => g.ToImportedGroup(_currentContext.OrganizationId.Value)),
|
||||
model.Members.Where(u => !u.Deleted).Select(u => u.ToImportedOrganizationUser()),
|
||||
model.Members.Where(u => u.Deleted).Select(u => u.ExternalId),
|
||||
model.OverwriteExisting.GetValueOrDefault());
|
||||
|
||||
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.ImportAsyncRefactor))
|
||||
{
|
||||
r = "success";
|
||||
}
|
||||
else
|
||||
{
|
||||
await _organizationService.ImportAsync(
|
||||
_currentContext.OrganizationId.Value,
|
||||
model.Groups.Select(g => g.ToImportedGroup(_currentContext.OrganizationId.Value)),
|
||||
model.Members.Where(u => !u.Deleted).Select(u => u.ToImportedOrganizationUser()),
|
||||
model.Members.Where(u => u.Deleted).Select(u => u.ExternalId),
|
||||
model.OverwriteExisting.GetValueOrDefault(),
|
||||
EventSystemUser.PublicApi
|
||||
);
|
||||
r = "fail";
|
||||
}
|
||||
return new OkResult();
|
||||
|
||||
return new OkObjectResult(r);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interface
|
||||
|
||||
public interface IImportOrganizationUsersAndGroupsCommand
|
||||
{
|
||||
Task ImportAsync(Guid organizationId,
|
||||
Task<string> ImportAsync(Guid organizationId,
|
||||
IEnumerable<ImportedGroup> groups,
|
||||
IEnumerable<ImportedOrganizationUser> newUsers,
|
||||
IEnumerable<string> removeUserExternalIds,
|
||||
|
@ -4,15 +4,14 @@ using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||
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.Data.Organizations;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
using Bit.Core.Repositories;
|
||||
@ -67,7 +66,7 @@ public class ImportOrganizationUsersAndGroupsCommand : IImportOrganizationUsersA
|
||||
/// who are not included in the current import.</param>
|
||||
/// <exception cref="NotFoundException">Thrown if the organization does not exist.</exception>
|
||||
/// <exception cref="BadRequestException">Thrown if the organization is not configured to use directory syncing.</exception>
|
||||
public async Task ImportAsync(Guid organizationId,
|
||||
public async Task<string> ImportAsync(Guid organizationId,
|
||||
IEnumerable<ImportedGroup> importedGroups,
|
||||
IEnumerable<ImportedOrganizationUser> importedUsers,
|
||||
IEnumerable<string> removeUserExternalIds,
|
||||
@ -102,6 +101,8 @@ public class ImportOrganizationUsersAndGroupsCommand : IImportOrganizationUsersA
|
||||
await ImportGroups(organization, importedGroups, importUserData);
|
||||
|
||||
await _eventService.LogOrganizationUserEventsAsync(events.Select(e => (e.ou, e.e, _EventSystemUser, e.d)));
|
||||
|
||||
return "success";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -196,73 +197,52 @@ public class ImportOrganizationUsersAndGroupsCommand : IImportOrganizationUsersA
|
||||
IEnumerable<ImportedOrganizationUser> importedUsers,
|
||||
OrganizationUserImportData importUserData)
|
||||
{
|
||||
var userInvites = new List<OrganizationUserInviteCommandModel>();
|
||||
// Determine which users are already in the organization
|
||||
var existingUsersSet = new HashSet<string>(importUserData.ExistingExternalUsersIdDict.Keys).ToList();
|
||||
var usersToAdd = importUserData.ImportedExternalIds.Except(existingUsersSet).ToList();
|
||||
|
||||
var seatsAvailable = int.MaxValue;
|
||||
var enoughSeatsAvailable = true;
|
||||
|
||||
if (organization.Seats.HasValue)
|
||||
{
|
||||
seatsAvailable = organization.Seats.Value - await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
|
||||
enoughSeatsAvailable = seatsAvailable >= usersToAdd.Count();
|
||||
}
|
||||
|
||||
|
||||
var userInvites = new List<(OrganizationUserInvite, string)>();
|
||||
var hasStandaloneSecretsManager = await _paymentService.HasSecretsManagerStandalone(organization);
|
||||
|
||||
foreach (var user in importedUsers)
|
||||
{
|
||||
userInvites.Add(new OrganizationUserInviteCommandModel(user.Email, user.ExternalId));
|
||||
}
|
||||
|
||||
var commandResult = await InviteUsersAsync(userInvites, organization);
|
||||
|
||||
switch (commandResult)
|
||||
{
|
||||
case Success<InviteOrganizationUsersResponse> result:
|
||||
foreach (var u in result.Value.InvitedUsers)
|
||||
{
|
||||
importUserData.ExistingExternalUsersIdDict.Add(u.ExternalId!, u.Id);
|
||||
}
|
||||
break;
|
||||
case Failure<InviteOrganizationUsersResponse> failure:
|
||||
throw new BadRequestException(failure.Error.Message);
|
||||
default:
|
||||
throw new InvalidOperationException($"Unhandled commandResult type: {commandResult.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the user invites through the InviteOrganizationUserCommand
|
||||
/// <param name="invites">The list of organization user invites command models to be used for inviting users</param>
|
||||
/// <param name="organization">The organization to which users are being invited</param>
|
||||
/// </summary>
|
||||
private async Task<CommandResult<InviteOrganizationUsersResponse>> InviteUsersAsync(List<OrganizationUserInviteCommandModel> 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.InviteImportedOrganizationUsersAsync(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an InviteOrganizationUsersRequest for the provided invites and sends the request via the InviteOrganizationUsersCommand.
|
||||
/// </summary>
|
||||
private async Task vNextInviteUsersAsync(List<OrganizationUserInviteCommandModel> userInvites,
|
||||
Organization organization,
|
||||
OrganizationUserImportData importUserData)
|
||||
{
|
||||
var plan = await _pricingClient.GetPlanOrThrow(organization.PlanType);
|
||||
var inviteOrganization = new InviteOrganization(organization, plan);
|
||||
var request = new InviteOrganizationUsersRequest(userInvites.ToArray(), inviteOrganization, Guid.Empty, DateTimeOffset.UtcNow);
|
||||
var commandResult = await _inviteOrganizationUsersCommand.InviteImportedOrganizationUsersAsync(request);
|
||||
|
||||
switch (commandResult)
|
||||
{
|
||||
case Success<InviteOrganizationUsersResponse> result:
|
||||
foreach (var u in result.Value.InvitedUsers)
|
||||
{
|
||||
if (u.ExternalId is null)
|
||||
if (!usersToAdd.Contains(user.ExternalId) || string.IsNullOrWhiteSpace(user.Email))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
importUserData.ExistingExternalUsersIdDict.Add(u.ExternalId, u.Id);
|
||||
try
|
||||
{
|
||||
var invite = new OrganizationUserInvite
|
||||
{
|
||||
Emails = new List<string> { user.Email },
|
||||
Type = OrganizationUserType.User,
|
||||
Collections = new List<CollectionAccessSelection>(),
|
||||
AccessSecretsManager = hasStandaloneSecretsManager
|
||||
};
|
||||
userInvites.Add((invite, user.ExternalId));
|
||||
}
|
||||
break;
|
||||
case Failure<InviteOrganizationUsersResponse> failure:
|
||||
throw new BadRequestException(failure.Error.Message);
|
||||
default:
|
||||
throw new InvalidOperationException($"Unhandled commandResult type: {commandResult.GetType().Name}");
|
||||
catch (BadRequestException)
|
||||
{
|
||||
// Thrown when the user is already invited to the organization
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var invitedUsers = await _organizationService.InviteUsersAsync(organization.Id, Guid.Empty, _EventSystemUser, userInvites);
|
||||
foreach (var invitedUser in invitedUsers)
|
||||
{
|
||||
importUserData.ExistingExternalUsersIdDict.TryAdd(invitedUser.ExternalId!, invitedUser.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,6 +112,7 @@ public static class FeatureFlagKeys
|
||||
public const string EventBasedOrganizationIntegrations = "event-based-organization-integrations";
|
||||
public const string OptimizeNestedTraverseTypescript = "pm-21695-optimize-nested-traverse-typescript";
|
||||
public const string SeparateCustomRolePermissions = "pm-19917-separate-custom-role-permissions";
|
||||
public const string ImportAsyncRefactor = "pm-22583-refactor-import-async";
|
||||
|
||||
/* Auth Team */
|
||||
public const string PM9112DeviceApprovalPersistence = "pm-9112-device-approval-persistence";
|
||||
|
@ -0,0 +1,72 @@
|
||||
using Bit.Api.AdminConsole.Public.Models.Request;
|
||||
using Bit.Api.IntegrationTest.Factories;
|
||||
using Bit.Api.IntegrationTest.Helpers;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Enums;
|
||||
using Xunit;
|
||||
|
||||
public class ImportOrganizationUsersAndGroupsCommandTests : IClassFixture<ApiApplicationFactory>, IAsyncLifetime
|
||||
{
|
||||
private readonly HttpClient _client;
|
||||
private readonly ApiApplicationFactory _factory;
|
||||
private readonly LoginHelper _loginHelper;
|
||||
private Organization _organization = null!;
|
||||
private string _ownerEmail = null!;
|
||||
|
||||
public ImportOrganizationUsersAndGroupsCommandTests(ApiApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
_factory.UpdateConfiguration("globalSettings:launchDarkly:flagValues:pm-22583-refactor-import-async",
|
||||
"true");
|
||||
_client = _factory.CreateClient();
|
||||
_loginHelper = new LoginHelper(_factory, _client);
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
// Create the owner account
|
||||
_ownerEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com";
|
||||
await _factory.LoginWithNewAccount(_ownerEmail);
|
||||
|
||||
// Create the organization
|
||||
(_organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory, plan: PlanType.EnterpriseAnnually2023,
|
||||
ownerEmail: _ownerEmail, passwordManagerSeats: 10, paymentMethod: PaymentMethodType.Card);
|
||||
|
||||
// Authorize with the organization api key
|
||||
await _loginHelper.LoginWithOrganizationApiKeyAsync(_organization.Id);
|
||||
}
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
_client.Dispose();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Import_Existing_User_Success()
|
||||
{
|
||||
var (email, orgUser) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(_factory, _organization.Id,
|
||||
OrganizationUserType.User);
|
||||
|
||||
var request = new OrganizationImportRequestModel();
|
||||
request.LargeImport = false;
|
||||
request.OverwriteExisting = false;
|
||||
request.Groups = [];
|
||||
request.Members = [
|
||||
new OrganizationImportRequestModel.OrganizationImportMemberRequestModel
|
||||
{
|
||||
Email = email,
|
||||
ExternalId = Guid.NewGuid().ToString(),
|
||||
Deleted = false
|
||||
}
|
||||
];
|
||||
|
||||
var response = await _client.PostAsync($"/public/organization/import", JsonContent.Create(request));
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Assert.Equal("success", result);
|
||||
//Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
|
||||
}
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -32,36 +30,46 @@ public class ImportOrganizationUsersAndGroupsCommandTests
|
||||
SutProvider<ImportOrganizationUsersAndGroupsCommand> sutProvider,
|
||||
Organization org,
|
||||
List<OrganizationUserUserDetails> existingUsers,
|
||||
List<ImportedOrganizationUser> newUsers,
|
||||
List<ImportedOrganizationUser> importedUsers,
|
||||
List<ImportedGroup> newGroups)
|
||||
{
|
||||
SetupOrganizationConfigForImport(sutProvider, org, existingUsers, newUsers);
|
||||
SetupOrganizationConfigForImport(sutProvider, org, existingUsers, importedUsers);
|
||||
|
||||
newUsers.Add(new ImportedOrganizationUser
|
||||
var orgUsers = new List<OrganizationUser>();
|
||||
|
||||
// fix mocked email format, mock OrganizationUsers.
|
||||
foreach (var u in importedUsers)
|
||||
{
|
||||
u.Email += "@bitwardentest.com";
|
||||
orgUsers.Add(new OrganizationUser { Email = u.Email, ExternalId = u.ExternalId });
|
||||
}
|
||||
|
||||
importedUsers.Add(new ImportedOrganizationUser
|
||||
{
|
||||
Email = existingUsers.First().Email,
|
||||
ExternalId = existingUsers.First().ExternalId
|
||||
});
|
||||
|
||||
foreach (var u in newUsers)
|
||||
{
|
||||
u.Email += "@bitwardentest.com";
|
||||
}
|
||||
|
||||
existingUsers.First().Type = OrganizationUserType.Owner;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(org.Id).Returns(org);
|
||||
sutProvider.GetDependency<IPaymentService>().HasSecretsManagerStandalone(org).Returns(false);
|
||||
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||
|
||||
sutProvider.GetDependency<IPaymentService>().HasSecretsManagerStandalone(org).Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyDetailsByOrganizationAsync(org.Id).Returns(existingUsers);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetCountByOrganizationIdAsync(org.Id).Returns(existingUsers.Count);
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(org.Id).Returns(true);
|
||||
sutProvider.GetDependency<IInviteOrganizationUsersCommand>().InviteImportedOrganizationUsersAsync(Arg.Any<InviteOrganizationUsersRequest>())
|
||||
.Returns(new Success<InviteOrganizationUsersResponse>(new InviteOrganizationUsersResponse(org.Id)));
|
||||
sutProvider.GetDependency<IOrganizationService>().InviteUsersAsync(org.Id, Guid.Empty, EventSystemUser.PublicApi,
|
||||
Arg.Any<IEnumerable<(OrganizationUserInvite, string)>>())
|
||||
.Returns(orgUsers);
|
||||
|
||||
await sutProvider.Sut.ImportAsync(org.Id, newGroups, newUsers, new List<string>(), false);
|
||||
await sutProvider.Sut.ImportAsync(org.Id, newGroups, importedUsers, new List<string>(), false);
|
||||
|
||||
var expectedNewUsersCount = importedUsers.Count - 1;
|
||||
|
||||
await sutProvider.GetDependency<IInviteOrganizationUsersCommand>().Received(1)
|
||||
.InviteImportedOrganizationUsersAsync(Arg.Any<InviteOrganizationUsersRequest>());
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
|
||||
.UpsertAsync(default);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
||||
@ -69,6 +77,11 @@ public class ImportOrganizationUsersAndGroupsCommandTests
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
|
||||
.CreateAsync(default);
|
||||
|
||||
// Send Invites
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).
|
||||
InviteUsersAsync(org.Id, Guid.Empty, EventSystemUser.PublicApi,
|
||||
Arg.Is<IEnumerable<(OrganizationUserInvite, string)>>(invites => invites.Count() == expectedNewUsersCount));
|
||||
|
||||
// Send events
|
||||
await sutProvider.GetDependency<IEventService>().Received(1)
|
||||
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUserUserDetails, EventType, EventSystemUser, DateTime?)>>());
|
||||
@ -79,36 +92,55 @@ public class ImportOrganizationUsersAndGroupsCommandTests
|
||||
SutProvider<ImportOrganizationUsersAndGroupsCommand> sutProvider,
|
||||
Organization org,
|
||||
List<OrganizationUserUserDetails> existingUsers,
|
||||
List<ImportedOrganizationUser> newUsers,
|
||||
List<ImportedOrganizationUser> importedUsers,
|
||||
List<ImportedGroup> newGroups)
|
||||
{
|
||||
SetupOrganizationConfigForImport(sutProvider, org, existingUsers, newUsers);
|
||||
SetupOrganizationConfigForImport(sutProvider, org, existingUsers, importedUsers);
|
||||
|
||||
var orgUsers = new List<OrganizationUser>();
|
||||
var reInvitedUser = existingUsers.First();
|
||||
reInvitedUser.ExternalId = null;
|
||||
// Existing user has no external ID. This will make the SUT call UpsertManyAsync
|
||||
reInvitedUser.ExternalId = "";
|
||||
|
||||
// Mock an existing org user for this "existing" user
|
||||
var reInvitedOrgUser = new OrganizationUser { Email = reInvitedUser.Email, Id = reInvitedUser.Id };
|
||||
|
||||
// fix email formatting, mock orgUsers to be returned
|
||||
foreach (var u in existingUsers)
|
||||
{
|
||||
u.Email += "@bitwardentest.com";
|
||||
orgUsers.Add(new OrganizationUser { Email = u.Email, ExternalId = u.ExternalId });
|
||||
}
|
||||
foreach (var u in newUsers)
|
||||
foreach (var u in importedUsers)
|
||||
{
|
||||
u.Email += "@bitwardentest.com";
|
||||
orgUsers.Add(new OrganizationUser { Email = u.Email, ExternalId = u.ExternalId });
|
||||
}
|
||||
|
||||
newUsers.Add(new ImportedOrganizationUser
|
||||
// add the existing user to be re-imported
|
||||
importedUsers.Add(new ImportedOrganizationUser
|
||||
{
|
||||
Email = reInvitedUser.Email,
|
||||
ExternalId = reInvitedUser.Email,
|
||||
});
|
||||
|
||||
var expectedNewUsersCount = importedUsers.Count - 1;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(org.Id).Returns(org);
|
||||
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(new List<OrganizationUser>([reInvitedOrgUser]));
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyDetailsByOrganizationAsync(org.Id).Returns(existingUsers);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetCountByOrganizationIdAsync(org.Id).Returns(existingUsers.Count);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(Arg.Any<IEnumerable<Guid>>()).Returns(new List<OrganizationUser> { new OrganizationUser { Id = reInvitedUser.Id } });
|
||||
sutProvider.GetDependency<IInviteOrganizationUsersCommand>().InviteImportedOrganizationUsersAsync(Arg.Any<InviteOrganizationUsersRequest>())
|
||||
.Returns(new Success<InviteOrganizationUsersResponse>(new InviteOrganizationUsersResponse(org.Id)));
|
||||
|
||||
await sutProvider.Sut.ImportAsync(org.Id, newGroups, newUsers, new List<string>(), false);
|
||||
sutProvider.GetDependency<IOrganizationService>().InviteUsersAsync(org.Id, Guid.Empty, EventSystemUser.PublicApi,
|
||||
Arg.Any<IEnumerable<(OrganizationUserInvite, string)>>())
|
||||
.Returns(orgUsers);
|
||||
|
||||
await sutProvider.Sut.ImportAsync(org.Id, newGroups, importedUsers, new List<string>(), false);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
|
||||
.UpsertAsync(default);
|
||||
@ -119,28 +151,55 @@ public class ImportOrganizationUsersAndGroupsCommandTests
|
||||
|
||||
// Upserted existing user
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
||||
.UpsertManyAsync(Arg.Is<IEnumerable<OrganizationUser>>(users => users.Count() == 1));
|
||||
.UpsertManyAsync(Arg.Is<IEnumerable<OrganizationUser>>(users => users.Count() == 1 && users.First() == reInvitedOrgUser));
|
||||
|
||||
await sutProvider.GetDependency<IInviteOrganizationUsersCommand>().Received(1)
|
||||
.InviteImportedOrganizationUsersAsync(Arg.Any<InviteOrganizationUsersRequest>());
|
||||
// Send Invites
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).
|
||||
InviteUsersAsync(org.Id, Guid.Empty, EventSystemUser.PublicApi,
|
||||
Arg.Is<IEnumerable<(OrganizationUserInvite, string)>>(invites => invites.Count() == expectedNewUsersCount));
|
||||
|
||||
// Send events
|
||||
await sutProvider.GetDependency<IEventService>().Received(1)
|
||||
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUserUserDetails, EventType, EventSystemUser, DateTime?)>>());
|
||||
|
||||
}
|
||||
|
||||
private void SetupOrganizationConfigForImport(
|
||||
SutProvider<ImportOrganizationUsersAndGroupsCommand> sutProvider,
|
||||
Organization org,
|
||||
List<OrganizationUserUserDetails> existingUsers,
|
||||
List<ImportedOrganizationUser> newUsers)
|
||||
List<ImportedOrganizationUser> importedUsers)
|
||||
{
|
||||
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
||||
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
||||
sutProvider.Create();
|
||||
|
||||
org.UseDirectory = true;
|
||||
org.Seats = newUsers.Count + existingUsers.Count + 1;
|
||||
org.Seats = importedUsers.Count + existingUsers.Count + 1;
|
||||
}
|
||||
|
||||
// Must set real guids in order for dictionary of guids to not throw aggregate exceptions
|
||||
private void SetupOrgUserRepositoryCreateManyAsyncMock(IOrganizationUserRepository organizationUserRepository)
|
||||
{
|
||||
organizationUserRepository.CreateManyAsync(Arg.Any<IEnumerable<OrganizationUser>>()).Returns(
|
||||
info =>
|
||||
{
|
||||
var orgUsers = info.Arg<IEnumerable<OrganizationUser>>();
|
||||
foreach (var orgUser in orgUsers)
|
||||
{
|
||||
orgUser.Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
return Task.FromResult<ICollection<Guid>>(orgUsers.Select(u => u.Id).ToList());
|
||||
}
|
||||
);
|
||||
|
||||
organizationUserRepository.CreateAsync(Arg.Any<OrganizationUser>(), Arg.Any<IEnumerable<CollectionAccessSelection>>()).Returns(
|
||||
info =>
|
||||
{
|
||||
var orgUser = info.Arg<OrganizationUser>();
|
||||
orgUser.Id = Guid.NewGuid();
|
||||
return Task.FromResult<Guid>(orgUser.Id);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user