mirror of
https://github.com/bitwarden/server.git
synced 2025-05-04 03:02:18 -05:00
[PM-19332] Create InitPendingOrganizationCommand (#5584)
* wip * implement CommandResult * remove auth handler * fix import * remove method from OrganizationService * cleanup, add tests * clean up * fix auth in tests * clean up comments * clean up comments * clean up test
This commit is contained in:
parent
92701d8cd0
commit
8ecd9c5fb3
@ -63,6 +63,7 @@ public class OrganizationUsersController : Controller
|
|||||||
private readonly IPricingClient _pricingClient;
|
private readonly IPricingClient _pricingClient;
|
||||||
private readonly IConfirmOrganizationUserCommand _confirmOrganizationUserCommand;
|
private readonly IConfirmOrganizationUserCommand _confirmOrganizationUserCommand;
|
||||||
private readonly IRestoreOrganizationUserCommand _restoreOrganizationUserCommand;
|
private readonly IRestoreOrganizationUserCommand _restoreOrganizationUserCommand;
|
||||||
|
private readonly IInitPendingOrganizationCommand _initPendingOrganizationCommand;
|
||||||
|
|
||||||
public OrganizationUsersController(
|
public OrganizationUsersController(
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
@ -89,7 +90,8 @@ public class OrganizationUsersController : Controller
|
|||||||
IFeatureService featureService,
|
IFeatureService featureService,
|
||||||
IPricingClient pricingClient,
|
IPricingClient pricingClient,
|
||||||
IConfirmOrganizationUserCommand confirmOrganizationUserCommand,
|
IConfirmOrganizationUserCommand confirmOrganizationUserCommand,
|
||||||
IRestoreOrganizationUserCommand restoreOrganizationUserCommand)
|
IRestoreOrganizationUserCommand restoreOrganizationUserCommand,
|
||||||
|
IInitPendingOrganizationCommand initPendingOrganizationCommand)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
@ -116,6 +118,7 @@ public class OrganizationUsersController : Controller
|
|||||||
_pricingClient = pricingClient;
|
_pricingClient = pricingClient;
|
||||||
_confirmOrganizationUserCommand = confirmOrganizationUserCommand;
|
_confirmOrganizationUserCommand = confirmOrganizationUserCommand;
|
||||||
_restoreOrganizationUserCommand = restoreOrganizationUserCommand;
|
_restoreOrganizationUserCommand = restoreOrganizationUserCommand;
|
||||||
|
_initPendingOrganizationCommand = initPendingOrganizationCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
@ -313,7 +316,7 @@ public class OrganizationUsersController : Controller
|
|||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
await _organizationService.InitPendingOrganization(user, orgId, organizationUserId, model.Keys.PublicKey, model.Keys.EncryptedPrivateKey, model.CollectionName, model.Token);
|
await _initPendingOrganizationCommand.InitPendingOrganizationAsync(user, orgId, organizationUserId, model.Keys.PublicKey, model.Keys.EncryptedPrivateKey, model.CollectionName, model.Token);
|
||||||
await _acceptOrgUserCommand.AcceptOrgUserByEmailTokenAsync(organizationUserId, user, model.Token, _userService);
|
await _acceptOrgUserCommand.AcceptOrgUserByEmailTokenAsync(organizationUserId, user, model.Token, _userService);
|
||||||
await _confirmOrganizationUserCommand.ConfirmUserAsync(orgId, organizationUserId, model.Key, user.Id);
|
await _confirmOrganizationUserCommand.ConfirmUserAsync(orgId, organizationUserId, model.Key, user.Id);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,128 @@
|
|||||||
|
using Bit.Core.AdminConsole.Enums;
|
||||||
|
using Bit.Core.AdminConsole.Services;
|
||||||
|
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
using Bit.Core.Tokens;
|
||||||
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
|
|
||||||
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||||
|
|
||||||
|
public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly IOrganizationService _organizationService;
|
||||||
|
private readonly ICollectionRepository _collectionRepository;
|
||||||
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
|
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
||||||
|
private readonly IDataProtector _dataProtector;
|
||||||
|
private readonly IGlobalSettings _globalSettings;
|
||||||
|
private readonly IPolicyService _policyService;
|
||||||
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
|
||||||
|
public InitPendingOrganizationCommand(
|
||||||
|
IOrganizationService organizationService,
|
||||||
|
ICollectionRepository collectionRepository,
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
|
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
|
||||||
|
IDataProtectionProvider dataProtectionProvider,
|
||||||
|
IGlobalSettings globalSettings,
|
||||||
|
IPolicyService policyService,
|
||||||
|
IOrganizationUserRepository organizationUserRepository
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_organizationService = organizationService;
|
||||||
|
_collectionRepository = collectionRepository;
|
||||||
|
_organizationRepository = organizationRepository;
|
||||||
|
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
||||||
|
_dataProtector = dataProtectionProvider.CreateProtector(OrgUserInviteTokenable.DataProtectorPurpose);
|
||||||
|
_globalSettings = globalSettings;
|
||||||
|
_policyService = policyService;
|
||||||
|
_organizationUserRepository = organizationUserRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitPendingOrganizationAsync(User user, Guid organizationId, Guid organizationUserId, string publicKey, string privateKey, string collectionName, string emailToken)
|
||||||
|
{
|
||||||
|
await ValidateSignUpPoliciesAsync(user.Id);
|
||||||
|
|
||||||
|
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
||||||
|
if (orgUser == null)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("User invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokenValid = ValidateInviteToken(orgUser, user, emailToken);
|
||||||
|
|
||||||
|
if (!tokenValid)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Invalid token");
|
||||||
|
}
|
||||||
|
|
||||||
|
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
||||||
|
|
||||||
|
if (org.Enabled)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Organization is already enabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (org.Status != OrganizationStatusType.Pending)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Organization is not on a Pending status.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(org.PublicKey))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Organization already has a Public Key.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(org.PrivateKey))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Organization already has a Private Key.");
|
||||||
|
}
|
||||||
|
|
||||||
|
org.Enabled = true;
|
||||||
|
org.Status = OrganizationStatusType.Created;
|
||||||
|
org.PublicKey = publicKey;
|
||||||
|
org.PrivateKey = privateKey;
|
||||||
|
|
||||||
|
await _organizationService.UpdateAsync(org);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(collectionName))
|
||||||
|
{
|
||||||
|
// give the owner Can Manage access over the default collection
|
||||||
|
List<CollectionAccessSelection> defaultOwnerAccess =
|
||||||
|
[new CollectionAccessSelection { Id = orgUser.Id, HidePasswords = false, ReadOnly = false, Manage = true }];
|
||||||
|
|
||||||
|
var defaultCollection = new Collection
|
||||||
|
{
|
||||||
|
Name = collectionName,
|
||||||
|
OrganizationId = org.Id
|
||||||
|
};
|
||||||
|
await _collectionRepository.CreateAsync(defaultCollection, null, defaultOwnerAccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ValidateSignUpPoliciesAsync(Guid ownerId)
|
||||||
|
{
|
||||||
|
var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(ownerId, PolicyType.SingleOrg);
|
||||||
|
if (anySingleOrgPolicies)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("You may not create an organization. You belong to an organization " +
|
||||||
|
"which has a policy that prohibits you from being a member of any other organization.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ValidateInviteToken(OrganizationUser orgUser, User user, string emailToken)
|
||||||
|
{
|
||||||
|
var tokenValid = OrgUserInviteTokenable.ValidateOrgUserInviteStringToken(
|
||||||
|
_orgUserInviteTokenDataFactory, emailToken, orgUser);
|
||||||
|
|
||||||
|
return tokenValid;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
namespace Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
|
||||||
|
public interface IInitPendingOrganizationCommand
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Update an Organization entry by setting the public/private keys, set it as 'Enabled' and move the Status from 'Pending' to 'Created'.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method must target a disabled Organization that has null keys and status as 'Pending'.
|
||||||
|
/// </remarks>
|
||||||
|
Task InitPendingOrganizationAsync(User user, Guid organizationId, Guid organizationUserId, string publicKey, string privateKey, string collectionName, string emailToken);
|
||||||
|
}
|
@ -48,13 +48,6 @@ public interface IOrganizationService
|
|||||||
Task<List<Tuple<OrganizationUser, string>>> RevokeUsersAsync(Guid organizationId,
|
Task<List<Tuple<OrganizationUser, string>>> RevokeUsersAsync(Guid organizationId,
|
||||||
IEnumerable<Guid> organizationUserIds, Guid? revokingUserId);
|
IEnumerable<Guid> organizationUserIds, Guid? revokingUserId);
|
||||||
Task CreatePendingOrganization(Organization organization, string ownerEmail, ClaimsPrincipal user, IUserService userService, bool salesAssistedTrialStarted);
|
Task CreatePendingOrganization(Organization organization, string ownerEmail, ClaimsPrincipal user, IUserService userService, bool salesAssistedTrialStarted);
|
||||||
/// <summary>
|
|
||||||
/// Update an Organization entry by setting the public/private keys, set it as 'Enabled' and move the Status from 'Pending' to 'Created'.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This method must target a disabled Organization that has null keys and status as 'Pending'.
|
|
||||||
/// </remarks>
|
|
||||||
Task InitPendingOrganization(User user, Guid organizationId, Guid organizationUserId, string publicKey, string privateKey, string collectionName, string emailToken);
|
|
||||||
Task ReplaceAndUpdateCacheAsync(Organization org, EventType? orgEvent = null);
|
Task ReplaceAndUpdateCacheAsync(Organization org, EventType? orgEvent = null);
|
||||||
|
|
||||||
void ValidatePasswordManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade);
|
void ValidatePasswordManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade);
|
||||||
|
@ -13,7 +13,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
|||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
|
||||||
using Bit.Core.Auth.Repositories;
|
using Bit.Core.Auth.Repositories;
|
||||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||||
using Bit.Core.Billing.Constants;
|
using Bit.Core.Billing.Constants;
|
||||||
@ -31,12 +30,10 @@ using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
|||||||
using Bit.Core.Platform.Push;
|
using Bit.Core.Platform.Push;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Tokens;
|
|
||||||
using Bit.Core.Tools.Enums;
|
using Bit.Core.Tools.Enums;
|
||||||
using Bit.Core.Tools.Models.Business;
|
using Bit.Core.Tools.Models.Business;
|
||||||
using Bit.Core.Tools.Services;
|
using Bit.Core.Tools.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Stripe;
|
using Stripe;
|
||||||
using OrganizationUserInvite = Bit.Core.Models.Business.OrganizationUserInvite;
|
using OrganizationUserInvite = Bit.Core.Models.Business.OrganizationUserInvite;
|
||||||
@ -77,8 +74,6 @@ public class OrganizationService : IOrganizationService
|
|||||||
private readonly IPricingClient _pricingClient;
|
private readonly IPricingClient _pricingClient;
|
||||||
private readonly IPolicyRequirementQuery _policyRequirementQuery;
|
private readonly IPolicyRequirementQuery _policyRequirementQuery;
|
||||||
private readonly ISendOrganizationInvitesCommand _sendOrganizationInvitesCommand;
|
private readonly ISendOrganizationInvitesCommand _sendOrganizationInvitesCommand;
|
||||||
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
|
||||||
private readonly IDataProtector _dataProtector;
|
|
||||||
|
|
||||||
public OrganizationService(
|
public OrganizationService(
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
@ -112,9 +107,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery,
|
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery,
|
||||||
IPricingClient pricingClient,
|
IPricingClient pricingClient,
|
||||||
IPolicyRequirementQuery policyRequirementQuery,
|
IPolicyRequirementQuery policyRequirementQuery,
|
||||||
ISendOrganizationInvitesCommand sendOrganizationInvitesCommand,
|
ISendOrganizationInvitesCommand sendOrganizationInvitesCommand
|
||||||
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
|
|
||||||
IDataProtectionProvider dataProtectionProvider
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
@ -149,8 +142,6 @@ public class OrganizationService : IOrganizationService
|
|||||||
_pricingClient = pricingClient;
|
_pricingClient = pricingClient;
|
||||||
_policyRequirementQuery = policyRequirementQuery;
|
_policyRequirementQuery = policyRequirementQuery;
|
||||||
_sendOrganizationInvitesCommand = sendOrganizationInvitesCommand;
|
_sendOrganizationInvitesCommand = sendOrganizationInvitesCommand;
|
||||||
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
|
||||||
_dataProtector = dataProtectionProvider.CreateProtector(OrgUserInviteTokenable.DataProtectorPurpose);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken,
|
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken,
|
||||||
@ -1921,71 +1912,4 @@ public class OrganizationService : IOrganizationService
|
|||||||
SalesAssistedTrialStarted = salesAssistedTrialStarted,
|
SalesAssistedTrialStarted = salesAssistedTrialStarted,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InitPendingOrganization(User user, Guid organizationId, Guid organizationUserId, string publicKey, string privateKey, string collectionName, string emailToken)
|
|
||||||
{
|
|
||||||
await ValidateSignUpPoliciesAsync(user.Id);
|
|
||||||
|
|
||||||
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
|
||||||
if (orgUser == null)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("User invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: PM-4142 - remove old token validation logic once 3 releases of backwards compatibility are complete
|
|
||||||
var newTokenValid = OrgUserInviteTokenable.ValidateOrgUserInviteStringToken(
|
|
||||||
_orgUserInviteTokenDataFactory, emailToken, orgUser);
|
|
||||||
|
|
||||||
var tokenValid = newTokenValid ||
|
|
||||||
CoreHelpers.UserInviteTokenIsValid(_dataProtector, emailToken, user.Email, orgUser.Id,
|
|
||||||
_globalSettings);
|
|
||||||
|
|
||||||
if (!tokenValid)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Invalid token.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var org = await GetOrgById(organizationId);
|
|
||||||
|
|
||||||
if (org.Enabled)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Organization is already enabled.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (org.Status != OrganizationStatusType.Pending)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Organization is not on a Pending status.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(org.PublicKey))
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Organization already has a Public Key.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(org.PrivateKey))
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Organization already has a Private Key.");
|
|
||||||
}
|
|
||||||
|
|
||||||
org.Enabled = true;
|
|
||||||
org.Status = OrganizationStatusType.Created;
|
|
||||||
org.PublicKey = publicKey;
|
|
||||||
org.PrivateKey = privateKey;
|
|
||||||
|
|
||||||
await UpdateAsync(org);
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(collectionName))
|
|
||||||
{
|
|
||||||
// give the owner Can Manage access over the default collection
|
|
||||||
List<CollectionAccessSelection> defaultOwnerAccess =
|
|
||||||
[new CollectionAccessSelection { Id = organizationUserId, HidePasswords = false, ReadOnly = false, Manage = true }];
|
|
||||||
|
|
||||||
var defaultCollection = new Collection
|
|
||||||
{
|
|
||||||
Name = collectionName,
|
|
||||||
OrganizationId = org.Id
|
|
||||||
};
|
|
||||||
await _collectionRepository.CreateAsync(defaultCollection, null, defaultOwnerAccess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -193,6 +193,7 @@ public static class OrganizationServiceCollectionExtensions
|
|||||||
services.AddScoped<IInviteUsersOrganizationValidator, InviteUsersOrganizationValidator>();
|
services.AddScoped<IInviteUsersOrganizationValidator, InviteUsersOrganizationValidator>();
|
||||||
services.AddScoped<IInviteUsersPasswordManagerValidator, InviteUsersPasswordManagerValidator>();
|
services.AddScoped<IInviteUsersPasswordManagerValidator, InviteUsersPasswordManagerValidator>();
|
||||||
services.AddScoped<IInviteUsersEnvironmentValidator, InviteUsersEnvironmentValidator>();
|
services.AddScoped<IInviteUsersEnvironmentValidator, InviteUsersEnvironmentValidator>();
|
||||||
|
services.AddScoped<IInitPendingOrganizationCommand, InitPendingOrganizationCommand>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to OrganizationSubscriptionServiceCollectionExtensions when OrganizationUser methods are moved out of
|
// TODO: move to OrganizationSubscriptionServiceCollectionExtensions when OrganizationUser methods are moved out of
|
||||||
|
@ -0,0 +1,169 @@
|
|||||||
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||||
|
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Tokens;
|
||||||
|
using Bit.Test.Common.AutoFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using Bit.Test.Common.Fakes;
|
||||||
|
using NSubstitute;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Organizations;
|
||||||
|
|
||||||
|
[SutProviderCustomize]
|
||||||
|
public class InitPendingOrganizationCommandTests
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly IOrgUserInviteTokenableFactory _orgUserInviteTokenableFactory = Substitute.For<IOrgUserInviteTokenableFactory>();
|
||||||
|
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory = new FakeDataProtectorTokenFactory<OrgUserInviteTokenable>();
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task Init_Organization_Success(User user, Guid orgId, Guid orgUserId, string publicKey,
|
||||||
|
string privateKey, SutProvider<InitPendingOrganizationCommand> sutProvider, Organization org, OrganizationUser orgUser)
|
||||||
|
{
|
||||||
|
var token = CreateToken(orgUser, orgUserId, sutProvider);
|
||||||
|
|
||||||
|
org.PrivateKey = null;
|
||||||
|
org.PublicKey = null;
|
||||||
|
|
||||||
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||||
|
organizationRepository.GetByIdAsync(orgId).Returns(org);
|
||||||
|
|
||||||
|
var organizationServcie = sutProvider.GetDependency<IOrganizationService>();
|
||||||
|
var collectionRepository = sutProvider.GetDependency<ICollectionRepository>();
|
||||||
|
|
||||||
|
await sutProvider.Sut.InitPendingOrganizationAsync(user, orgId, orgUserId, publicKey, privateKey, "", token);
|
||||||
|
|
||||||
|
await organizationRepository.Received().GetByIdAsync(orgId);
|
||||||
|
await organizationServcie.Received().UpdateAsync(org);
|
||||||
|
await collectionRepository.DidNotReceiveWithAnyArgs().CreateAsync(default);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task Init_Organization_With_CollectionName_Success(User user, Guid orgId, Guid orgUserId, string publicKey,
|
||||||
|
string privateKey, SutProvider<InitPendingOrganizationCommand> sutProvider, Organization org, string collectionName, OrganizationUser orgUser)
|
||||||
|
{
|
||||||
|
var token = CreateToken(orgUser, orgUserId, sutProvider);
|
||||||
|
|
||||||
|
org.PrivateKey = null;
|
||||||
|
org.PublicKey = null;
|
||||||
|
org.Id = orgId;
|
||||||
|
|
||||||
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||||
|
organizationRepository.GetByIdAsync(orgId).Returns(org);
|
||||||
|
|
||||||
|
var organizationServcie = sutProvider.GetDependency<IOrganizationService>();
|
||||||
|
var collectionRepository = sutProvider.GetDependency<ICollectionRepository>();
|
||||||
|
|
||||||
|
await sutProvider.Sut.InitPendingOrganizationAsync(user, orgId, orgUserId, publicKey, privateKey, collectionName, token);
|
||||||
|
|
||||||
|
await organizationRepository.Received().GetByIdAsync(orgId);
|
||||||
|
await organizationServcie.Received().UpdateAsync(org);
|
||||||
|
|
||||||
|
await collectionRepository.Received().CreateAsync(
|
||||||
|
Arg.Any<Collection>(),
|
||||||
|
Arg.Is<List<CollectionAccessSelection>>(l => l == null),
|
||||||
|
Arg.Is<List<CollectionAccessSelection>>(l => l.Any(i => i.Manage == true)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task Init_Organization_When_Organization_Is_Enabled(User user, Guid orgId, Guid orgUserId, string publicKey,
|
||||||
|
string privateKey, SutProvider<InitPendingOrganizationCommand> sutProvider, Organization org, OrganizationUser orgUser)
|
||||||
|
|
||||||
|
{
|
||||||
|
var token = CreateToken(orgUser, orgUserId, sutProvider);
|
||||||
|
|
||||||
|
org.Enabled = true;
|
||||||
|
|
||||||
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||||
|
organizationRepository.GetByIdAsync(orgId).Returns(org);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.InitPendingOrganizationAsync(user, orgId, orgUserId, publicKey, privateKey, "", token));
|
||||||
|
|
||||||
|
Assert.Equal("Organization is already enabled.", exception.Message);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task Init_Organization_When_Organization_Is_Not_Pending(User user, Guid orgId, Guid orgUserId, string publicKey,
|
||||||
|
string privateKey, SutProvider<InitPendingOrganizationCommand> sutProvider, Organization org, OrganizationUser orgUser)
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
var token = CreateToken(orgUser, orgUserId, sutProvider);
|
||||||
|
|
||||||
|
org.Status = Enums.OrganizationStatusType.Created;
|
||||||
|
|
||||||
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||||
|
organizationRepository.GetByIdAsync(orgId).Returns(org);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.InitPendingOrganizationAsync(user, orgId, orgUserId, publicKey, privateKey, "", token));
|
||||||
|
|
||||||
|
Assert.Equal("Organization is not on a Pending status.", exception.Message);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task Init_Organization_When_Organization_Has_Public_Key(User user, Guid orgId, Guid orgUserId, string publicKey,
|
||||||
|
string privateKey, SutProvider<InitPendingOrganizationCommand> sutProvider, Organization org, OrganizationUser orgUser)
|
||||||
|
|
||||||
|
{
|
||||||
|
var token = CreateToken(orgUser, orgUserId, sutProvider);
|
||||||
|
|
||||||
|
org.PublicKey = publicKey;
|
||||||
|
|
||||||
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||||
|
organizationRepository.GetByIdAsync(orgId).Returns(org);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.InitPendingOrganizationAsync(user, orgId, orgUserId, publicKey, privateKey, "", token));
|
||||||
|
|
||||||
|
Assert.Equal("Organization already has a Public Key.", exception.Message);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task Init_Organization_When_Organization_Has_Private_Key(User user, Guid orgId, Guid orgUserId, string publicKey,
|
||||||
|
string privateKey, SutProvider<InitPendingOrganizationCommand> sutProvider, Organization org, OrganizationUser orgUser)
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
var token = CreateToken(orgUser, orgUserId, sutProvider);
|
||||||
|
|
||||||
|
org.PublicKey = null;
|
||||||
|
org.PrivateKey = privateKey;
|
||||||
|
org.Enabled = false;
|
||||||
|
|
||||||
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||||
|
organizationRepository.GetByIdAsync(orgId).Returns(org);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.InitPendingOrganizationAsync(user, orgId, orgUserId, publicKey, privateKey, "", token));
|
||||||
|
|
||||||
|
Assert.Equal("Organization already has a Private Key.", exception.Message);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateToken(OrganizationUser orgUser, Guid orgUserId, SutProvider<InitPendingOrganizationCommand> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
||||||
|
sutProvider.Create();
|
||||||
|
|
||||||
|
_orgUserInviteTokenableFactory.CreateToken(orgUser).Returns(new OrgUserInviteTokenable(orgUser)
|
||||||
|
{
|
||||||
|
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
||||||
|
});
|
||||||
|
|
||||||
|
var orgUserInviteTokenable = _orgUserInviteTokenableFactory.CreateToken(orgUser);
|
||||||
|
var protectedToken = _orgUserInviteTokenDataFactory.Protect(orgUserInviteTokenable);
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(orgUserId).Returns(orgUser);
|
||||||
|
|
||||||
|
return protectedToken;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user