-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PM-16777] Fix exception when bulk restoring revoked users who never …
…accepted invitations (#5224) * Fix null handling for UserId in Two Factor Authentication checks * Add tests for restoring users with and without 2FA policies
- Loading branch information
Showing
2 changed files
with
107 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2180,4 +2180,107 @@ private void SetupOrgUserRepositoryCreateAsyncMock(IOrganizationUserRepository o | |
} | ||
); | ||
} | ||
|
||
[Theory, BitAutoData] | ||
public async Task RestoreUsers_Success(Organization organization, | ||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, | ||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser1, | ||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser2, | ||
SutProvider<OrganizationService> sutProvider) | ||
{ | ||
// Arrange | ||
RestoreRevokeUser_Setup(organization, owner, orgUser1, sutProvider); | ||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>(); | ||
var eventService = sutProvider.GetDependency<IEventService>(); | ||
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>(); | ||
var userService = Substitute.For<IUserService>(); | ||
|
||
orgUser1.Email = orgUser2.Email = null; // Mock that users were previously confirmed | ||
orgUser1.OrganizationId = orgUser2.OrganizationId = organization.Id; | ||
organizationUserRepository | ||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser1.Id) && ids.Contains(orgUser2.Id))) | ||
.Returns(new[] { orgUser1, orgUser2 }); | ||
|
||
twoFactorIsEnabledQuery | ||
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser1.UserId!.Value) && ids.Contains(orgUser2.UserId!.Value))) | ||
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)> | ||
{ | ||
(orgUser1.UserId!.Value, true), | ||
(orgUser2.UserId!.Value, false) | ||
}); | ||
|
||
// Act | ||
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, new[] { orgUser1.Id, orgUser2.Id }, owner.Id, userService); | ||
|
||
// Assert | ||
Assert.Equal(2, result.Count); | ||
Assert.All(result, r => Assert.Empty(r.Item2)); // No error messages | ||
await organizationUserRepository | ||
.Received(1) | ||
.RestoreAsync(orgUser1.Id, OrganizationUserStatusType.Confirmed); | ||
await organizationUserRepository | ||
.Received(1) | ||
.RestoreAsync(orgUser2.Id, OrganizationUserStatusType.Confirmed); | ||
await eventService.Received(1) | ||
.LogOrganizationUserEventAsync(orgUser1, EventType.OrganizationUser_Restored); | ||
await eventService.Received(1) | ||
.LogOrganizationUserEventAsync(orgUser2, EventType.OrganizationUser_Restored); | ||
} | ||
|
||
[Theory, BitAutoData] | ||
public async Task RestoreUsers_With2FAPolicy_BlocksNonCompliantUser(Organization organization, | ||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, | ||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser1, | ||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser2, | ||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser3, | ||
SutProvider<OrganizationService> sutProvider) | ||
{ | ||
// Arrange | ||
RestoreRevokeUser_Setup(organization, owner, orgUser1, sutProvider); | ||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>(); | ||
var userRepository = sutProvider.GetDependency<IUserRepository>(); | ||
var policyService = sutProvider.GetDependency<IPolicyService>(); | ||
var userService = Substitute.For<IUserService>(); | ||
|
||
orgUser1.Email = orgUser2.Email = null; | ||
orgUser3.UserId = null; | ||
orgUser3.Key = null; | ||
orgUser1.OrganizationId = orgUser2.OrganizationId = orgUser3.OrganizationId = organization.Id; | ||
organizationUserRepository | ||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser1.Id) && ids.Contains(orgUser2.Id) && ids.Contains(orgUser3.Id))) | ||
.Returns(new[] { orgUser1, orgUser2, orgUser3 }); | ||
|
||
userRepository.GetByIdAsync(orgUser2.UserId!.Value).Returns(new User { Email = "[email protected]" }); | ||
|
||
// Setup 2FA policy | ||
policyService.GetPoliciesApplicableToUserAsync(Arg.Any<Guid>(), PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>()) | ||
.Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organization.Id, PolicyType = PolicyType.TwoFactorAuthentication } }); | ||
|
||
// User1 has 2FA, User2 doesn't | ||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>() | ||
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser1.UserId!.Value) && ids.Contains(orgUser2.UserId!.Value))) | ||
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)> | ||
{ | ||
(orgUser1.UserId!.Value, true), | ||
(orgUser2.UserId!.Value, false) | ||
}); | ||
|
||
// Act | ||
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, new[] { orgUser1.Id, orgUser2.Id, orgUser3.Id }, owner.Id, userService); | ||
|
||
// Assert | ||
Assert.Equal(3, result.Count); | ||
Assert.Empty(result[0].Item2); // First user should succeed | ||
Assert.Contains("two-step login", result[1].Item2); // Second user should fail | ||
Assert.Empty(result[2].Item2); // Third user should succeed | ||
await organizationUserRepository | ||
.Received(1) | ||
.RestoreAsync(orgUser1.Id, OrganizationUserStatusType.Confirmed); | ||
await organizationUserRepository | ||
.DidNotReceive() | ||
.RestoreAsync(orgUser2.Id, Arg.Any<OrganizationUserStatusType>()); | ||
await organizationUserRepository | ||
.Received(1) | ||
.RestoreAsync(orgUser3.Id, OrganizationUserStatusType.Invited); | ||
} | ||
} |