using System.Net; using Bit.Api.AdminConsole.Public.Models.Request; using Bit.Api.IntegrationTest.Factories; using Bit.Api.IntegrationTest.Helpers; using Bit.Core; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Billing.Enums; using Bit.Core.Enums; using Bit.Core.Repositories; using Bit.Core.Services; using NSubstitute; using Xunit; namespace Bit.Api.IntegrationTest.AdminConsole.Import; public class ImportOrganizationUsersAndGroupsCommandTests : IClassFixture, 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.SubstituteService((IFeatureService featureService) => featureService.IsEnabled(FeatureFlagKeys.ImportAsyncRefactor) .Returns(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_Organization_User_Succeeds() { var (email, ou) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(_factory, _organization.Id, OrganizationUserType.User); var externalId = Guid.NewGuid().ToString(); var request = new OrganizationImportRequestModel(); request.LargeImport = false; request.OverwriteExisting = false; request.Groups = []; request.Members = [ new OrganizationImportRequestModel.OrganizationImportMemberRequestModel { Email = email, ExternalId = externalId, Deleted = false } ]; var response = await _client.PostAsync($"/public/organization/import", JsonContent.Create(request)); Assert.Equal(HttpStatusCode.OK, response.StatusCode); // Assert against the database values var organizationUserRepository = _factory.GetService(); var orgUser = await organizationUserRepository.GetByIdAsync(ou.Id); Assert.NotNull(orgUser); Assert.Equal(ou.Id, orgUser.Id); Assert.Equal(email, orgUser.Email); Assert.Equal(OrganizationUserType.User, orgUser.Type); Assert.Equal(externalId, orgUser.ExternalId); Assert.Equal(OrganizationUserStatusType.Confirmed, orgUser.Status); Assert.Equal(_organization.Id, orgUser.OrganizationId); } [Fact] public async Task Import_New_Organization_User_Succeeds() { var email = $"integration-test{Guid.NewGuid()}@bitwarden.com"; await _factory.LoginWithNewAccount(email); var externalId = Guid.NewGuid().ToString(); var request = new OrganizationImportRequestModel(); request.LargeImport = false; request.OverwriteExisting = false; request.Groups = []; request.Members = [ new OrganizationImportRequestModel.OrganizationImportMemberRequestModel { Email = email, ExternalId = externalId, Deleted = false } ]; var response = await _client.PostAsync($"/public/organization/import", JsonContent.Create(request)); Assert.Equal(HttpStatusCode.OK, response.StatusCode); // Assert against the database values var organizationUserRepository = _factory.GetService(); var orgUser = await organizationUserRepository.GetByOrganizationEmailAsync(_organization.Id, email); Assert.NotNull(orgUser); Assert.Equal(email, orgUser.Email); Assert.Equal(OrganizationUserType.User, orgUser.Type); Assert.Equal(externalId, orgUser.ExternalId); Assert.Equal(OrganizationUserStatusType.Invited, orgUser.Status); Assert.Equal(_organization.Id, orgUser.OrganizationId); } [Fact] public async Task Import_New_And_Existing_Organization_Users_Succeeds() { // Existing organization user var (existingEmail, ou) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(_factory, _organization.Id, OrganizationUserType.User); var existingExternalId = Guid.NewGuid().ToString(); // New organization user var newEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com"; await _factory.LoginWithNewAccount(newEmail); var newExternalId = Guid.NewGuid().ToString(); var request = new OrganizationImportRequestModel(); request.LargeImport = false; request.OverwriteExisting = false; request.Groups = []; request.Members = [ new OrganizationImportRequestModel.OrganizationImportMemberRequestModel { Email = existingEmail, ExternalId = existingExternalId, Deleted = false }, new OrganizationImportRequestModel.OrganizationImportMemberRequestModel { Email = newEmail, ExternalId = newExternalId, Deleted = false } ]; var response = await _client.PostAsync($"/public/organization/import", JsonContent.Create(request)); Assert.Equal(HttpStatusCode.OK, response.StatusCode); // Assert against the database values var organizationUserRepository = _factory.GetService(); // Existing user var existingOrgUser = await organizationUserRepository.GetByIdAsync(ou.Id); Assert.NotNull(existingOrgUser); Assert.Equal(existingEmail, existingOrgUser.Email); Assert.Equal(OrganizationUserType.User, existingOrgUser.Type); Assert.Equal(existingExternalId, existingOrgUser.ExternalId); Assert.Equal(OrganizationUserStatusType.Confirmed, existingOrgUser.Status); Assert.Equal(_organization.Id, existingOrgUser.OrganizationId); // New User var newOrgUser = await organizationUserRepository.GetByOrganizationEmailAsync(_organization.Id, newEmail); Assert.NotNull(newOrgUser); Assert.Equal(newEmail, newOrgUser.Email); Assert.Equal(OrganizationUserType.User, newOrgUser.Type); Assert.Equal(newExternalId, newOrgUser.ExternalId); Assert.Equal(OrganizationUserStatusType.Invited, newOrgUser.Status); Assert.Equal(_organization.Id, newOrgUser.OrganizationId); } [Fact] public async Task Import_Existing_Groups_Succeeds() { var group = await OrganizationTestHelpers.CreateGroup(_factory, _organization.Id); var request = new OrganizationImportRequestModel(); request.LargeImport = false; request.OverwriteExisting = false; request.Groups = [ new OrganizationImportRequestModel.OrganizationImportGroupRequestModel { Name = group.Name, ExternalId = Guid.NewGuid().ToString(), MemberExternalIds = [] } ]; request.Members = []; var response = await _client.PostAsync($"/public/organization/import", JsonContent.Create(request)); Assert.Equal(HttpStatusCode.OK, response.StatusCode); // Assert against the database values var groupRepository = _factory.GetService(); var existingGroup = await groupRepository.GetByIdAsync(group.Id); Assert.NotNull(existingGroup); Assert.Equal(existingGroup.Id, group.Id); Assert.Equal(existingGroup.Name, group.Name); Assert.Equal(existingGroup.ExternalId, group.ExternalId); } [Fact] public async Task Import_New_Groups_Succeeds() { var group = new Group { OrganizationId = _organization.Id, ExternalId = new Guid().ToString(), Name = "bwtest1" }; var request = new OrganizationImportRequestModel(); request.LargeImport = false; request.OverwriteExisting = false; request.Groups = [ new OrganizationImportRequestModel.OrganizationImportGroupRequestModel { Name = group.Name, ExternalId = group.ExternalId, MemberExternalIds = [] } ]; request.Members = []; var response = await _client.PostAsync($"/public/organization/import", JsonContent.Create(request)); Assert.Equal(HttpStatusCode.OK, response.StatusCode); // Assert against the database values var groupRepository = _factory.GetService(); var existingGroups = await groupRepository.GetManyByOrganizationIdAsync(_organization.Id); var existingGroup = existingGroups.Where(g => g.ExternalId == group.ExternalId).FirstOrDefault(); Assert.NotNull(existingGroup); Assert.Equal(existingGroup.Name, group.Name); Assert.Equal(existingGroup.ExternalId, group.ExternalId); } [Fact] public async Task Import_New_And_Existing_Groups_Succeeds() { var existingGroup = await OrganizationTestHelpers.CreateGroup(_factory, _organization.Id); var newGroup = new Group { OrganizationId = _organization.Id, ExternalId = "test", Name = "bwtest1" }; var request = new OrganizationImportRequestModel(); request.LargeImport = false; request.OverwriteExisting = false; request.Groups = [ new OrganizationImportRequestModel.OrganizationImportGroupRequestModel { Name = existingGroup.Name, ExternalId = Guid.NewGuid().ToString(), MemberExternalIds = [] }, new OrganizationImportRequestModel.OrganizationImportGroupRequestModel { Name = newGroup.Name, ExternalId = newGroup.ExternalId, MemberExternalIds = [] } ]; request.Members = []; var response = await _client.PostAsync($"/public/organization/import", JsonContent.Create(request)); Assert.Equal(HttpStatusCode.OK, response.StatusCode); // Assert against the database values var groupRepository = _factory.GetService(); var groups = await groupRepository.GetManyByOrganizationIdAsync(_organization.Id); var newGroupInDb = groups.Where(g => g.ExternalId == newGroup.ExternalId).FirstOrDefault(); Assert.NotNull(newGroupInDb); Assert.Equal(newGroupInDb.Name, newGroup.Name); Assert.Equal(newGroupInDb.ExternalId, newGroup.ExternalId); var existingGroupInDb = groups.Where(g => g.ExternalId == existingGroup.ExternalId).FirstOrDefault(); Assert.NotNull(existingGroupInDb); Assert.Equal(existingGroupInDb.Id, existingGroup.Id); Assert.Equal(existingGroupInDb.Name, existingGroup.Name); Assert.Equal(existingGroupInDb.ExternalId, existingGroup.ExternalId); } }