1
0
mirror of https://github.com/bitwarden/server synced 2026-01-08 11:33:26 +00:00

[PM-20140] Prevent accidental bulk removal of users without a Master Password (#6173)

This commit is contained in:
Thomas Rittson
2025-08-12 10:21:29 +10:00
committed by GitHub
parent 3c5de319d1
commit 9022ad2360
6 changed files with 118 additions and 31 deletions

View File

@@ -1,9 +1,9 @@
using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
using Bit.Core.AdminConsole.OrganizationFeatures.Import;
using Bit.Core.Auth.Models.Business.Tokenables;
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.OrganizationUsers;
@@ -18,11 +18,10 @@ using NSubstitute;
using Xunit;
using Organization = Bit.Core.AdminConsole.Entities.Organization;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationUsers;
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Import;
public class ImportOrganizationUsersAndGroupsCommandTests
{
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory = new FakeDataProtectorTokenFactory<OrgUserInviteTokenable>();
[Theory, PaidOrganizationCustomize, BitAutoData]
@@ -66,7 +65,6 @@ public class ImportOrganizationUsersAndGroupsCommandTests
Users = existingUsers.Count,
Sponsored = 0
});
sutProvider.GetDependency<ICurrentContext>().ManageUsers(org.Id).Returns(true);
sutProvider.GetDependency<IOrganizationService>().InviteUsersAsync(org.Id, Guid.Empty, EventSystemUser.PublicApi,
Arg.Any<IEnumerable<(OrganizationUserInvite, string)>>())
.Returns(orgUsers);
@@ -92,6 +90,38 @@ public class ImportOrganizationUsersAndGroupsCommandTests
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUserUserDetails, EventType, EventSystemUser, DateTime?)>>());
}
[Theory, PaidOrganizationCustomize, BitAutoData]
public async Task OverwriteExistingUsers_WhenRemovingUserWithoutMasterPassword_Throws(
SutProvider<ImportOrganizationUsersAndGroupsCommand> sutProvider,
Organization org, List<OrganizationUserUserDetails> existingUsers)
{
SetupOrganizationConfigForImport(sutProvider, org, existingUsers, []);
// Existing user does not have a master password
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.DirectoryConnectorPreventUserRemoval)
.Returns(true);
existingUsers.First().HasMasterPassword = false;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(org.Id).Returns(org);
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyDetailsByOrganizationAsync(org.Id).Returns(existingUsers);
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.ImportAsync(org.Id, [], [], [], true));
Assert.Contains("Sync failed. To proceed, disable the 'Remove and re-add users during next sync' setting and try again.", exception.Message);
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
.CreateAsync(default);
await sutProvider.GetDependency<IOrganizationService>().DidNotReceiveWithAnyArgs()
.InviteUsersAsync(default, default, default, default);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs()
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUserUserDetails, EventType, EventSystemUser, DateTime?)>>());
}
[Theory, PaidOrganizationCustomize, BitAutoData]
public async Task OrgImportCreateNewUsersAndMarryExistingUser(
SutProvider<ImportOrganizationUsersAndGroupsCommand> sutProvider,