1
0
mirror of https://github.com/bitwarden/server synced 2025-12-29 06:33:43 +00:00

Merge branch 'km/pm-10564' into km/pm-15084

# Conflicts:
#	src/Core/Enums/PushType.cs
This commit is contained in:
Maciej Zieniuk
2024-12-18 23:33:17 +00:00
365 changed files with 39467 additions and 3870 deletions

View File

@@ -1,6 +1,9 @@
using System.Text.Json;
using System.Security.Claims;
using System.Text.Json;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Enums;
@@ -9,13 +12,17 @@ using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Billing.Services;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data.Organizations;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Core.Vault.Repositories;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
@@ -62,6 +69,9 @@ public class UserServiceTests
sutProvider.GetDependency<ILicensingService>()
.VerifyLicense(userLicense)
.Returns(true);
sutProvider.GetDependency<ILicensingService>()
.GetClaimsPrincipalFromLicense(userLicense)
.Returns((ClaimsPrincipal)null);
await sutProvider.Sut.UpdateLicenseAsync(user, userLicense);
@@ -231,41 +241,7 @@ public class UserServiceTests
});
// HACK: SutProvider is being weird about not injecting the IPasswordHasher that I configured
var sut = new UserService(
sutProvider.GetDependency<IUserRepository>(),
sutProvider.GetDependency<ICipherRepository>(),
sutProvider.GetDependency<IOrganizationUserRepository>(),
sutProvider.GetDependency<IOrganizationRepository>(),
sutProvider.GetDependency<IMailService>(),
sutProvider.GetDependency<IPushNotificationService>(),
sutProvider.GetDependency<IUserStore<User>>(),
sutProvider.GetDependency<IOptions<IdentityOptions>>(),
sutProvider.GetDependency<IPasswordHasher<User>>(),
sutProvider.GetDependency<IEnumerable<IUserValidator<User>>>(),
sutProvider.GetDependency<IEnumerable<IPasswordValidator<User>>>(),
sutProvider.GetDependency<ILookupNormalizer>(),
sutProvider.GetDependency<IdentityErrorDescriber>(),
sutProvider.GetDependency<IServiceProvider>(),
sutProvider.GetDependency<ILogger<UserManager<User>>>(),
sutProvider.GetDependency<ILicensingService>(),
sutProvider.GetDependency<IEventService>(),
sutProvider.GetDependency<IApplicationCacheService>(),
sutProvider.GetDependency<IDataProtectionProvider>(),
sutProvider.GetDependency<IPaymentService>(),
sutProvider.GetDependency<IPolicyRepository>(),
sutProvider.GetDependency<IPolicyService>(),
sutProvider.GetDependency<IReferenceEventService>(),
sutProvider.GetDependency<IFido2>(),
sutProvider.GetDependency<ICurrentContext>(),
sutProvider.GetDependency<IGlobalSettings>(),
sutProvider.GetDependency<IAcceptOrgUserCommand>(),
sutProvider.GetDependency<IProviderUserRepository>(),
sutProvider.GetDependency<IStripeSyncService>(),
new FakeDataProtectorTokenFactory<OrgUserInviteTokenable>(),
sutProvider.GetDependency<IFeatureService>(),
sutProvider.GetDependency<IPremiumUserBillingService>(),
sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
);
var sut = RebuildSut(sutProvider);
var actualIsVerified = await sut.VerifySecretAsync(user, secret);
@@ -349,6 +325,262 @@ public class UserServiceTests
Assert.False(result);
}
[Theory, BitAutoData]
public async Task DisableTwoFactorProviderAsync_WhenOrganizationHas2FAPolicyEnabled_DisablingAllProviders_RemovesUserFromOrganizationAndSendsEmail(
SutProvider<UserService> sutProvider, User user, Organization organization)
{
// Arrange
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
[TwoFactorProviderType.Email] = new() { Enabled = true }
});
sutProvider.GetDependency<IPolicyService>()
.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication)
.Returns(
[
new OrganizationUserPolicyDetails
{
OrganizationId = organization.Id,
PolicyType = PolicyType.TwoFactorAuthentication,
PolicyEnabled = true
}
]);
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization.Id)
.Returns(organization);
var expectedSavedProviders = JsonHelpers.LegacySerialize(new Dictionary<TwoFactorProviderType, TwoFactorProvider>(), JsonHelpers.LegacyEnumKeyResolver);
// Act
await sutProvider.Sut.DisableTwoFactorProviderAsync(user, TwoFactorProviderType.Email);
// Assert
await sutProvider.GetDependency<IUserRepository>()
.Received(1)
.ReplaceAsync(Arg.Is<User>(u => u.Id == user.Id && u.TwoFactorProviders == expectedSavedProviders));
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogUserEventAsync(user.Id, EventType.User_Disabled2fa);
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
.Received(1)
.RemoveUserAsync(organization.Id, user.Id);
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), user.Email);
}
[Theory, BitAutoData]
public async Task DisableTwoFactorProviderAsync_WhenOrganizationHas2FAPolicyEnabled_UserHasOneProviderEnabled_DoesNotRemoveUserFromOrganization(
SutProvider<UserService> sutProvider, User user, Organization organization)
{
// Arrange
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
[TwoFactorProviderType.Email] = new() { Enabled = true },
[TwoFactorProviderType.Remember] = new() { Enabled = true }
});
sutProvider.GetDependency<IPolicyService>()
.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication)
.Returns(
[
new OrganizationUserPolicyDetails
{
OrganizationId = organization.Id,
PolicyType = PolicyType.TwoFactorAuthentication,
PolicyEnabled = true
}
]);
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization.Id)
.Returns(organization);
var expectedSavedProviders = JsonHelpers.LegacySerialize(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
[TwoFactorProviderType.Remember] = new() { Enabled = true }
}, JsonHelpers.LegacyEnumKeyResolver);
// Act
await sutProvider.Sut.DisableTwoFactorProviderAsync(user, TwoFactorProviderType.Email);
// Assert
await sutProvider.GetDependency<IUserRepository>()
.Received(1)
.ReplaceAsync(Arg.Is<User>(u => u.Id == user.Id && u.TwoFactorProviders == expectedSavedProviders));
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogUserEventAsync(user.Id, EventType.User_Disabled2fa);
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
.DidNotReceiveWithAnyArgs()
.RemoveUserAsync(default, default);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(default, default);
}
[Theory, BitAutoData]
public async Task DisableTwoFactorProviderAsync_WithAccountDeprovisioningEnabled_WhenOrganizationHas2FAPolicyEnabled_WhenUserIsManaged_DisablingAllProviders_RemovesOrRevokesUserAndSendsEmail(
SutProvider<UserService> sutProvider, User user, Organization organization1, Organization organization2)
{
// Arrange
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
[TwoFactorProviderType.Email] = new() { Enabled = true }
});
organization1.Enabled = organization2.Enabled = true;
organization1.UseSso = organization2.UseSso = true;
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
.Returns(true);
sutProvider.GetDependency<IPolicyService>()
.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication)
.Returns(
[
new OrganizationUserPolicyDetails
{
OrganizationId = organization1.Id,
PolicyType = PolicyType.TwoFactorAuthentication,
PolicyEnabled = true
},
new OrganizationUserPolicyDetails
{
OrganizationId = organization2.Id,
PolicyType = PolicyType.TwoFactorAuthentication,
PolicyEnabled = true
}
]);
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization1.Id)
.Returns(organization1);
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization2.Id)
.Returns(organization2);
sutProvider.GetDependency<IOrganizationRepository>()
.GetByVerifiedUserEmailDomainAsync(user.Id)
.Returns(new[] { organization1 });
var expectedSavedProviders = JsonHelpers.LegacySerialize(new Dictionary<TwoFactorProviderType, TwoFactorProvider>(), JsonHelpers.LegacyEnumKeyResolver);
// Act
await sutProvider.Sut.DisableTwoFactorProviderAsync(user, TwoFactorProviderType.Email);
// Assert
await sutProvider.GetDependency<IUserRepository>()
.Received(1)
.ReplaceAsync(Arg.Is<User>(u => u.Id == user.Id && u.TwoFactorProviders == expectedSavedProviders));
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogUserEventAsync(user.Id, EventType.User_Disabled2fa);
// Revoke the user from the first organization because they are managed by it
await sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
.Received(1)
.RevokeNonCompliantOrganizationUsersAsync(
Arg.Is<RevokeOrganizationUsersRequest>(r => r.OrganizationId == organization1.Id &&
r.OrganizationUsers.First().UserId == user.Id &&
r.OrganizationUsers.First().OrganizationId == organization1.Id));
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendOrganizationUserRevokedForTwoFactoryPolicyEmailAsync(organization1.DisplayName(), user.Email);
// Remove the user from the second organization because they are not managed by it
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
.Received(1)
.RemoveUserAsync(organization2.Id, user.Id);
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization2.DisplayName(), user.Email);
}
[Theory, BitAutoData]
public async Task ResendNewDeviceVerificationEmail_UserNull_SendOTPAsyncNotCalled(
SutProvider<UserService> sutProvider, string email, string secret)
{
sutProvider.GetDependency<IUserRepository>()
.GetByEmailAsync(email)
.Returns(null as User);
await sutProvider.Sut.ResendNewDeviceVerificationEmail(email, secret);
await sutProvider.GetDependency<IMailService>()
.DidNotReceive()
.SendOTPEmailAsync(Arg.Any<string>(), Arg.Any<string>());
}
[Theory, BitAutoData]
public async Task ResendNewDeviceVerificationEmail_SecretNotValid_SendOTPAsyncNotCalled(
SutProvider<UserService> sutProvider, string email, string secret)
{
sutProvider.GetDependency<IUserRepository>()
.GetByEmailAsync(email)
.Returns(null as User);
await sutProvider.Sut.ResendNewDeviceVerificationEmail(email, secret);
await sutProvider.GetDependency<IMailService>()
.DidNotReceive()
.SendOTPEmailAsync(Arg.Any<string>(), Arg.Any<string>());
}
[Theory, BitAutoData]
public async Task ResendNewDeviceVerificationEmail_SendsToken_Success(
SutProvider<UserService> sutProvider, User user)
{
// Arrange
var testPassword = "test_password";
var tokenProvider = SetupFakeTokenProvider(sutProvider, user);
SetupUserAndDevice(user, true);
// Setup the fake password verification
var substitutedUserPasswordStore = Substitute.For<IUserPasswordStore<User>>();
substitutedUserPasswordStore
.GetPasswordHashAsync(user, Arg.Any<CancellationToken>())
.Returns((ci) =>
{
return Task.FromResult("hashed_test_password");
});
sutProvider.SetDependency<IUserStore<User>>(substitutedUserPasswordStore, "store");
sutProvider.GetDependency<IPasswordHasher<User>>("passwordHasher")
.VerifyHashedPassword(user, "hashed_test_password", testPassword)
.Returns((ci) =>
{
return PasswordVerificationResult.Success;
});
sutProvider.GetDependency<IUserRepository>()
.GetByEmailAsync(user.Email)
.Returns(user);
// HACK: SutProvider is being weird about not injecting the IPasswordHasher that I configured
var sut = RebuildSut(sutProvider);
await sut.ResendNewDeviceVerificationEmail(user.Email, testPassword);
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendOTPEmailAsync(user.Email, Arg.Any<string>());
}
[Theory]
[BitAutoData("")]
[BitAutoData("null")]
public async Task SendOTPAsync_UserEmailNull_ThrowsBadRequest(
string email,
SutProvider<UserService> sutProvider, User user)
{
user.Email = email == "null" ? null : "";
var expectedMessage = "No user email.";
try
{
await sutProvider.Sut.SendOTPAsync(user);
}
catch (BadRequestException ex)
{
Assert.Equal(ex.Message, expectedMessage);
await sutProvider.GetDependency<IMailService>()
.DidNotReceive()
.SendOTPEmailAsync(Arg.Any<string>(), Arg.Any<string>());
}
}
private static void SetupUserAndDevice(User user,
bool shouldHavePassword)
{
@@ -400,4 +632,44 @@ public class UserServiceTests
return fakeUserTwoFactorProvider;
}
private IUserService RebuildSut(SutProvider<UserService> sutProvider)
{
return new UserService(
sutProvider.GetDependency<IUserRepository>(),
sutProvider.GetDependency<ICipherRepository>(),
sutProvider.GetDependency<IOrganizationUserRepository>(),
sutProvider.GetDependency<IOrganizationRepository>(),
sutProvider.GetDependency<IMailService>(),
sutProvider.GetDependency<IPushNotificationService>(),
sutProvider.GetDependency<IUserStore<User>>(),
sutProvider.GetDependency<IOptions<IdentityOptions>>(),
sutProvider.GetDependency<IPasswordHasher<User>>(),
sutProvider.GetDependency<IEnumerable<IUserValidator<User>>>(),
sutProvider.GetDependency<IEnumerable<IPasswordValidator<User>>>(),
sutProvider.GetDependency<ILookupNormalizer>(),
sutProvider.GetDependency<IdentityErrorDescriber>(),
sutProvider.GetDependency<IServiceProvider>(),
sutProvider.GetDependency<ILogger<UserManager<User>>>(),
sutProvider.GetDependency<ILicensingService>(),
sutProvider.GetDependency<IEventService>(),
sutProvider.GetDependency<IApplicationCacheService>(),
sutProvider.GetDependency<IDataProtectionProvider>(),
sutProvider.GetDependency<IPaymentService>(),
sutProvider.GetDependency<IPolicyRepository>(),
sutProvider.GetDependency<IPolicyService>(),
sutProvider.GetDependency<IReferenceEventService>(),
sutProvider.GetDependency<IFido2>(),
sutProvider.GetDependency<ICurrentContext>(),
sutProvider.GetDependency<IGlobalSettings>(),
sutProvider.GetDependency<IAcceptOrgUserCommand>(),
sutProvider.GetDependency<IProviderUserRepository>(),
sutProvider.GetDependency<IStripeSyncService>(),
new FakeDataProtectorTokenFactory<OrgUserInviteTokenable>(),
sutProvider.GetDependency<IFeatureService>(),
sutProvider.GetDependency<IPremiumUserBillingService>(),
sutProvider.GetDependency<IRemoveOrganizationUserCommand>(),
sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
);
}
}