mirror of
https://github.com/bitwarden/server
synced 2025-12-06 00:03:34 +00:00
fix(2fa): [PM-22323] Do not show 2FA warning for 2FA setup and login emails
* Added configuration to not display 2FA setup instruction * Refactored to new service. * Linting. * Dependency injection * Changed to scoped to have access to ICurrentContext. * Inverted logic for EmailTotpAction * Fixed tests. * Fixed tests. * More tests. * Fixed tests. * Linting. * Added tests at controller level. * Linting * Fixed error in test. * Review updates. * Accidentally deleted imports.
This commit is contained in:
@@ -4,6 +4,7 @@ using Bit.Api.Auth.Models.Request.Accounts;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.Models.Api.Request.Accounts;
|
||||
using Bit.Core.Auth.Services;
|
||||
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
|
||||
@@ -31,6 +32,8 @@ public class AccountsControllerTests : IDisposable
|
||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||
private readonly ITdeOffboardingPasswordCommand _tdeOffboardingPasswordCommand;
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly ITwoFactorEmailService _twoFactorEmailService;
|
||||
|
||||
|
||||
public AccountsControllerTests()
|
||||
{
|
||||
@@ -43,6 +46,8 @@ public class AccountsControllerTests : IDisposable
|
||||
_twoFactorIsEnabledQuery = Substitute.For<ITwoFactorIsEnabledQuery>();
|
||||
_tdeOffboardingPasswordCommand = Substitute.For<ITdeOffboardingPasswordCommand>();
|
||||
_featureService = Substitute.For<IFeatureService>();
|
||||
_twoFactorEmailService = Substitute.For<ITwoFactorEmailService>();
|
||||
|
||||
|
||||
_sut = new AccountsController(
|
||||
_organizationService,
|
||||
@@ -53,7 +58,8 @@ public class AccountsControllerTests : IDisposable
|
||||
_setInitialMasterPasswordCommand,
|
||||
_tdeOffboardingPasswordCommand,
|
||||
_twoFactorIsEnabledQuery,
|
||||
_featureService
|
||||
_featureService,
|
||||
_twoFactorEmailService
|
||||
);
|
||||
}
|
||||
|
||||
@@ -547,6 +553,46 @@ public class AccountsControllerTests : IDisposable
|
||||
Assert.Equal(model.VerifyDevices, user.VerifyDevices);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ResendNewDeviceVerificationEmail_WhenUserNotFound_ShouldFail(
|
||||
UnauthenticatedSecretVerificationRequestModel model)
|
||||
{
|
||||
// Arrange
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(Task.FromResult((User)null));
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => _sut.ResendNewDeviceOtpAsync(model));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ResendNewDeviceVerificationEmail_WhenSecretNotValid_ShouldFail(
|
||||
User user,
|
||||
UnauthenticatedSecretVerificationRequestModel model)
|
||||
{
|
||||
// Arrange
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(Task.FromResult(user));
|
||||
_userService.VerifySecretAsync(user, Arg.Any<string>()).Returns(Task.FromResult(false));
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => _sut.ResendNewDeviceOtpAsync(model));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ResendNewDeviceVerificationEmail_WhenTokenValid_SendsEmail(User user,
|
||||
UnauthenticatedSecretVerificationRequestModel model)
|
||||
{
|
||||
// Arrange
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(Task.FromResult(user));
|
||||
_userService.VerifySecretAsync(user, Arg.Any<string>()).Returns(Task.FromResult(true));
|
||||
|
||||
// Act
|
||||
await _sut.ResendNewDeviceOtpAsync(model);
|
||||
|
||||
// Assert
|
||||
await _twoFactorEmailService.Received(1).SendNewDeviceVerificationEmailAsync(user);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
254
test/Core.Test/Auth/Services/TwoFactorEmailServiceTests.cs
Normal file
254
test/Core.Test/Auth/Services/TwoFactorEmailServiceTests.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Models;
|
||||
using Bit.Core.Auth.Services;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Core.Auth.Enums;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Auth.Services;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class TwoFactorEmailServiceTests
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendTwoFactorEmailAsync_Success(SutProvider<TwoFactorEmailService> sutProvider, User user)
|
||||
{
|
||||
var email = user.Email.ToLowerInvariant();
|
||||
var token = "thisisatokentocompare";
|
||||
var IpAddress = "1.1.1.1";
|
||||
var deviceType = DeviceType.Android;
|
||||
|
||||
var context = sutProvider.GetDependency<ICurrentContext>();
|
||||
context.DeviceType = deviceType;
|
||||
context.IpAddress = IpAddress;
|
||||
|
||||
var userTwoFactorTokenProvider = Substitute.For<IUserTwoFactorTokenProvider<User>>();
|
||||
userTwoFactorTokenProvider
|
||||
.CanGenerateTwoFactorTokenAsync(Arg.Any<UserManager<User>>(), user)
|
||||
.Returns(Task.FromResult(true));
|
||||
userTwoFactorTokenProvider
|
||||
.GenerateAsync("TwoFactor", Arg.Any<UserManager<User>>(), user)
|
||||
.Returns(Task.FromResult(token));
|
||||
|
||||
var userManager = sutProvider.GetDependency<UserManager<User>>();
|
||||
userManager.RegisterTokenProvider(CoreHelpers.CustomProviderName(TwoFactorProviderType.Email), userTwoFactorTokenProvider);
|
||||
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["Email"] = email },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
await sutProvider.Sut.SendTwoFactorEmailAsync(user);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendTwoFactorEmailAsync(email, user.Email, token, IpAddress, deviceType.ToString(),
|
||||
TwoFactorEmailPurpose.Login);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendTwoFactorSetupEmailAsync_Success(SutProvider<TwoFactorEmailService> sutProvider, User user)
|
||||
{
|
||||
var email = user.Email.ToLowerInvariant();
|
||||
var token = "thisisatokentocompare";
|
||||
var IpAddress = "1.1.1.1";
|
||||
var deviceType = DeviceType.Android;
|
||||
|
||||
var context = sutProvider.GetDependency<ICurrentContext>();
|
||||
context.DeviceType = deviceType;
|
||||
context.IpAddress = IpAddress;
|
||||
|
||||
var userTwoFactorTokenProvider = Substitute.For<IUserTwoFactorTokenProvider<User>>();
|
||||
userTwoFactorTokenProvider
|
||||
.CanGenerateTwoFactorTokenAsync(Arg.Any<UserManager<User>>(), user)
|
||||
.Returns(Task.FromResult(true));
|
||||
userTwoFactorTokenProvider
|
||||
.GenerateAsync("TwoFactor", Arg.Any<UserManager<User>>(), user)
|
||||
.Returns(Task.FromResult(token));
|
||||
|
||||
var userManager = sutProvider.GetDependency<UserManager<User>>();
|
||||
userManager.RegisterTokenProvider(CoreHelpers.CustomProviderName(TwoFactorProviderType.Email), userTwoFactorTokenProvider);
|
||||
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["Email"] = email },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
|
||||
await sutProvider.Sut.SendTwoFactorSetupEmailAsync(user);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendTwoFactorEmailAsync(email, user.Email, token, IpAddress, deviceType.ToString(),
|
||||
TwoFactorEmailPurpose.Setup);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendNewDeviceVerificationEmailAsync_Success(SutProvider<TwoFactorEmailService> sutProvider, User user)
|
||||
{
|
||||
var email = user.Email.ToLowerInvariant();
|
||||
var token = "thisisatokentocompare";
|
||||
var IpAddress = "1.1.1.1";
|
||||
var deviceType = DeviceType.Android;
|
||||
|
||||
var context = sutProvider.GetDependency<ICurrentContext>();
|
||||
context.DeviceType = deviceType;
|
||||
context.IpAddress = IpAddress;
|
||||
|
||||
var userTwoFactorTokenProvider = Substitute.For<IUserTwoFactorTokenProvider<User>>();
|
||||
userTwoFactorTokenProvider
|
||||
.CanGenerateTwoFactorTokenAsync(Arg.Any<UserManager<User>>(), user)
|
||||
.Returns(Task.FromResult(true));
|
||||
userTwoFactorTokenProvider
|
||||
.GenerateAsync("otp:" + user.Email, Arg.Any<UserManager<User>>(), user)
|
||||
.Returns(Task.FromResult(token));
|
||||
|
||||
var userManager = sutProvider.GetDependency<UserManager<User>>();
|
||||
userManager.RegisterTokenProvider(TokenOptions.DefaultEmailProvider, userTwoFactorTokenProvider);
|
||||
|
||||
await sutProvider.Sut.SendNewDeviceVerificationEmailAsync(user);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendTwoFactorEmailAsync(email, user.Email, token, IpAddress, deviceType.ToString(),
|
||||
TwoFactorEmailPurpose.NewDeviceVerification);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderOnUser(SutProvider<TwoFactorEmailService> sutProvider, User user)
|
||||
{
|
||||
user.TwoFactorProviders = null;
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderMetadataOnUser(SutProvider<TwoFactorEmailService> sutProvider, User user)
|
||||
{
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = null,
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderEmailMetadataOnUser(SutProvider<TwoFactorEmailService> sutProvider, User user)
|
||||
{
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["qweqwe"] = user.Email.ToLowerInvariant() },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendNewDeviceVerificationEmailAsync_ExceptionBecauseUserNull(SutProvider<TwoFactorEmailService> sutProvider)
|
||||
{
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(() => sutProvider.Sut.SendNewDeviceVerificationEmailAsync(null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(DeviceType.UnknownBrowser, "Unknown Browser")]
|
||||
[BitAutoData(DeviceType.Android, "Android")]
|
||||
public async Task SendTwoFactorEmailAsync_DeviceMatches(DeviceType deviceType, string deviceTypeName,
|
||||
SutProvider<TwoFactorEmailService> sutProvider,
|
||||
User user)
|
||||
{
|
||||
var email = user.Email.ToLowerInvariant();
|
||||
var token = "thisisatokentocompare";
|
||||
var IpAddress = "1.1.1.1";
|
||||
|
||||
var context = sutProvider.GetDependency<ICurrentContext>();
|
||||
context.DeviceType = deviceType;
|
||||
context.IpAddress = IpAddress;
|
||||
|
||||
var userTwoFactorTokenProvider = Substitute.For<IUserTwoFactorTokenProvider<User>>();
|
||||
userTwoFactorTokenProvider
|
||||
.CanGenerateTwoFactorTokenAsync(Arg.Any<UserManager<User>>(), user)
|
||||
.Returns(Task.FromResult(true));
|
||||
userTwoFactorTokenProvider
|
||||
.GenerateAsync("TwoFactor", Arg.Any<UserManager<User>>(), user)
|
||||
.Returns(Task.FromResult(token));
|
||||
|
||||
var userManager = sutProvider.GetDependency<UserManager<User>>();
|
||||
userManager.RegisterTokenProvider(CoreHelpers.CustomProviderName(TwoFactorProviderType.Email), userTwoFactorTokenProvider);
|
||||
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["Email"] = email },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
|
||||
await sutProvider.Sut.SendTwoFactorEmailAsync(user);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendTwoFactorEmailAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), deviceTypeName, TwoFactorEmailPurpose.Login);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendTwoFactorEmailAsync_NullDeviceTypeShouldSendUnkownBrowserType(SutProvider<TwoFactorEmailService> sutProvider, User user)
|
||||
{
|
||||
var email = user.Email.ToLowerInvariant();
|
||||
var token = "thisisatokentocompare";
|
||||
var IpAddress = "1.1.1.1";
|
||||
|
||||
var userTwoFactorTokenProvider = Substitute.For<IUserTwoFactorTokenProvider<User>>();
|
||||
userTwoFactorTokenProvider
|
||||
.CanGenerateTwoFactorTokenAsync(Arg.Any<UserManager<User>>(), user)
|
||||
.Returns(Task.FromResult(true));
|
||||
userTwoFactorTokenProvider
|
||||
.GenerateAsync("TwoFactor", Arg.Any<UserManager<User>>(), user)
|
||||
.Returns(Task.FromResult(token));
|
||||
|
||||
var context = Substitute.For<ICurrentContext>();
|
||||
context.DeviceType = null;
|
||||
context.IpAddress = IpAddress;
|
||||
|
||||
var userManager = sutProvider.GetDependency<UserManager<User>>();
|
||||
userManager.RegisterTokenProvider(CoreHelpers.CustomProviderName(TwoFactorProviderType.Email), userTwoFactorTokenProvider);
|
||||
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["Email"] = email },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
|
||||
await sutProvider.Sut.SendTwoFactorEmailAsync(user);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendTwoFactorEmailAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), "Unknown Browser", Arg.Any<TwoFactorEmailPurpose>());
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Models;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
@@ -84,125 +83,6 @@ public class UserServiceTests
|
||||
Assert.Equal(1, versionProp.GetInt32());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendTwoFactorEmailAsync_Success(SutProvider<UserService> sutProvider, User user)
|
||||
{
|
||||
var email = user.Email.ToLowerInvariant();
|
||||
var token = "thisisatokentocompare";
|
||||
var authentication = true;
|
||||
var IpAddress = "1.1.1.1";
|
||||
var deviceType = "Android";
|
||||
|
||||
var userTwoFactorTokenProvider = Substitute.For<IUserTwoFactorTokenProvider<User>>();
|
||||
userTwoFactorTokenProvider
|
||||
.CanGenerateTwoFactorTokenAsync(Arg.Any<UserManager<User>>(), user)
|
||||
.Returns(Task.FromResult(true));
|
||||
userTwoFactorTokenProvider
|
||||
.GenerateAsync("TwoFactor", Arg.Any<UserManager<User>>(), user)
|
||||
.Returns(Task.FromResult(token));
|
||||
|
||||
var context = sutProvider.GetDependency<ICurrentContext>();
|
||||
context.DeviceType = DeviceType.Android;
|
||||
context.IpAddress = IpAddress;
|
||||
|
||||
sutProvider.Sut.RegisterTokenProvider("Custom_Email", userTwoFactorTokenProvider);
|
||||
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["Email"] = email },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
await sutProvider.Sut.SendTwoFactorEmailAsync(user);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendTwoFactorEmailAsync(email, user.Email, token, IpAddress, deviceType, authentication);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderOnUser(SutProvider<UserService> sutProvider, User user)
|
||||
{
|
||||
user.TwoFactorProviders = null;
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderMetadataOnUser(SutProvider<UserService> sutProvider, User user)
|
||||
{
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = null,
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderEmailMetadataOnUser(SutProvider<UserService> sutProvider, User user)
|
||||
{
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["qweqwe"] = user.Email.ToLowerInvariant() },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendNewDeviceVerificationEmailAsync_ExceptionBecauseUserNull(SutProvider<UserService> sutProvider)
|
||||
{
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(() => sutProvider.Sut.SendNewDeviceVerificationEmailAsync(null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(DeviceType.UnknownBrowser, "Unknown Browser")]
|
||||
[BitAutoData(DeviceType.Android, "Android")]
|
||||
public async Task SendNewDeviceVerificationEmailAsync_DeviceMatches(DeviceType deviceType, string deviceTypeName,
|
||||
User user)
|
||||
{
|
||||
var sutProvider = new SutProvider<UserService>()
|
||||
.CreateWithUserServiceCustomizations(user);
|
||||
|
||||
var context = sutProvider.GetDependency<ICurrentContext>();
|
||||
context.DeviceType = deviceType;
|
||||
context.IpAddress = "1.1.1.1";
|
||||
|
||||
await sutProvider.Sut.SendNewDeviceVerificationEmailAsync(user);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendTwoFactorEmailAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), deviceTypeName, Arg.Any<bool>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SendNewDeviceVerificationEmailAsync_NullDeviceTypeShouldSendUnkownBrowserType(User user)
|
||||
{
|
||||
var sutProvider = new SutProvider<UserService>()
|
||||
.CreateWithUserServiceCustomizations(user);
|
||||
|
||||
var context = sutProvider.GetDependency<ICurrentContext>();
|
||||
context.DeviceType = null;
|
||||
context.IpAddress = "1.1.1.1";
|
||||
|
||||
await sutProvider.Sut.SendNewDeviceVerificationEmailAsync(user);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendTwoFactorEmailAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), "Unknown Browser", Arg.Any<bool>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task HasPremiumFromOrganization_Returns_False_If_No_Orgs(SutProvider<UserService> sutProvider, User user)
|
||||
{
|
||||
@@ -577,78 +457,6 @@ public class UserServiceTests
|
||||
.SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(default, default);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ResendNewDeviceVerificationEmail_UserNull_SendTwoFactorEmailAsyncNotCalled(
|
||||
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()
|
||||
.SendTwoFactorEmailAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<bool>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ResendNewDeviceVerificationEmail_SecretNotValid_SendTwoFactorEmailAsyncNotCalled(
|
||||
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()
|
||||
.SendTwoFactorEmailAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<bool>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ResendNewDeviceVerificationEmail_SendsToken_Success(User user)
|
||||
{
|
||||
// Arrange
|
||||
var testPassword = "test_password";
|
||||
SetupUserAndDevice(user, true);
|
||||
|
||||
var sutProvider = new SutProvider<UserService>()
|
||||
.CreateWithUserServiceCustomizations(user);
|
||||
|
||||
// Setup the fake password verification
|
||||
sutProvider
|
||||
.GetDependency<IUserPasswordStore<User>>()
|
||||
.GetPasswordHashAsync(user, Arg.Any<CancellationToken>())
|
||||
.Returns((ci) =>
|
||||
{
|
||||
return Task.FromResult("hashed_test_password");
|
||||
});
|
||||
|
||||
sutProvider.GetDependency<IPasswordHasher<User>>()
|
||||
.VerifyHashedPassword(user, "hashed_test_password", testPassword)
|
||||
.Returns((ci) =>
|
||||
{
|
||||
return PasswordVerificationResult.Success;
|
||||
});
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetByEmailAsync(user.Email)
|
||||
.Returns(user);
|
||||
|
||||
var context = sutProvider.GetDependency<ICurrentContext>();
|
||||
context.DeviceType = DeviceType.Android;
|
||||
context.IpAddress = "1.1.1.1";
|
||||
|
||||
await sutProvider.Sut.ResendNewDeviceVerificationEmail(user.Email, testPassword);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendTwoFactorEmailAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<bool>());
|
||||
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData("")]
|
||||
[BitAutoData("null")]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Auth.Services;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Api;
|
||||
@@ -26,6 +27,7 @@ public class DeviceValidatorTests
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IDistributedCache _distributedCache;
|
||||
private readonly ITwoFactorEmailService _twoFactorEmailService;
|
||||
private readonly Logger<DeviceValidator> _logger;
|
||||
|
||||
private readonly DeviceValidator _sut;
|
||||
@@ -39,6 +41,7 @@ public class DeviceValidatorTests
|
||||
_currentContext = Substitute.For<ICurrentContext>();
|
||||
_userService = Substitute.For<IUserService>();
|
||||
_distributedCache = Substitute.For<IDistributedCache>();
|
||||
_twoFactorEmailService = Substitute.For<ITwoFactorEmailService>();
|
||||
_logger = new Logger<DeviceValidator>(Substitute.For<ILoggerFactory>());
|
||||
_sut = new DeviceValidator(
|
||||
_deviceService,
|
||||
@@ -48,6 +51,7 @@ public class DeviceValidatorTests
|
||||
_currentContext,
|
||||
_userService,
|
||||
_distributedCache,
|
||||
_twoFactorEmailService,
|
||||
_logger);
|
||||
}
|
||||
|
||||
@@ -580,7 +584,7 @@ public class DeviceValidatorTests
|
||||
var result = await _sut.ValidateRequestDeviceAsync(request, context);
|
||||
|
||||
// Assert
|
||||
await _userService.Received(1).SendNewDeviceVerificationEmailAsync(context.User);
|
||||
await _twoFactorEmailService.Received(1).SendNewDeviceVerificationEmailAsync(context.User);
|
||||
await _deviceService.Received(0).SaveAsync(Arg.Any<Device>());
|
||||
|
||||
Assert.False(result);
|
||||
|
||||
Reference in New Issue
Block a user