mirror of
https://github.com/bitwarden/server
synced 2026-01-02 00:23:40 +00:00
Merge branch 'main' into auth/pm-22975/client-version-validator
This commit is contained in:
@@ -11,6 +11,7 @@ using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.KeyManagement.Kdf;
|
||||
using Bit.Core.KeyManagement.Models.Api.Request;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.KeyManagement.Queries.Interfaces;
|
||||
using Bit.Core.Repositories;
|
||||
@@ -38,6 +39,7 @@ public class AccountsControllerTests : IDisposable
|
||||
private readonly IUserAccountKeysQuery _userAccountKeysQuery;
|
||||
private readonly ITwoFactorEmailService _twoFactorEmailService;
|
||||
private readonly IChangeKdfCommand _changeKdfCommand;
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public AccountsControllerTests()
|
||||
{
|
||||
@@ -53,6 +55,7 @@ public class AccountsControllerTests : IDisposable
|
||||
_userAccountKeysQuery = Substitute.For<IUserAccountKeysQuery>();
|
||||
_twoFactorEmailService = Substitute.For<ITwoFactorEmailService>();
|
||||
_changeKdfCommand = Substitute.For<IChangeKdfCommand>();
|
||||
_userRepository = Substitute.For<IUserRepository>();
|
||||
|
||||
_sut = new AccountsController(
|
||||
_organizationService,
|
||||
@@ -66,7 +69,8 @@ public class AccountsControllerTests : IDisposable
|
||||
_featureService,
|
||||
_userAccountKeysQuery,
|
||||
_twoFactorEmailService,
|
||||
_changeKdfCommand
|
||||
_changeKdfCommand,
|
||||
_userRepository
|
||||
);
|
||||
}
|
||||
|
||||
@@ -688,6 +692,37 @@ public class AccountsControllerTests : IDisposable
|
||||
await _sut.PostKdf(model);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task PostKeys_NoUser_Errors(KeysRequestModel model)
|
||||
{
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(Task.FromResult<User>(null));
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => _sut.PostKeys(model));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData("existing", "existing")]
|
||||
[BitAutoData((string)null, "existing")]
|
||||
[BitAutoData("", "existing")]
|
||||
[BitAutoData(" ", "existing")]
|
||||
[BitAutoData("existing", null)]
|
||||
[BitAutoData("existing", "")]
|
||||
[BitAutoData("existing", " ")]
|
||||
public async Task PostKeys_UserAlreadyHasKeys_Errors(string? existingPrivateKey, string? existingPublicKey,
|
||||
KeysRequestModel model)
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
user.PrivateKey = existingPrivateKey;
|
||||
user.PublicKey = existingPublicKey;
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(Task.FromResult(user));
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => _sut.PostKeys(model));
|
||||
|
||||
Assert.NotNull(exception.Message);
|
||||
Assert.Contains("User has existing keypair", exception.Message);
|
||||
}
|
||||
|
||||
// Below are helper functions that currently belong to this
|
||||
// test class, but ultimately may need to be split out into
|
||||
// something greater in order to share common test steps with
|
||||
@@ -738,5 +773,77 @@ public class AccountsControllerTests : IDisposable
|
||||
_userService.GetUserByIdAsync(Arg.Any<Guid>())
|
||||
.Returns(Task.FromResult((User)null));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostKeys_WithAccountKeys_CallsSetV2AccountCryptographicState(
|
||||
User user,
|
||||
KeysRequestModel model)
|
||||
{
|
||||
// Arrange
|
||||
user.PublicKey = null;
|
||||
user.PrivateKey = null;
|
||||
model.AccountKeys = new AccountKeysRequestModel
|
||||
{
|
||||
UserKeyEncryptedAccountPrivateKey = "wrapped-private-key",
|
||||
AccountPublicKey = "public-key",
|
||||
PublicKeyEncryptionKeyPair = new PublicKeyEncryptionKeyPairRequestModel
|
||||
{
|
||||
PublicKey = "public-key",
|
||||
WrappedPrivateKey = "wrapped-private-key",
|
||||
SignedPublicKey = "signed-public-key"
|
||||
},
|
||||
SignatureKeyPair = new SignatureKeyPairRequestModel
|
||||
{
|
||||
VerifyingKey = "verifying-key",
|
||||
SignatureAlgorithm = "ed25519",
|
||||
WrappedSigningKey = "wrapped-signing-key"
|
||||
},
|
||||
SecurityState = new SecurityStateModel
|
||||
{
|
||||
SecurityState = "security-state",
|
||||
SecurityVersion = 2
|
||||
}
|
||||
};
|
||||
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
||||
|
||||
// Act
|
||||
var result = await _sut.PostKeys(model);
|
||||
|
||||
// Assert
|
||||
await _userRepository.Received(1).SetV2AccountCryptographicStateAsync(
|
||||
user.Id,
|
||||
Arg.Any<UserAccountKeysData>());
|
||||
await _userService.DidNotReceiveWithAnyArgs().SaveUserAsync(Arg.Any<User>());
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("keys", result.Object);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostKeys_WithoutAccountKeys_CallsSaveUser(
|
||||
User user,
|
||||
KeysRequestModel model)
|
||||
{
|
||||
// Arrange
|
||||
user.PublicKey = null;
|
||||
user.PrivateKey = null;
|
||||
model.AccountKeys = null;
|
||||
model.PublicKey = "public-key";
|
||||
model.EncryptedPrivateKey = "encrypted-private-key";
|
||||
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
||||
|
||||
// Act
|
||||
var result = await _sut.PostKeys(model);
|
||||
|
||||
// Assert
|
||||
await _userService.Received(1).SaveUserAsync(Arg.Is<User>(u =>
|
||||
u.PublicKey == model.PublicKey &&
|
||||
u.PrivateKey == model.EncryptedPrivateKey));
|
||||
await _userRepository.DidNotReceiveWithAnyArgs()
|
||||
.SetV2AccountCryptographicStateAsync(Arg.Any<Guid>(), Arg.Any<UserAccountKeysData>());
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("keys", result.Object);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using AutoFixture;
|
||||
using AutoFixture.Xunit2;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.AutoFixture;
|
||||
@@ -9,10 +10,16 @@ namespace Bit.Core.Test.AdminConsole.AutoFixture;
|
||||
internal class OrganizationUserPolicyDetailsCustomization : ICustomization
|
||||
{
|
||||
public PolicyType Type { get; set; }
|
||||
public OrganizationUserStatusType Status { get; set; }
|
||||
public OrganizationUserType UserType { get; set; }
|
||||
public bool IsProvider { get; set; }
|
||||
|
||||
public OrganizationUserPolicyDetailsCustomization(PolicyType type)
|
||||
public OrganizationUserPolicyDetailsCustomization(PolicyType type, OrganizationUserStatusType status, OrganizationUserType userType, bool isProvider)
|
||||
{
|
||||
Type = type;
|
||||
Status = status;
|
||||
UserType = userType;
|
||||
IsProvider = isProvider;
|
||||
}
|
||||
|
||||
public void Customize(IFixture fixture)
|
||||
@@ -20,6 +27,9 @@ internal class OrganizationUserPolicyDetailsCustomization : ICustomization
|
||||
fixture.Customize<OrganizationUserPolicyDetails>(composer => composer
|
||||
.With(o => o.OrganizationId, Guid.NewGuid())
|
||||
.With(o => o.PolicyType, Type)
|
||||
.With(o => o.OrganizationUserStatus, Status)
|
||||
.With(o => o.OrganizationUserType, UserType)
|
||||
.With(o => o.IsProvider, IsProvider)
|
||||
.With(o => o.PolicyEnabled, true));
|
||||
}
|
||||
}
|
||||
@@ -27,14 +37,25 @@ internal class OrganizationUserPolicyDetailsCustomization : ICustomization
|
||||
public class OrganizationUserPolicyDetailsAttribute : CustomizeAttribute
|
||||
{
|
||||
private readonly PolicyType _type;
|
||||
private readonly OrganizationUserStatusType _status;
|
||||
private readonly OrganizationUserType _userType;
|
||||
private readonly bool _isProvider;
|
||||
|
||||
public OrganizationUserPolicyDetailsAttribute(PolicyType type)
|
||||
public OrganizationUserPolicyDetailsAttribute(PolicyType type) : this(type, OrganizationUserStatusType.Accepted, OrganizationUserType.User, false)
|
||||
{
|
||||
_type = type;
|
||||
}
|
||||
|
||||
public OrganizationUserPolicyDetailsAttribute(PolicyType type, OrganizationUserStatusType status, OrganizationUserType userType, bool isProvider)
|
||||
{
|
||||
_type = type;
|
||||
_status = status;
|
||||
_userType = userType;
|
||||
_isProvider = isProvider;
|
||||
}
|
||||
|
||||
public override ICustomization GetCustomization(ParameterInfo parameter)
|
||||
{
|
||||
return new OrganizationUserPolicyDetailsCustomization(_type);
|
||||
return new OrganizationUserPolicyDetailsCustomization(_type, _status, _userType, _isProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.AutoConfirmUser;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
@@ -24,6 +26,7 @@ using Bit.Test.Common.Fakes;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using static Bit.Core.AdminConsole.Utilities.v2.Validation.ValidationResultHelpers;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationUsers;
|
||||
|
||||
@@ -673,6 +676,79 @@ public class AcceptOrgUserCommandTests
|
||||
Assert.Equal("User not found within organization.", exception.Message);
|
||||
}
|
||||
|
||||
// Auto-confirm policy validation tests --------------------------------------------------------------------------
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task AcceptOrgUserAsync_WithAutoConfirmIsNotEnabled_DoesNotCheckCompliance(
|
||||
SutProvider<AcceptOrgUserCommand> sutProvider,
|
||||
User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails)
|
||||
{
|
||||
// Arrange
|
||||
SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails);
|
||||
|
||||
// Act
|
||||
var resultOrgUser = await sutProvider.Sut.AcceptOrgUserAsync(orgUser, user, _userService);
|
||||
|
||||
// Assert
|
||||
AssertValidAcceptedOrgUser(resultOrgUser, orgUser, user);
|
||||
|
||||
await sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>().DidNotReceiveWithAnyArgs()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task AcceptOrgUserAsync_WithUserThatIsCompliantWithAutoConfirm_AcceptsUser(
|
||||
SutProvider<AcceptOrgUserCommand> sutProvider,
|
||||
User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails)
|
||||
{
|
||||
// Arrange
|
||||
SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails);
|
||||
|
||||
// Mock auto-confirm enforcement query to return valid (no auto-confirm restrictions)
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>())
|
||||
.Returns(Valid(new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser], user)));
|
||||
|
||||
// Act
|
||||
var resultOrgUser = await sutProvider.Sut.AcceptOrgUserAsync(orgUser, user, _userService);
|
||||
|
||||
// Assert
|
||||
AssertValidAcceptedOrgUser(resultOrgUser, orgUser, user);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).ReplaceAsync(
|
||||
Arg.Is<OrganizationUser>(ou => ou.Id == orgUser.Id && ou.Status == OrganizationUserStatusType.Accepted));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task AcceptOrgUserAsync_WithAutoConfirmIsEnabledAndFailsCompliance_ThrowsBadRequestException(
|
||||
SutProvider<AcceptOrgUserCommand> sutProvider,
|
||||
User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails,
|
||||
OrganizationUser otherOrgUser)
|
||||
{
|
||||
// Arrange
|
||||
SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>())
|
||||
.Returns(Invalid(
|
||||
new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser, otherOrgUser], user),
|
||||
new UserCannotBelongToAnotherOrganization()));
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.AcceptOrgUserAsync(orgUser, user, _userService));
|
||||
|
||||
// Should get auto-confirm error
|
||||
Assert.Equal(new UserCannotBelongToAnotherOrganization().Message, exception.Message);
|
||||
}
|
||||
|
||||
// Private helpers -------------------------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
@@ -716,7 +792,7 @@ public class AcceptOrgUserCommandTests
|
||||
/// - Provides mock data for an admin to validate email functionality.
|
||||
/// - Returns the corresponding organization for the given org ID.
|
||||
/// </summary>
|
||||
private void SetupCommonAcceptOrgUserMocks(SutProvider<AcceptOrgUserCommand> sutProvider, User user,
|
||||
private static void SetupCommonAcceptOrgUserMocks(SutProvider<AcceptOrgUserCommand> sutProvider, User user,
|
||||
Organization org,
|
||||
OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails)
|
||||
{
|
||||
@@ -729,18 +805,12 @@ public class AcceptOrgUserCommandTests
|
||||
// User is not part of any other orgs
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns(
|
||||
Task.FromResult<ICollection<OrganizationUser>>(new List<OrganizationUser>())
|
||||
);
|
||||
.Returns([]);
|
||||
|
||||
// Org they are trying to join does not have single org policy
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg, OrganizationUserStatusType.Invited)
|
||||
.Returns(
|
||||
Task.FromResult<ICollection<OrganizationUserPolicyDetails>>(
|
||||
new List<OrganizationUserPolicyDetails>()
|
||||
)
|
||||
);
|
||||
.Returns([]);
|
||||
|
||||
// User is not part of any organization that applies the single org policy
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
@@ -750,20 +820,24 @@ public class AcceptOrgUserCommandTests
|
||||
// Org does not require 2FA
|
||||
sutProvider.GetDependency<IPolicyService>().GetPoliciesApplicableToUserAsync(user.Id,
|
||||
PolicyType.TwoFactorAuthentication, OrganizationUserStatusType.Invited)
|
||||
.Returns(Task.FromResult<ICollection<OrganizationUserPolicyDetails>>(
|
||||
new List<OrganizationUserPolicyDetails>()));
|
||||
.Returns([]);
|
||||
|
||||
// Provide at least 1 admin to test email functionality
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByMinimumRoleAsync(orgUser.OrganizationId, OrganizationUserType.Admin)
|
||||
.Returns(Task.FromResult<IEnumerable<OrganizationUserUserDetails>>(
|
||||
new List<OrganizationUserUserDetails>() { adminUserDetails }
|
||||
));
|
||||
.Returns([adminUserDetails]);
|
||||
|
||||
// Return org
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(org.Id)
|
||||
.Returns(Task.FromResult(org));
|
||||
.Returns(org);
|
||||
|
||||
// Auto-confirm enforcement query returns valid by default (no restrictions)
|
||||
var request = new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser], user);
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(request)
|
||||
.Returns(Valid(request));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.AutoConfirmUser;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
@@ -12,6 +13,7 @@ using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Test.AdminConsole.AutoFixture;
|
||||
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
||||
using Bit.Core.Test.AutoFixture.OrganizationUserFixtures;
|
||||
@@ -19,6 +21,7 @@ using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using static Bit.Core.AdminConsole.Utilities.v2.Validation.ValidationResultHelpers;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.AutoConfirmUsers;
|
||||
|
||||
@@ -116,11 +119,11 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
|
||||
SutProvider<AutomaticallyConfirmOrganizationUsersValidator> sutProvider,
|
||||
[Organization(useAutomaticUserConfirmation: true, planType: PlanType.EnterpriseAnnually)] Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
|
||||
Guid userId,
|
||||
User user,
|
||||
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = userId;
|
||||
organizationUser.UserId = user.Id;
|
||||
organizationUser.OrganizationId = organization.Id;
|
||||
|
||||
var request = new AutomaticallyConfirmOrganizationUserValidationRequest
|
||||
@@ -140,12 +143,23 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns([(userId, true)]);
|
||||
.Returns([(user.Id, true)]);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([organizationUser]);
|
||||
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>())
|
||||
.Returns(Valid(
|
||||
new AutomaticUserConfirmationPolicyEnforcementRequest(organization.Id,
|
||||
[organizationUser],
|
||||
user)));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync(request);
|
||||
|
||||
@@ -319,11 +333,11 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
|
||||
SutProvider<AutomaticallyConfirmOrganizationUsersValidator> sutProvider,
|
||||
[Organization(useAutomaticUserConfirmation: true)] Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
|
||||
Guid userId,
|
||||
User user,
|
||||
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = userId;
|
||||
organizationUser.UserId = user.Id;
|
||||
organizationUser.OrganizationId = organization.Id;
|
||||
|
||||
var request = new AutomaticallyConfirmOrganizationUserValidationRequest
|
||||
@@ -343,12 +357,24 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns([(userId, true)]);
|
||||
.Returns([(user.Id, true)]);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([organizationUser]);
|
||||
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>())
|
||||
.Returns(Valid(
|
||||
new AutomaticUserConfirmationPolicyEnforcementRequest(organization.Id,
|
||||
[organizationUser],
|
||||
user)));
|
||||
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync(request);
|
||||
|
||||
@@ -362,11 +388,11 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
|
||||
SutProvider<AutomaticallyConfirmOrganizationUsersValidator> sutProvider,
|
||||
[Organization(useAutomaticUserConfirmation: true)] Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
|
||||
Guid userId,
|
||||
User user,
|
||||
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = userId;
|
||||
organizationUser.UserId = user.Id;
|
||||
organizationUser.OrganizationId = organization.Id;
|
||||
|
||||
var request = new AutomaticallyConfirmOrganizationUserValidationRequest
|
||||
@@ -386,16 +412,28 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns([(userId, false)]);
|
||||
.Returns([(user.Id, false)]);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(userId)
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||
.Returns(new RequireTwoFactorPolicyRequirement([])); // No 2FA policy
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([organizationUser]);
|
||||
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>())
|
||||
.Returns(Valid(
|
||||
new AutomaticUserConfirmationPolicyEnforcementRequest(organization.Id,
|
||||
[organizationUser],
|
||||
user)));
|
||||
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync(request);
|
||||
|
||||
@@ -403,128 +441,17 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
|
||||
Assert.True(result.IsValid);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_UserInMultipleOrgs_WithSingleOrgPolicyOnThisOrg_ReturnsError(
|
||||
SutProvider<AutomaticallyConfirmOrganizationUsersValidator> sutProvider,
|
||||
[Organization(useAutomaticUserConfirmation: true)] Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
|
||||
OrganizationUser otherOrgUser,
|
||||
Guid userId,
|
||||
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = userId;
|
||||
organizationUser.OrganizationId = organization.Id;
|
||||
|
||||
var request = new AutomaticallyConfirmOrganizationUserValidationRequest
|
||||
{
|
||||
PerformedBy = Substitute.For<IActingUser>(),
|
||||
DefaultUserCollectionName = "test-collection",
|
||||
OrganizationUser = organizationUser,
|
||||
OrganizationUserId = organizationUser.Id,
|
||||
Organization = organization,
|
||||
OrganizationId = organization.Id,
|
||||
Key = "test-key"
|
||||
};
|
||||
|
||||
var singleOrgPolicyDetails = new PolicyDetails
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
PolicyType = PolicyType.SingleOrg
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IPolicyRepository>()
|
||||
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation)
|
||||
.Returns(autoConfirmPolicy);
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns([(userId, true)]);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.Returns([organizationUser, otherOrgUser]);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<SingleOrganizationPolicyRequirement>(userId)
|
||||
.Returns(new SingleOrganizationPolicyRequirement([singleOrgPolicyDetails]));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsError);
|
||||
Assert.IsType<OrganizationEnforcesSingleOrgPolicy>(result.AsError);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_UserInMultipleOrgs_WithSingleOrgPolicyOnOtherOrg_ReturnsError(
|
||||
SutProvider<AutomaticallyConfirmOrganizationUsersValidator> sutProvider,
|
||||
[Organization(useAutomaticUserConfirmation: true)] Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
|
||||
OrganizationUser otherOrgUser,
|
||||
Guid userId,
|
||||
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = userId;
|
||||
organizationUser.OrganizationId = organization.Id;
|
||||
|
||||
var request = new AutomaticallyConfirmOrganizationUserValidationRequest
|
||||
{
|
||||
PerformedBy = Substitute.For<IActingUser>(),
|
||||
DefaultUserCollectionName = "test-collection",
|
||||
OrganizationUser = organizationUser,
|
||||
OrganizationUserId = organizationUser.Id,
|
||||
Organization = organization,
|
||||
OrganizationId = organization.Id,
|
||||
Key = "test-key"
|
||||
};
|
||||
|
||||
var otherOrgId = Guid.NewGuid(); // Different org
|
||||
var singleOrgPolicyDetails = new PolicyDetails
|
||||
{
|
||||
OrganizationId = otherOrgId,
|
||||
PolicyType = PolicyType.SingleOrg,
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IPolicyRepository>()
|
||||
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation)
|
||||
.Returns(autoConfirmPolicy);
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns([(userId, true)]);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.Returns([organizationUser, otherOrgUser]);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<SingleOrganizationPolicyRequirement>(userId)
|
||||
.Returns(new SingleOrganizationPolicyRequirement([singleOrgPolicyDetails]));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsError);
|
||||
Assert.IsType<OtherOrganizationEnforcesSingleOrgPolicy>(result.AsError);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_UserInSingleOrg_ReturnsValidResult(
|
||||
SutProvider<AutomaticallyConfirmOrganizationUsersValidator> sutProvider,
|
||||
[Organization(useAutomaticUserConfirmation: true)] Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
|
||||
Guid userId,
|
||||
User user,
|
||||
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = userId;
|
||||
organizationUser.UserId = user.Id;
|
||||
organizationUser.OrganizationId = organization.Id;
|
||||
|
||||
var request = new AutomaticallyConfirmOrganizationUserValidationRequest
|
||||
@@ -544,61 +471,22 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns([(userId, true)]);
|
||||
.Returns([(user.Id, true)]);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([organizationUser]); // Single org
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync(request);
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsValid);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_UserInMultipleOrgs_WithNoSingleOrgPolicy_ReturnsValidResult(
|
||||
SutProvider<AutomaticallyConfirmOrganizationUsersValidator> sutProvider,
|
||||
[Organization(useAutomaticUserConfirmation: true)] Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
|
||||
OrganizationUser otherOrgUser,
|
||||
Guid userId,
|
||||
Policy autoConfirmPolicy)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = userId;
|
||||
organizationUser.OrganizationId = organization.Id;
|
||||
autoConfirmPolicy.Type = PolicyType.AutomaticUserConfirmation;
|
||||
autoConfirmPolicy.Enabled = true;
|
||||
|
||||
var request = new AutomaticallyConfirmOrganizationUserValidationRequest
|
||||
{
|
||||
PerformedBy = Substitute.For<IActingUser>(),
|
||||
DefaultUserCollectionName = "test-collection",
|
||||
OrganizationUser = organizationUser,
|
||||
OrganizationUserId = organizationUser.Id,
|
||||
Organization = organization,
|
||||
OrganizationId = organization.Id,
|
||||
Key = "test-key"
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IPolicyRepository>()
|
||||
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation)
|
||||
.Returns(autoConfirmPolicy);
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns([(userId, true)]);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.Returns([organizationUser, otherOrgUser]);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<SingleOrganizationPolicyRequirement>(userId)
|
||||
.Returns(new SingleOrganizationPolicyRequirement([]));
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>())
|
||||
.Returns(Valid(
|
||||
new AutomaticUserConfirmationPolicyEnforcementRequest(organization.Id,
|
||||
[organizationUser],
|
||||
user)));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync(request);
|
||||
@@ -693,4 +581,59 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
|
||||
Assert.True(result.IsError);
|
||||
Assert.IsType<AutomaticallyConfirmUsersPolicyIsNotEnabled>(result.AsError);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_WithNonProviderUser_ReturnsValidResult(
|
||||
SutProvider<AutomaticallyConfirmOrganizationUsersValidator> sutProvider,
|
||||
[Organization(useAutomaticUserConfirmation: true)] Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
|
||||
User user,
|
||||
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = user.Id;
|
||||
organizationUser.OrganizationId = organization.Id;
|
||||
|
||||
var request = new AutomaticallyConfirmOrganizationUserValidationRequest
|
||||
{
|
||||
PerformedBy = Substitute.For<IActingUser>(),
|
||||
DefaultUserCollectionName = "test-collection",
|
||||
OrganizationUser = organizationUser,
|
||||
OrganizationUserId = organizationUser.Id,
|
||||
Organization = organization,
|
||||
OrganizationId = organization.Id,
|
||||
Key = "test-key"
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IPolicyRepository>()
|
||||
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation)
|
||||
.Returns(autoConfirmPolicy);
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns([(user.Id, true)]);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([organizationUser]);
|
||||
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>())
|
||||
.Returns(Valid(
|
||||
new AutomaticUserConfirmationPolicyEnforcementRequest(organization.Id,
|
||||
[organizationUser],
|
||||
user)));
|
||||
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsValid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.AutoConfirmUser;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
@@ -21,6 +23,7 @@ using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using static Bit.Core.AdminConsole.Utilities.v2.Validation.ValidationResultHelpers;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||
|
||||
@@ -559,4 +562,256 @@ public class ConfirmOrganizationUserCommandTests
|
||||
.DidNotReceive()
|
||||
.UpsertDefaultCollectionsAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>(), Arg.Any<string>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ConfirmUserAsync_WithAutoConfirmEnabledAndUserBelongsToAnotherOrg_ThrowsBadRequest(
|
||||
Organization org, OrganizationUser confirmingUser,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
||||
OrganizationUser otherOrgUser, string key, SutProvider<ConfirmOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
org.PlanType = PlanType.EnterpriseAnnually;
|
||||
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
||||
orgUser.UserId = user.Id;
|
||||
otherOrgUser.UserId = user.Id;
|
||||
otherOrgUser.OrganizationId = Guid.NewGuid(); // Different org
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync([]).ReturnsForAnyArgs([orgUser]);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByManyUsersAsync([])
|
||||
.ReturnsForAnyArgs([orgUser, otherOrgUser]);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(org.Id).Returns(org);
|
||||
sutProvider.GetDependency<IUserRepository>().GetManyAsync([]).ReturnsForAnyArgs([user]);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>())
|
||||
.Returns(Invalid(
|
||||
new AutomaticUserConfirmationPolicyEnforcementRequest(orgUser.Id, [orgUser, otherOrgUser], user),
|
||||
new UserCannotBelongToAnotherOrganization()));
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id));
|
||||
|
||||
Assert.Equal(new UserCannotBelongToAnotherOrganization().Message, exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ConfirmUserAsync_WithAutoConfirmEnabledForOtherOrg_ThrowsBadRequest(
|
||||
Organization org, OrganizationUser confirmingUser,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
||||
OrganizationUser otherOrgUser, string key, SutProvider<ConfirmOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
org.PlanType = PlanType.EnterpriseAnnually;
|
||||
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
||||
orgUser.UserId = user.Id;
|
||||
otherOrgUser.UserId = user.Id;
|
||||
otherOrgUser.OrganizationId = Guid.NewGuid();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync([]).ReturnsForAnyArgs([orgUser]);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByManyUsersAsync([])
|
||||
.ReturnsForAnyArgs([orgUser, otherOrgUser]);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(org.Id).Returns(org);
|
||||
sutProvider.GetDependency<IUserRepository>().GetManyAsync([]).ReturnsForAnyArgs([user]);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>())
|
||||
.Returns(Invalid(
|
||||
new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser, otherOrgUser], user),
|
||||
new OtherOrganizationDoesNotAllowOtherMembership()));
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id));
|
||||
|
||||
Assert.Equal(new OtherOrganizationDoesNotAllowOtherMembership().Message, exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ConfirmUserAsync_WithAutoConfirmEnabledAndUserIsProvider_ThrowsBadRequest(
|
||||
Organization org, OrganizationUser confirmingUser,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
||||
string key, SutProvider<ConfirmOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
org.PlanType = PlanType.EnterpriseAnnually;
|
||||
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
||||
orgUser.UserId = user.Id;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync([]).ReturnsForAnyArgs([orgUser]);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByManyUsersAsync([])
|
||||
.ReturnsForAnyArgs([orgUser]);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(org.Id).Returns(org);
|
||||
sutProvider.GetDependency<IUserRepository>().GetManyAsync([]).ReturnsForAnyArgs([user]);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>())
|
||||
.Returns(Invalid(
|
||||
new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser], user),
|
||||
new ProviderUsersCannotJoin()));
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id));
|
||||
|
||||
Assert.Equal(new ProviderUsersCannotJoin().Message, exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ConfirmUserAsync_WithAutoConfirmNotApplicable_Succeeds(
|
||||
Organization org, OrganizationUser confirmingUser,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
||||
string key, SutProvider<ConfirmOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
org.PlanType = PlanType.EnterpriseAnnually;
|
||||
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
||||
orgUser.UserId = user.Id;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync([]).ReturnsForAnyArgs([orgUser]);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByManyUsersAsync([])
|
||||
.ReturnsForAnyArgs([orgUser]);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(org.Id).Returns(org);
|
||||
sutProvider.GetDependency<IUserRepository>().GetManyAsync([]).ReturnsForAnyArgs([user]);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>())
|
||||
.Returns(Valid(new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser], user)));
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IEventService>()
|
||||
.Received(1).LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Confirmed);
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1).SendOrganizationConfirmedEmailAsync(org.DisplayName(), user.Email, orgUser.AccessSecretsManager);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ConfirmUserAsync_WithAutoConfirmValidationBeforeSingleOrgPolicy_ChecksAutoConfirmFirst(
|
||||
Organization org, OrganizationUser confirmingUser,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
||||
OrganizationUser otherOrgUser,
|
||||
[OrganizationUserPolicyDetails(PolicyType.SingleOrg)] OrganizationUserPolicyDetails singleOrgPolicy,
|
||||
string key, SutProvider<ConfirmOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
// Arrange - Setup conditions that would fail BOTH auto-confirm AND single org policy
|
||||
org.PlanType = PlanType.EnterpriseAnnually;
|
||||
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
||||
orgUser.UserId = user.Id;
|
||||
otherOrgUser.UserId = user.Id;
|
||||
otherOrgUser.OrganizationId = Guid.NewGuid();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync([]).ReturnsForAnyArgs([orgUser]);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByManyUsersAsync([])
|
||||
.ReturnsForAnyArgs([orgUser, otherOrgUser]);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(org.Id).Returns(org);
|
||||
sutProvider.GetDependency<IUserRepository>().GetManyAsync([]).ReturnsForAnyArgs([user]);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)
|
||||
.Returns(true);
|
||||
|
||||
singleOrgPolicy.OrganizationId = org.Id;
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg)
|
||||
.Returns([singleOrgPolicy]);
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Any<AutomaticUserConfirmationPolicyEnforcementRequest>())
|
||||
.Returns(Invalid(
|
||||
new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser, otherOrgUser], user),
|
||||
new UserCannotBelongToAnotherOrganization()));
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id));
|
||||
|
||||
Assert.Equal(new UserCannotBelongToAnotherOrganization().Message, exception.Message);
|
||||
Assert.NotEqual("Cannot confirm this member to the organization until they leave or remove all other organizations.",
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ConfirmUsersAsync_WithAutoConfirmEnabled_MixedResults(
|
||||
Organization org, OrganizationUser confirmingUser,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser1,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser2,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser3,
|
||||
OrganizationUser otherOrgUser, User user1, User user2, User user3,
|
||||
string key, SutProvider<ConfirmOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
org.PlanType = PlanType.EnterpriseAnnually;
|
||||
orgUser1.OrganizationId = orgUser2.OrganizationId = orgUser3.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
||||
orgUser1.UserId = user1.Id;
|
||||
orgUser2.UserId = user2.Id;
|
||||
orgUser3.UserId = user3.Id;
|
||||
otherOrgUser.UserId = user3.Id;
|
||||
otherOrgUser.OrganizationId = Guid.NewGuid();
|
||||
|
||||
var orgUsers = new[] { orgUser1, orgUser2, orgUser3 };
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync([]).ReturnsForAnyArgs(orgUsers);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(org.Id).Returns(org);
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetManyAsync([]).ReturnsForAnyArgs([user1, user2, user3]);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByManyUsersAsync([])
|
||||
.ReturnsForAnyArgs([orgUser1, orgUser2, orgUser3, otherOrgUser]);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Is<AutomaticUserConfirmationPolicyEnforcementRequest>(r => r.User.Id == user1.Id))
|
||||
.Returns(Valid(new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser1], user1)));
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Is<AutomaticUserConfirmationPolicyEnforcementRequest>(r => r.User.Id == user2.Id))
|
||||
.Returns(Valid(new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser2], user2)));
|
||||
|
||||
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementValidator>()
|
||||
.IsCompliantAsync(Arg.Is<AutomaticUserConfirmationPolicyEnforcementRequest>(r => r.User.Id == user3.Id))
|
||||
.Returns(Invalid(
|
||||
new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser3, otherOrgUser], user3),
|
||||
new OtherOrganizationDoesNotAllowOtherMembership()));
|
||||
|
||||
var keys = orgUsers.ToDictionary(ou => ou.Id, _ => key);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ConfirmUsersAsync(confirmingUser.OrganizationId, keys, confirmingUser.Id);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Empty(result[0].Item2);
|
||||
Assert.Empty(result[1].Item2);
|
||||
Assert.Equal(new OtherOrganizationDoesNotAllowOtherMembership().Message, result[2].Item2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
using Bit.Core.AdminConsole.Entities.Provider;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.AutoConfirmUser;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class AutomaticUserConfirmationPolicyEnforcementValidatorTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task IsCompliantAsync_WithPolicyEnabledAndUserIsProviderMember_ReturnsProviderUsersCannotJoinError(
|
||||
SutProvider<AutomaticUserConfirmationPolicyEnforcementValidator> sutProvider,
|
||||
OrganizationUser organizationUser,
|
||||
ProviderUser providerUser,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = providerUser.UserId = user.Id;
|
||||
|
||||
var policyDetails = new PolicyDetails
|
||||
{
|
||||
OrganizationId = organizationUser.OrganizationId,
|
||||
PolicyType = PolicyType.AutomaticUserConfirmation
|
||||
};
|
||||
|
||||
var request = new AutomaticUserConfirmationPolicyEnforcementRequest(
|
||||
organizationUser.OrganizationId,
|
||||
[organizationUser],
|
||||
user);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id)
|
||||
.Returns(new AutomaticUserConfirmationPolicyRequirement([policyDetails]));
|
||||
|
||||
sutProvider.GetDependency<IProviderUserRepository>()
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([providerUser]);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.IsCompliantAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsError);
|
||||
Assert.IsType<ProviderUsersCannotJoin>(result.AsError);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task IsCompliantAsync_WithPolicyEnabledOnOtherOrganization_ReturnsOtherOrganizationDoesNotAllowOtherMembershipError(
|
||||
SutProvider<AutomaticUserConfirmationPolicyEnforcementValidator> sutProvider,
|
||||
OrganizationUser organizationUser,
|
||||
OrganizationUser otherOrganizationUser,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = user.Id;
|
||||
otherOrganizationUser.UserId = user.Id;
|
||||
|
||||
var otherOrgId = Guid.NewGuid();
|
||||
var policyDetails = new PolicyDetails
|
||||
{
|
||||
OrganizationId = otherOrgId, // Different from organizationUser.OrganizationId
|
||||
PolicyType = PolicyType.AutomaticUserConfirmation
|
||||
};
|
||||
|
||||
var request = new AutomaticUserConfirmationPolicyEnforcementRequest(
|
||||
organizationUser.OrganizationId,
|
||||
[organizationUser, otherOrganizationUser],
|
||||
user);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id)
|
||||
.Returns(new AutomaticUserConfirmationPolicyRequirement([policyDetails]));
|
||||
|
||||
sutProvider.GetDependency<IProviderUserRepository>()
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([]);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.IsCompliantAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsError);
|
||||
Assert.IsType<OtherOrganizationDoesNotAllowOtherMembership>(result.AsError);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task IsCompliantAsync_WithPolicyDisabledUserIsAMemberOfAnotherOrgReturnsValid(
|
||||
SutProvider<AutomaticUserConfirmationPolicyEnforcementValidator> sutProvider,
|
||||
OrganizationUser organizationUser,
|
||||
OrganizationUser otherOrgUser,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = user.Id;
|
||||
otherOrgUser.UserId = user.Id;
|
||||
|
||||
var request = new AutomaticUserConfirmationPolicyEnforcementRequest(
|
||||
organizationUser.OrganizationId,
|
||||
[organizationUser, otherOrgUser],
|
||||
user);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id)
|
||||
.Returns(new AutomaticUserConfirmationPolicyRequirement([]));
|
||||
|
||||
sutProvider.GetDependency<IProviderUserRepository>()
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([]);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.IsCompliantAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsValid);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task IsCompliantAsync_WithPolicyEnabledUserIsAMemberOfAnotherOrg_ReturnsCannotBeMemberOfAnotherOrgError(
|
||||
SutProvider<AutomaticUserConfirmationPolicyEnforcementValidator> sutProvider,
|
||||
OrganizationUser organizationUser,
|
||||
OrganizationUser otherOrgUser,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = user.Id;
|
||||
otherOrgUser.UserId = user.Id;
|
||||
|
||||
var request = new AutomaticUserConfirmationPolicyEnforcementRequest(
|
||||
organizationUser.OrganizationId,
|
||||
[organizationUser, otherOrgUser],
|
||||
user);
|
||||
|
||||
var policyDetails = new PolicyDetails
|
||||
{
|
||||
OrganizationId = organizationUser.OrganizationId,
|
||||
PolicyType = PolicyType.AutomaticUserConfirmation
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id)
|
||||
.Returns(new AutomaticUserConfirmationPolicyRequirement([policyDetails]));
|
||||
|
||||
sutProvider.GetDependency<IProviderUserRepository>()
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([]);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.IsCompliantAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsError);
|
||||
Assert.IsType<UserCannotBelongToAnotherOrganization>(result.AsError);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task IsCompliantAsync_WithPolicyEnabledAndChecksConditionsInCorrectOrder_ReturnsFirstFailure(
|
||||
SutProvider<AutomaticUserConfirmationPolicyEnforcementValidator> sutProvider,
|
||||
OrganizationUser organizationUser,
|
||||
OrganizationUser otherOrgUser,
|
||||
ProviderUser providerUser,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
var policyDetails = new PolicyDetails
|
||||
{
|
||||
OrganizationId = organizationUser.OrganizationId,
|
||||
PolicyType = PolicyType.AutomaticUserConfirmation,
|
||||
OrganizationUserId = organizationUser.Id
|
||||
};
|
||||
|
||||
var request = new AutomaticUserConfirmationPolicyEnforcementRequest(
|
||||
organizationUser.OrganizationId,
|
||||
[organizationUser, otherOrgUser],
|
||||
user);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id)
|
||||
.Returns(new AutomaticUserConfirmationPolicyRequirement([policyDetails]));
|
||||
|
||||
sutProvider.GetDependency<IProviderUserRepository>()
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([providerUser]);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.IsCompliantAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsError);
|
||||
Assert.IsType<CurrentOrganizationUserIsNotPresentInRequest>(result.AsError);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task IsCompliantAsync_WithPolicyIsEnabledNoOtherOrganizationsAndNotAProvider_ReturnsValid(
|
||||
SutProvider<AutomaticUserConfirmationPolicyEnforcementValidator> sutProvider,
|
||||
OrganizationUser organizationUser,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = user.Id;
|
||||
|
||||
var request = new AutomaticUserConfirmationPolicyEnforcementRequest(
|
||||
organizationUser.OrganizationId,
|
||||
[organizationUser],
|
||||
user);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id)
|
||||
.Returns(new AutomaticUserConfirmationPolicyRequirement([
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationUserId = organizationUser.Id,
|
||||
OrganizationId = organizationUser.OrganizationId,
|
||||
PolicyType = PolicyType.AutomaticUserConfirmation,
|
||||
}
|
||||
]));
|
||||
|
||||
sutProvider.GetDependency<IProviderUserRepository>()
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([]);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.IsCompliantAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsValid);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task IsCompliantAsync_WithPolicyDisabledForCurrentAndOtherOrg_ReturnsValid(
|
||||
SutProvider<AutomaticUserConfirmationPolicyEnforcementValidator> sutProvider,
|
||||
OrganizationUser organizationUser,
|
||||
OrganizationUser otherOrgUser,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
otherOrgUser.UserId = organizationUser.UserId = user.Id;
|
||||
|
||||
var request = new AutomaticUserConfirmationPolicyEnforcementRequest(
|
||||
organizationUser.OrganizationId,
|
||||
[organizationUser],
|
||||
user);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id)
|
||||
.Returns(new AutomaticUserConfirmationPolicyRequirement([]));
|
||||
|
||||
sutProvider.GetDependency<IProviderUserRepository>()
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([]);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.IsCompliantAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsValid);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task IsCompliantAsync_WithPolicyDisabledForCurrentAndOtherOrgAndIsProvider_ReturnsValid(
|
||||
SutProvider<AutomaticUserConfirmationPolicyEnforcementValidator> sutProvider,
|
||||
OrganizationUser organizationUser,
|
||||
OrganizationUser otherOrgUser,
|
||||
ProviderUser providerUser,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
providerUser.UserId = otherOrgUser.UserId = organizationUser.UserId = user.Id;
|
||||
|
||||
var request = new AutomaticUserConfirmationPolicyEnforcementRequest(
|
||||
organizationUser.OrganizationId,
|
||||
[organizationUser],
|
||||
user);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id)
|
||||
.Returns(new AutomaticUserConfirmationPolicyRequirement([]));
|
||||
|
||||
sutProvider.GetDependency<IProviderUserRepository>()
|
||||
.GetManyByUserAsync(user.Id)
|
||||
.Returns([providerUser]);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.IsCompliantAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsValid);
|
||||
}
|
||||
}
|
||||
@@ -1382,4 +1382,90 @@ public class RegisterUserCommandTests
|
||||
.Received(1)
|
||||
.SendOrganizationUserWelcomeEmailAsync(user, organization.DisplayName());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RegisterSSOAutoProvisionedUserAsync_WithBlockedDomain_ThrowsException(
|
||||
User user,
|
||||
Organization organization,
|
||||
SutProvider<RegisterUserCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.Email = "user@blocked-domain.com";
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.BlockClaimedDomainAccountCreation)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
||||
.HasVerifiedDomainWithBlockClaimedDomainPolicyAsync("blocked-domain.com", organization.Id)
|
||||
.Returns(true);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.RegisterSSOAutoProvisionedUserAsync(user, organization));
|
||||
Assert.Equal("This email address is claimed by an organization using Bitwarden.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RegisterSSOAutoProvisionedUserAsync_WithOwnClaimedDomain_Succeeds(
|
||||
User user,
|
||||
Organization organization,
|
||||
SutProvider<RegisterUserCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.Email = "user@company-domain.com";
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.BlockClaimedDomainAccountCreation)
|
||||
.Returns(true);
|
||||
|
||||
// Domain is claimed by THIS organization, so it should be allowed
|
||||
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
||||
.HasVerifiedDomainWithBlockClaimedDomainPolicyAsync("company-domain.com", organization.Id)
|
||||
.Returns(false); // Not blocked because organization.Id is excluded
|
||||
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.CreateUserAsync(user)
|
||||
.Returns(IdentityResult.Success);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.RegisterSSOAutoProvisionedUserAsync(user, organization);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Succeeded);
|
||||
await sutProvider.GetDependency<IUserService>()
|
||||
.Received(1)
|
||||
.CreateUserAsync(user);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RegisterSSOAutoProvisionedUserAsync_WithNonClaimedDomain_Succeeds(
|
||||
User user,
|
||||
Organization organization,
|
||||
SutProvider<RegisterUserCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.Email = "user@unclaimed-domain.com";
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.BlockClaimedDomainAccountCreation)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
||||
.HasVerifiedDomainWithBlockClaimedDomainPolicyAsync("unclaimed-domain.com", organization.Id)
|
||||
.Returns(false); // Domain is not claimed by any org
|
||||
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.CreateUserAsync(user)
|
||||
.Returns(IdentityResult.Success);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.RegisterSSOAutoProvisionedUserAsync(user, organization);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Succeeded);
|
||||
await sutProvider.GetDependency<IUserService>()
|
||||
.Received(1)
|
||||
.CreateUserAsync(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@ public class AccountsControllerTests : IClassFixture<IdentityApplicationFactory>
|
||||
[StringLength(1000), Required] string masterPasswordHash, [StringLength(50)] string masterPasswordHint, [Required] string userSymmetricKey,
|
||||
[Required] KeysRequestModel userAsymmetricKeys, int kdfMemory, int kdfParallelism)
|
||||
{
|
||||
userAsymmetricKeys.AccountKeys = null;
|
||||
// Localize substitutions to this test.
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
|
||||
@@ -202,6 +203,7 @@ public class AccountsControllerTests : IClassFixture<IdentityApplicationFactory>
|
||||
[StringLength(1000), Required] string masterPasswordHash, [StringLength(50)] string masterPasswordHint, [Required] string userSymmetricKey,
|
||||
[Required] KeysRequestModel userAsymmetricKeys, int kdfMemory, int kdfParallelism)
|
||||
{
|
||||
userAsymmetricKeys.AccountKeys = null;
|
||||
// Localize substitutions to this test.
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
localFactory.UpdateConfiguration("globalSettings:disableUserRegistration", "true");
|
||||
@@ -233,6 +235,7 @@ public class AccountsControllerTests : IClassFixture<IdentityApplicationFactory>
|
||||
[StringLength(1000)] string masterPasswordHash, [StringLength(50)] string masterPasswordHint, string userSymmetricKey,
|
||||
KeysRequestModel userAsymmetricKeys, int kdfMemory, int kdfParallelism)
|
||||
{
|
||||
userAsymmetricKeys.AccountKeys = null;
|
||||
|
||||
// Localize factory to just this test.
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
@@ -310,6 +313,7 @@ public class AccountsControllerTests : IClassFixture<IdentityApplicationFactory>
|
||||
[StringLength(1000)] string masterPasswordHash, [StringLength(50)] string masterPasswordHint, string userSymmetricKey,
|
||||
KeysRequestModel userAsymmetricKeys, int kdfMemory, int kdfParallelism, Guid orgSponsorshipId)
|
||||
{
|
||||
userAsymmetricKeys.AccountKeys = null;
|
||||
|
||||
// Localize factory to just this test.
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
@@ -386,6 +390,7 @@ public class AccountsControllerTests : IClassFixture<IdentityApplicationFactory>
|
||||
[StringLength(1000)] string masterPasswordHash, [StringLength(50)] string masterPasswordHint, string userSymmetricKey,
|
||||
KeysRequestModel userAsymmetricKeys, int kdfMemory, int kdfParallelism, EmergencyAccess emergencyAccess)
|
||||
{
|
||||
userAsymmetricKeys.AccountKeys = null;
|
||||
|
||||
// Localize factory to just this test.
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
@@ -455,6 +460,7 @@ public class AccountsControllerTests : IClassFixture<IdentityApplicationFactory>
|
||||
[StringLength(1000)] string masterPasswordHash, [StringLength(50)] string masterPasswordHint, string userSymmetricKey,
|
||||
KeysRequestModel userAsymmetricKeys, int kdfMemory, int kdfParallelism)
|
||||
{
|
||||
userAsymmetricKeys.AccountKeys = null;
|
||||
|
||||
// Localize factory to just this test.
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
|
||||
@@ -24,6 +24,13 @@ namespace Bit.Identity.IntegrationTest.Endpoints;
|
||||
[SutProviderCustomize]
|
||||
public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
||||
{
|
||||
private static readonly KeysRequestModel TEST_ACCOUNT_KEYS = new KeysRequestModel
|
||||
{
|
||||
AccountKeys = null,
|
||||
PublicKey = "public-key",
|
||||
EncryptedPrivateKey = "encrypted-private-key",
|
||||
};
|
||||
|
||||
private const int SecondsInMinute = 60;
|
||||
private const int MinutesInHour = 60;
|
||||
private const int SecondsInHour = SecondsInMinute * MinutesInHour;
|
||||
@@ -64,6 +71,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
||||
[Theory, BitAutoData, RegisterFinishRequestModelCustomize]
|
||||
public async Task TokenEndpoint_GrantTypePassword_Success(RegisterFinishRequestModel requestModel)
|
||||
{
|
||||
requestModel.UserAsymmetricKeys = TEST_ACCOUNT_KEYS;
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
var user = await localFactory.RegisterNewIdentityFactoryUserAsync(requestModel);
|
||||
|
||||
@@ -89,6 +97,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
||||
public async Task TokenEndpoint_GrantTypePassword_WithAllUserTypes_WithSsoPolicyDisabled_WithEnforceSsoPolicyForAllUsersTrue_Success(
|
||||
OrganizationUserType organizationUserType, RegisterFinishRequestModel requestModel, Guid organizationId, int generatedUsername)
|
||||
{
|
||||
requestModel.UserAsymmetricKeys = TEST_ACCOUNT_KEYS;
|
||||
requestModel.Email = $"{generatedUsername}@example.com";
|
||||
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
@@ -114,6 +123,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
||||
public async Task TokenEndpoint_GrantTypePassword_WithAllUserTypes_WithSsoPolicyDisabled_WithEnforceSsoPolicyForAllUsersFalse_Success(
|
||||
OrganizationUserType organizationUserType, RegisterFinishRequestModel requestModel, Guid organizationId, int generatedUsername)
|
||||
{
|
||||
requestModel.UserAsymmetricKeys = TEST_ACCOUNT_KEYS;
|
||||
requestModel.Email = $"{generatedUsername}@example.com";
|
||||
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
@@ -140,6 +150,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
||||
public async Task TokenEndpoint_GrantTypePassword_WithAllUserTypes_WithSsoPolicyEnabled_WithEnforceSsoPolicyForAllUsersTrue_Throw(
|
||||
OrganizationUserType organizationUserType, RegisterFinishRequestModel requestModel, Guid organizationId, int generatedUsername)
|
||||
{
|
||||
requestModel.UserAsymmetricKeys = TEST_ACCOUNT_KEYS;
|
||||
requestModel.Email = $"{generatedUsername}@example.com";
|
||||
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
@@ -163,6 +174,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
||||
public async Task TokenEndpoint_GrantTypePassword_WithOwnerOrAdmin_WithSsoPolicyEnabled_WithEnforceSsoPolicyForAllUsersFalse_Success(
|
||||
OrganizationUserType organizationUserType, RegisterFinishRequestModel requestModel, Guid organizationId, int generatedUsername)
|
||||
{
|
||||
requestModel.UserAsymmetricKeys = TEST_ACCOUNT_KEYS;
|
||||
requestModel.Email = $"{generatedUsername}@example.com";
|
||||
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
@@ -186,6 +198,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
||||
public async Task TokenEndpoint_GrantTypePassword_WithNonOwnerOrAdmin_WithSsoPolicyEnabled_WithEnforceSsoPolicyForAllUsersFalse_Throws(
|
||||
OrganizationUserType organizationUserType, RegisterFinishRequestModel requestModel, Guid organizationId, int generatedUsername)
|
||||
{
|
||||
requestModel.UserAsymmetricKeys = TEST_ACCOUNT_KEYS;
|
||||
requestModel.Email = $"{generatedUsername}@example.com";
|
||||
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
@@ -207,6 +220,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
||||
[Theory, BitAutoData, RegisterFinishRequestModelCustomize]
|
||||
public async Task TokenEndpoint_GrantTypeRefreshToken_Success(RegisterFinishRequestModel requestModel)
|
||||
{
|
||||
requestModel.UserAsymmetricKeys = TEST_ACCOUNT_KEYS;
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
|
||||
var user = await localFactory.RegisterNewIdentityFactoryUserAsync(requestModel);
|
||||
@@ -229,6 +243,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
||||
[Theory, BitAutoData, RegisterFinishRequestModelCustomize]
|
||||
public async Task TokenEndpoint_GrantTypeClientCredentials_Success(RegisterFinishRequestModel model)
|
||||
{
|
||||
model.UserAsymmetricKeys = TEST_ACCOUNT_KEYS;
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
var user = await localFactory.RegisterNewIdentityFactoryUserAsync(model);
|
||||
|
||||
@@ -253,6 +268,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
||||
RegisterFinishRequestModel model,
|
||||
string deviceId)
|
||||
{
|
||||
model.UserAsymmetricKeys.AccountKeys = null;
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
var server = localFactory.WithWebHostBuilder(builder =>
|
||||
{
|
||||
@@ -456,6 +472,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
||||
public async Task TokenEndpoint_TooQuickInOneSecond_BlockRequest(
|
||||
RegisterFinishRequestModel requestModel)
|
||||
{
|
||||
requestModel.UserAsymmetricKeys = TEST_ACCOUNT_KEYS;
|
||||
const int AmountInOneSecondAllowed = 10;
|
||||
|
||||
// The rule we are testing is 10 requests in 1 second
|
||||
|
||||
Reference in New Issue
Block a user