Browse Source
* Add IResendOrganizationInviteCommand and ResendOrganizationInviteCommand implementation * Add unit tests for ResendOrganizationInviteCommand to validate invite resend functionality * Refactor Organizations, OrganizationUsers, and Members controllers to use IResendInviteCommand for invite resending functionality * Fix Organizations, OrganizationUsers, and Members controllers to replace IResendInviteCommand with IResendOrganizationInviteCommand * Remove ResendInviteAsync method from IOrganizationService and its implementation in OrganizationService to streamline invite management functionality. * Add IResendOrganizationInviteCommand registration in OrganizationServiceCollectionExtensionspull/6193/head
9 changed files with 226 additions and 25 deletions
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; |
||||
|
||||
public interface IResendOrganizationInviteCommand |
||||
{ |
||||
/// <summary> |
||||
/// Resend an invite to an organization user. |
||||
/// </summary> |
||||
/// <param name="organizationId">The ID of the organization.</param> |
||||
/// <param name="invitingUserId">The ID of the user who is inviting the organization user.</param> |
||||
/// <param name="organizationUserId">The ID of the organization user to resend the invite to.</param> |
||||
/// <param name="initOrganization">Whether to initialize the organization. |
||||
/// This is should only be true when inviting the owner of a new organization.</param> |
||||
Task ResendInviteAsync(Guid organizationId, Guid? invitingUserId, Guid organizationUserId, bool initOrganization = false); |
||||
} |
||||
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
using Bit.Core.AdminConsole.Entities; |
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; |
||||
using Bit.Core.AdminConsole.Utilities.DebuggingInstruments; |
||||
using Bit.Core.Entities; |
||||
using Bit.Core.Enums; |
||||
using Bit.Core.Exceptions; |
||||
using Bit.Core.Repositories; |
||||
using Microsoft.Extensions.Logging; |
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; |
||||
|
||||
public class ResendOrganizationInviteCommand : IResendOrganizationInviteCommand |
||||
{ |
||||
private readonly IOrganizationUserRepository _organizationUserRepository; |
||||
private readonly IOrganizationRepository _organizationRepository; |
||||
private readonly ISendOrganizationInvitesCommand _sendOrganizationInvitesCommand; |
||||
private readonly ILogger<ResendOrganizationInviteCommand> _logger; |
||||
|
||||
public ResendOrganizationInviteCommand( |
||||
IOrganizationUserRepository organizationUserRepository, |
||||
IOrganizationRepository organizationRepository, |
||||
ISendOrganizationInvitesCommand sendOrganizationInvitesCommand, |
||||
ILogger<ResendOrganizationInviteCommand> logger) |
||||
{ |
||||
_organizationUserRepository = organizationUserRepository; |
||||
_organizationRepository = organizationRepository; |
||||
_sendOrganizationInvitesCommand = sendOrganizationInvitesCommand; |
||||
_logger = logger; |
||||
} |
||||
|
||||
public async Task ResendInviteAsync(Guid organizationId, Guid? invitingUserId, Guid organizationUserId, |
||||
bool initOrganization = false) |
||||
{ |
||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(organizationUserId); |
||||
if (organizationUser == null || organizationUser.OrganizationId != organizationId || |
||||
organizationUser.Status != OrganizationUserStatusType.Invited) |
||||
{ |
||||
throw new BadRequestException("User invalid."); |
||||
} |
||||
|
||||
_logger.LogUserInviteStateDiagnostics(organizationUser); |
||||
|
||||
var organization = await _organizationRepository.GetByIdAsync(organizationUser.OrganizationId); |
||||
if (organization == null) |
||||
{ |
||||
throw new BadRequestException("Organization invalid."); |
||||
} |
||||
await SendInviteAsync(organizationUser, organization, initOrganization); |
||||
} |
||||
|
||||
private async Task SendInviteAsync(OrganizationUser organizationUser, Organization organization, bool initOrganization) => |
||||
await _sendOrganizationInvitesCommand.SendInvitesAsync(new SendInvitesRequest( |
||||
users: [organizationUser], |
||||
organization: organization, |
||||
initOrganization: initOrganization)); |
||||
} |
||||
@ -0,0 +1,137 @@
@@ -0,0 +1,137 @@
|
||||
using Bit.Core.AdminConsole.Entities; |
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; |
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; |
||||
using Bit.Core.Entities; |
||||
using Bit.Core.Enums; |
||||
using Bit.Core.Exceptions; |
||||
using Bit.Core.Repositories; |
||||
using Bit.Test.Common.AutoFixture; |
||||
using Bit.Test.Common.AutoFixture.Attributes; |
||||
using NSubstitute; |
||||
using Xunit; |
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; |
||||
|
||||
[SutProviderCustomize] |
||||
public class ResendOrganizationInviteCommandTests |
||||
{ |
||||
[Theory] |
||||
[BitAutoData] |
||||
public async Task ResendInviteAsync_WhenValidUserAndOrganization_SendsInvite( |
||||
Organization organization, |
||||
OrganizationUser organizationUser, |
||||
SutProvider<ResendOrganizationInviteCommand> sutProvider) |
||||
{ |
||||
// Arrange |
||||
organizationUser.OrganizationId = organization.Id; |
||||
organizationUser.Status = OrganizationUserStatusType.Invited; |
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>() |
||||
.GetByIdAsync(organizationUser.Id) |
||||
.Returns(organizationUser); |
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>() |
||||
.GetByIdAsync(organization.Id) |
||||
.Returns(organization); |
||||
|
||||
// Act |
||||
await sutProvider.Sut.ResendInviteAsync(organization.Id, invitingUserId: null, organizationUser.Id); |
||||
|
||||
// Assert |
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>() |
||||
.Received(1) |
||||
.SendInvitesAsync(Arg.Is<SendInvitesRequest>(req => |
||||
req.Organization == organization && |
||||
req.Users.Length == 1 && |
||||
req.Users[0] == organizationUser && |
||||
req.InitOrganization == false)); |
||||
} |
||||
|
||||
[Theory] |
||||
[BitAutoData] |
||||
public async Task ResendInviteAsync_WhenInitOrganizationTrue_SendsInviteWithInitFlag( |
||||
Organization organization, |
||||
OrganizationUser organizationUser, |
||||
SutProvider<ResendOrganizationInviteCommand> sutProvider) |
||||
{ |
||||
// Arrange |
||||
organizationUser.OrganizationId = organization.Id; |
||||
organizationUser.Status = OrganizationUserStatusType.Invited; |
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>() |
||||
.GetByIdAsync(organizationUser.Id) |
||||
.Returns(organizationUser); |
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>() |
||||
.GetByIdAsync(organization.Id) |
||||
.Returns(organization); |
||||
|
||||
// Act |
||||
await sutProvider.Sut.ResendInviteAsync(organization.Id, invitingUserId: null, organizationUser.Id, initOrganization: true); |
||||
|
||||
// Assert |
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>() |
||||
.Received(1) |
||||
.SendInvitesAsync(Arg.Is<SendInvitesRequest>(req => |
||||
req.Organization == organization && |
||||
req.Users.Length == 1 && |
||||
req.Users[0] == organizationUser && |
||||
req.InitOrganization == true)); |
||||
} |
||||
|
||||
[Theory] |
||||
[BitAutoData] |
||||
public async Task ResendInviteAsync_WhenOrganizationUserInvalid_ThrowsBadRequest( |
||||
Organization organization, |
||||
OrganizationUser organizationUser, |
||||
SutProvider<ResendOrganizationInviteCommand> sutProvider) |
||||
{ |
||||
// Arrange |
||||
organizationUser.OrganizationId = organization.Id; |
||||
organizationUser.Status = OrganizationUserStatusType.Accepted; |
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>() |
||||
.GetByIdAsync(organizationUser.Id) |
||||
.Returns(organizationUser); |
||||
|
||||
// Act + Assert |
||||
var ex = await Assert.ThrowsAsync<BadRequestException>(() => |
||||
sutProvider.Sut.ResendInviteAsync(organization.Id, invitingUserId: null, organizationUser.Id)); |
||||
|
||||
Assert.Equal("User invalid.", ex.Message); |
||||
|
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>() |
||||
.DidNotReceive() |
||||
.SendInvitesAsync(Arg.Any<SendInvitesRequest>()); |
||||
} |
||||
|
||||
[Theory] |
||||
[BitAutoData] |
||||
public async Task ResendInviteAsync_WhenOrganizationNotFound_ThrowsBadRequest( |
||||
Organization organization, |
||||
OrganizationUser organizationUser, |
||||
SutProvider<ResendOrganizationInviteCommand> sutProvider) |
||||
{ |
||||
// Arrange |
||||
organizationUser.OrganizationId = organization.Id; |
||||
organizationUser.Status = OrganizationUserStatusType.Invited; |
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>() |
||||
.GetByIdAsync(organizationUser.Id) |
||||
.Returns(organizationUser); |
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>() |
||||
.GetByIdAsync(organization.Id) |
||||
.Returns((Organization?)null); |
||||
|
||||
// Act + Assert |
||||
var ex = await Assert.ThrowsAsync<BadRequestException>(() => |
||||
sutProvider.Sut.ResendInviteAsync(organization.Id, invitingUserId: null, organizationUser.Id)); |
||||
|
||||
Assert.Equal("Organization invalid.", ex.Message); |
||||
|
||||
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>() |
||||
.DidNotReceive() |
||||
.SendInvitesAsync(Arg.Any<SendInvitesRequest>()); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue