mirror of
https://github.com/bitwarden/server
synced 2025-12-14 15:23:42 +00:00
[PM-12492] Create ResendOrganizationInviteCommand (#6182)
* 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 OrganizationServiceCollectionExtensions
This commit is contained in:
@@ -9,6 +9,7 @@ using Bit.Admin.Utilities;
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||||
using Bit.Core.AdminConsole.Providers.Interfaces;
|
using Bit.Core.AdminConsole.Providers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
@@ -32,7 +33,6 @@ namespace Bit.Admin.AdminConsole.Controllers;
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
public class OrganizationsController : Controller
|
public class OrganizationsController : Controller
|
||||||
{
|
{
|
||||||
private readonly IOrganizationService _organizationService;
|
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
|
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
|
||||||
@@ -55,9 +55,9 @@ public class OrganizationsController : Controller
|
|||||||
private readonly IProviderBillingService _providerBillingService;
|
private readonly IProviderBillingService _providerBillingService;
|
||||||
private readonly IOrganizationInitiateDeleteCommand _organizationInitiateDeleteCommand;
|
private readonly IOrganizationInitiateDeleteCommand _organizationInitiateDeleteCommand;
|
||||||
private readonly IPricingClient _pricingClient;
|
private readonly IPricingClient _pricingClient;
|
||||||
|
private readonly IResendOrganizationInviteCommand _resendOrganizationInviteCommand;
|
||||||
|
|
||||||
public OrganizationsController(
|
public OrganizationsController(
|
||||||
IOrganizationService organizationService,
|
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
IOrganizationConnectionRepository organizationConnectionRepository,
|
IOrganizationConnectionRepository organizationConnectionRepository,
|
||||||
@@ -79,9 +79,9 @@ public class OrganizationsController : Controller
|
|||||||
IRemoveOrganizationFromProviderCommand removeOrganizationFromProviderCommand,
|
IRemoveOrganizationFromProviderCommand removeOrganizationFromProviderCommand,
|
||||||
IProviderBillingService providerBillingService,
|
IProviderBillingService providerBillingService,
|
||||||
IOrganizationInitiateDeleteCommand organizationInitiateDeleteCommand,
|
IOrganizationInitiateDeleteCommand organizationInitiateDeleteCommand,
|
||||||
IPricingClient pricingClient)
|
IPricingClient pricingClient,
|
||||||
|
IResendOrganizationInviteCommand resendOrganizationInviteCommand)
|
||||||
{
|
{
|
||||||
_organizationService = organizationService;
|
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_organizationConnectionRepository = organizationConnectionRepository;
|
_organizationConnectionRepository = organizationConnectionRepository;
|
||||||
@@ -104,6 +104,7 @@ public class OrganizationsController : Controller
|
|||||||
_providerBillingService = providerBillingService;
|
_providerBillingService = providerBillingService;
|
||||||
_organizationInitiateDeleteCommand = organizationInitiateDeleteCommand;
|
_organizationInitiateDeleteCommand = organizationInitiateDeleteCommand;
|
||||||
_pricingClient = pricingClient;
|
_pricingClient = pricingClient;
|
||||||
|
_resendOrganizationInviteCommand = resendOrganizationInviteCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
[RequirePermission(Permission.Org_List_View)]
|
[RequirePermission(Permission.Org_List_View)]
|
||||||
@@ -395,7 +396,7 @@ public class OrganizationsController : Controller
|
|||||||
var organizationUsers = await _organizationUserRepository.GetManyByOrganizationAsync(id, OrganizationUserType.Owner);
|
var organizationUsers = await _organizationUserRepository.GetManyByOrganizationAsync(id, OrganizationUserType.Owner);
|
||||||
foreach (var organizationUser in organizationUsers)
|
foreach (var organizationUser in organizationUsers)
|
||||||
{
|
{
|
||||||
await _organizationService.ResendInviteAsync(id, null, organizationUser.Id, true);
|
await _resendOrganizationInviteCommand.ResendInviteAsync(id, null, organizationUser.Id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Json(null);
|
return Json(null);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using Bit.Core;
|
|||||||
using Bit.Core.AdminConsole.Enums;
|
using Bit.Core.AdminConsole.Enums;
|
||||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.RestoreUser.v1;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.RestoreUser.v1;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||||
@@ -62,6 +63,7 @@ public class OrganizationUsersController : Controller
|
|||||||
private readonly IPolicyRequirementQuery _policyRequirementQuery;
|
private readonly IPolicyRequirementQuery _policyRequirementQuery;
|
||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
private readonly IPricingClient _pricingClient;
|
private readonly IPricingClient _pricingClient;
|
||||||
|
private readonly IResendOrganizationInviteCommand _resendOrganizationInviteCommand;
|
||||||
private readonly IConfirmOrganizationUserCommand _confirmOrganizationUserCommand;
|
private readonly IConfirmOrganizationUserCommand _confirmOrganizationUserCommand;
|
||||||
private readonly IRestoreOrganizationUserCommand _restoreOrganizationUserCommand;
|
private readonly IRestoreOrganizationUserCommand _restoreOrganizationUserCommand;
|
||||||
private readonly IInitPendingOrganizationCommand _initPendingOrganizationCommand;
|
private readonly IInitPendingOrganizationCommand _initPendingOrganizationCommand;
|
||||||
@@ -92,7 +94,8 @@ public class OrganizationUsersController : Controller
|
|||||||
IConfirmOrganizationUserCommand confirmOrganizationUserCommand,
|
IConfirmOrganizationUserCommand confirmOrganizationUserCommand,
|
||||||
IRestoreOrganizationUserCommand restoreOrganizationUserCommand,
|
IRestoreOrganizationUserCommand restoreOrganizationUserCommand,
|
||||||
IInitPendingOrganizationCommand initPendingOrganizationCommand,
|
IInitPendingOrganizationCommand initPendingOrganizationCommand,
|
||||||
IRevokeOrganizationUserCommand revokeOrganizationUserCommand)
|
IRevokeOrganizationUserCommand revokeOrganizationUserCommand,
|
||||||
|
IResendOrganizationInviteCommand resendOrganizationInviteCommand)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
@@ -116,6 +119,7 @@ public class OrganizationUsersController : Controller
|
|||||||
_policyRequirementQuery = policyRequirementQuery;
|
_policyRequirementQuery = policyRequirementQuery;
|
||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
_pricingClient = pricingClient;
|
_pricingClient = pricingClient;
|
||||||
|
_resendOrganizationInviteCommand = resendOrganizationInviteCommand;
|
||||||
_confirmOrganizationUserCommand = confirmOrganizationUserCommand;
|
_confirmOrganizationUserCommand = confirmOrganizationUserCommand;
|
||||||
_restoreOrganizationUserCommand = restoreOrganizationUserCommand;
|
_restoreOrganizationUserCommand = restoreOrganizationUserCommand;
|
||||||
_initPendingOrganizationCommand = initPendingOrganizationCommand;
|
_initPendingOrganizationCommand = initPendingOrganizationCommand;
|
||||||
@@ -266,7 +270,7 @@ public class OrganizationUsersController : Controller
|
|||||||
public async Task Reinvite(Guid orgId, Guid id)
|
public async Task Reinvite(Guid orgId, Guid id)
|
||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User);
|
var userId = _userService.GetProperUserId(User);
|
||||||
await _organizationService.ResendInviteAsync(orgId, userId.Value, id);
|
await _resendOrganizationInviteCommand.ResendInviteAsync(orgId, userId.Value, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{organizationUserId}/accept-init")]
|
[HttpPost("{organizationUserId}/accept-init")]
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Bit.Api.AdminConsole.Public.Models.Request;
|
|||||||
using Bit.Api.AdminConsole.Public.Models.Response;
|
using Bit.Api.AdminConsole.Public.Models.Response;
|
||||||
using Bit.Api.Models.Public.Response;
|
using Bit.Api.Models.Public.Response;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
@@ -32,6 +33,7 @@ public class MembersController : Controller
|
|||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||||
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
|
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
|
||||||
|
private readonly IResendOrganizationInviteCommand _resendOrganizationInviteCommand;
|
||||||
|
|
||||||
public MembersController(
|
public MembersController(
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
@@ -45,7 +47,8 @@ public class MembersController : Controller
|
|||||||
IPaymentService paymentService,
|
IPaymentService paymentService,
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||||
IRemoveOrganizationUserCommand removeOrganizationUserCommand)
|
IRemoveOrganizationUserCommand removeOrganizationUserCommand,
|
||||||
|
IResendOrganizationInviteCommand resendOrganizationInviteCommand)
|
||||||
{
|
{
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_groupRepository = groupRepository;
|
_groupRepository = groupRepository;
|
||||||
@@ -59,6 +62,7 @@ public class MembersController : Controller
|
|||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||||
_removeOrganizationUserCommand = removeOrganizationUserCommand;
|
_removeOrganizationUserCommand = removeOrganizationUserCommand;
|
||||||
|
_resendOrganizationInviteCommand = resendOrganizationInviteCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -260,7 +264,7 @@ public class MembersController : Controller
|
|||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
await _organizationService.ResendInviteAsync(_currentContext.OrganizationId.Value, null, id);
|
await _resendOrganizationInviteCommand.ResendInviteAsync(_currentContext.OrganizationId.Value, null, id);
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
@@ -29,7 +29,6 @@ public interface IOrganizationService
|
|||||||
Task<List<OrganizationUser>> InviteUsersAsync(Guid organizationId, Guid? invitingUserId, EventSystemUser? systemUser,
|
Task<List<OrganizationUser>> InviteUsersAsync(Guid organizationId, Guid? invitingUserId, EventSystemUser? systemUser,
|
||||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites);
|
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites);
|
||||||
Task<IEnumerable<Tuple<OrganizationUser, string>>> ResendInvitesAsync(Guid organizationId, Guid? invitingUserId, IEnumerable<Guid> organizationUsersId);
|
Task<IEnumerable<Tuple<OrganizationUser, string>>> ResendInvitesAsync(Guid organizationId, Guid? invitingUserId, IEnumerable<Guid> organizationUsersId);
|
||||||
Task ResendInviteAsync(Guid organizationId, Guid? invitingUserId, Guid organizationUserId, bool initOrganization = false);
|
|
||||||
Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId);
|
Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId);
|
||||||
Task ImportAsync(Guid organizationId, IEnumerable<ImportedGroup> groups,
|
Task ImportAsync(Guid organizationId, IEnumerable<ImportedGroup> groups,
|
||||||
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
|
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
|
||||||
|
|||||||
@@ -766,21 +766,6 @@ public class OrganizationService : IOrganizationService
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ResendInviteAsync(Guid organizationId, Guid? invitingUserId, Guid organizationUserId,
|
|
||||||
bool initOrganization = false)
|
|
||||||
{
|
|
||||||
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
|
||||||
if (orgUser == null || orgUser.OrganizationId != organizationId ||
|
|
||||||
orgUser.Status != OrganizationUserStatusType.Invited)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("User invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogUserInviteStateDiagnostics(orgUser);
|
|
||||||
|
|
||||||
var org = await GetOrgById(orgUser.OrganizationId);
|
|
||||||
await SendInviteAsync(orgUser, org, initOrganization);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SendInvitesAsync(IEnumerable<OrganizationUser> orgUsers, Organization organization) =>
|
private async Task SendInvitesAsync(IEnumerable<OrganizationUser> orgUsers, Organization organization) =>
|
||||||
await _sendOrganizationInvitesCommand.SendInvitesAsync(new SendInvitesRequest(orgUsers, organization));
|
await _sendOrganizationInvitesCommand.SendInvitesAsync(new SendInvitesRequest(orgUsers, organization));
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ public static class OrganizationServiceCollectionExtensions
|
|||||||
|
|
||||||
services.AddScoped<IInviteOrganizationUsersCommand, InviteOrganizationUsersCommand>();
|
services.AddScoped<IInviteOrganizationUsersCommand, InviteOrganizationUsersCommand>();
|
||||||
services.AddScoped<ISendOrganizationInvitesCommand, SendOrganizationInvitesCommand>();
|
services.AddScoped<ISendOrganizationInvitesCommand, SendOrganizationInvitesCommand>();
|
||||||
|
services.AddScoped<IResendOrganizationInviteCommand, ResendOrganizationInviteCommand>();
|
||||||
|
|
||||||
services.AddScoped<IInviteUsersValidator, InviteOrganizationUsersValidator>();
|
services.AddScoped<IInviteUsersValidator, InviteOrganizationUsersValidator>();
|
||||||
services.AddScoped<IInviteUsersOrganizationValidator, InviteUsersOrganizationValidator>();
|
services.AddScoped<IInviteUsersOrganizationValidator, InviteUsersOrganizationValidator>();
|
||||||
|
|||||||
@@ -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>());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user