You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
647 lines
24 KiB
647 lines
24 KiB
using Bit.Core.Context; |
|
using Bit.Core.Entities; |
|
using Bit.Core.Enums; |
|
using Bit.Core.Models.Api; |
|
using Bit.Core.Repositories; |
|
using Bit.Core.Services; |
|
using Bit.Core.Settings; |
|
using Bit.Identity.IdentityServer; |
|
using Bit.Identity.IdentityServer.RequestValidators; |
|
using Bit.Test.Common.AutoFixture.Attributes; |
|
using Duende.IdentityServer.Validation; |
|
using Microsoft.Extensions.Caching.Distributed; |
|
using Microsoft.Extensions.Logging; |
|
using NSubstitute; |
|
using Xunit; |
|
using AuthFixtures = Bit.Identity.Test.AutoFixture; |
|
|
|
namespace Bit.Identity.Test.IdentityServer; |
|
|
|
public class DeviceValidatorTests |
|
{ |
|
private readonly IDeviceService _deviceService; |
|
private readonly IDeviceRepository _deviceRepository; |
|
private readonly GlobalSettings _globalSettings; |
|
private readonly IMailService _mailService; |
|
private readonly ICurrentContext _currentContext; |
|
private readonly IUserService _userService; |
|
private readonly IDistributedCache _distributedCache; |
|
private readonly Logger<DeviceValidator> _logger; |
|
|
|
private readonly DeviceValidator _sut; |
|
|
|
public DeviceValidatorTests() |
|
{ |
|
_deviceService = Substitute.For<IDeviceService>(); |
|
_deviceRepository = Substitute.For<IDeviceRepository>(); |
|
_globalSettings = new GlobalSettings(); |
|
_mailService = Substitute.For<IMailService>(); |
|
_currentContext = Substitute.For<ICurrentContext>(); |
|
_userService = Substitute.For<IUserService>(); |
|
_distributedCache = Substitute.For<IDistributedCache>(); |
|
_logger = new Logger<DeviceValidator>(Substitute.For<ILoggerFactory>()); |
|
_sut = new DeviceValidator( |
|
_deviceService, |
|
_deviceRepository, |
|
_globalSettings, |
|
_mailService, |
|
_currentContext, |
|
_userService, |
|
_distributedCache, |
|
_logger); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void GetKnownDeviceAsync_UserNull_ReturnsFalse( |
|
Device device) |
|
{ |
|
// Arrange |
|
// AutoData arranges |
|
|
|
// Act |
|
var result = await _sut.GetKnownDeviceAsync(null, device); |
|
|
|
// Assert |
|
Assert.Null(result); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void GetKnownDeviceAsync_DeviceNull_ReturnsFalse( |
|
User user) |
|
{ |
|
// Arrange |
|
// Device raw data is null which will cause the device to be null |
|
|
|
// Act |
|
var result = await _sut.GetKnownDeviceAsync(user, null); |
|
|
|
// Assert |
|
Assert.Null(result); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void GetKnownDeviceAsync_DeviceNotInDatabase_ReturnsFalse( |
|
User user, |
|
Device device) |
|
{ |
|
// Arrange |
|
_deviceRepository.GetByIdentifierAsync(Arg.Any<string>(), Arg.Any<Guid>()) |
|
.Returns(null as Device); |
|
// Act |
|
var result = await _sut.GetKnownDeviceAsync(user, device); |
|
|
|
// Assert |
|
Assert.Null(result); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void GetKnownDeviceAsync_UserAndDeviceValid_ReturnsTrue( |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request, |
|
User user, |
|
Device device) |
|
{ |
|
// Arrange |
|
AddValidDeviceToRequest(request); |
|
_deviceRepository.GetByIdentifierAsync(Arg.Any<string>(), Arg.Any<Guid>()) |
|
.Returns(device); |
|
// Act |
|
var result = await _sut.GetKnownDeviceAsync(user, device); |
|
|
|
// Assert |
|
Assert.NotNull(result); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData("not null", "Android", "")] |
|
[BitAutoData("not null", "", "not null")] |
|
[BitAutoData("", "Android", "not null")] |
|
public void GetDeviceFromRequest_RawDeviceInfoNull_ReturnsNull( |
|
string deviceIdentifier, |
|
string deviceType, |
|
string deviceName, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
request.Raw["DeviceIdentifier"] = deviceIdentifier; |
|
request.Raw["DeviceType"] = deviceType; |
|
request.Raw["DeviceName"] = deviceName; |
|
|
|
// Act |
|
var result = DeviceValidator.GetDeviceFromRequest(request); |
|
|
|
// Assert |
|
Assert.Null(result); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public void GetDeviceFromRequest_RawDeviceInfoValid_ReturnsDevice( |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
AddValidDeviceToRequest(request); |
|
|
|
// Act |
|
var result = DeviceValidator.GetDeviceFromRequest(request); |
|
|
|
// Assert |
|
Assert.NotNull(result); |
|
Assert.Equal("DeviceIdentifier", result.Identifier); |
|
Assert.Equal("DeviceName", result.Name); |
|
Assert.Equal(DeviceType.Android, result.Type); |
|
Assert.Equal("DevicePushToken", result.PushToken); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void ValidateRequestDeviceAsync_DeviceNull_ContextModified_ReturnsFalse( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
context.KnownDevice = false; |
|
context.Device = null; |
|
|
|
// Act |
|
Assert.NotNull(context.User); |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _deviceService.Received(0).SaveAsync(Arg.Any<Device>()); |
|
|
|
Assert.False(result); |
|
Assert.NotNull(context.CustomResponse["ErrorModel"]); |
|
var expectedErrorModel = new ErrorResponseModel("no device information provided"); |
|
var actualResponse = (ErrorResponseModel)context.CustomResponse["ErrorModel"]; |
|
Assert.Equal(expectedErrorModel.Message, actualResponse.Message); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void ValidateRequestDeviceAsync_RequestDeviceKnown_ContextDeviceModified_ReturnsTrue( |
|
Device device, |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
context.KnownDevice = false; |
|
context.Device = null; |
|
AddValidDeviceToRequest(request); |
|
_deviceRepository.GetByIdentifierAsync(Arg.Any<string>(), Arg.Any<Guid>()) |
|
.Returns(device); |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _deviceService.Received(0).SaveAsync(Arg.Any<Device>()); |
|
|
|
Assert.True(result); |
|
Assert.False(context.CustomResponse.ContainsKey("ErrorModel")); |
|
Assert.NotNull(context.Device); |
|
Assert.Equal(context.Device, device); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void ValidateRequestDeviceAsync_ContextDeviceKnown_ContextDeviceModified_ReturnsTrue( |
|
Device databaseDevice, |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
context.KnownDevice = false; |
|
_deviceRepository.GetByIdentifierAsync(Arg.Any<string>(), Arg.Any<Guid>()) |
|
.Returns(databaseDevice); |
|
// we want to show that the context device is updated when the device is known |
|
Assert.NotEqual(context.Device, databaseDevice); |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _deviceService.Received(0).SaveAsync(Arg.Any<Device>()); |
|
|
|
Assert.True(result); |
|
Assert.False(context.CustomResponse.ContainsKey("ErrorModel")); |
|
Assert.Equal(context.Device, databaseDevice); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void ValidateRequestDeviceAsync_ExistingUserNewDeviceLogin_SendNewDeviceLoginEmail_ReturnsTrue( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
context.KnownDevice = false; |
|
AddValidDeviceToRequest(request); |
|
_globalSettings.DisableEmailNewDevice = false; |
|
_deviceRepository.GetByIdentifierAsync(context.Device.Identifier, context.User.Id) |
|
.Returns(null as Device); |
|
// set user creation to more than 10 minutes ago |
|
context.User.CreationDate = DateTime.UtcNow - TimeSpan.FromMinutes(11); |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _deviceService.Received(1).SaveAsync(context.Device); |
|
await _mailService.Received(1).SendNewDeviceLoggedInEmail( |
|
Arg.Any<string>(), Arg.Any<string>(), Arg.Any<DateTime>(), Arg.Any<string>()); |
|
Assert.True(result); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void ValidateRequestDeviceAsync_NewUserNewDeviceLogin_DoesNotSendNewDeviceLoginEmail_ReturnsTrue( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
context.KnownDevice = false; |
|
AddValidDeviceToRequest(request); |
|
_globalSettings.DisableEmailNewDevice = false; |
|
_deviceRepository.GetByIdentifierAsync(context.Device.Identifier, context.User.Id) |
|
.Returns(null as Device); |
|
// set user creation to less than 10 minutes ago |
|
context.User.CreationDate = DateTime.UtcNow - TimeSpan.FromMinutes(9); |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _deviceService.Received(1).SaveAsync(context.Device); |
|
await _mailService.Received(0).SendNewDeviceLoggedInEmail( |
|
Arg.Any<string>(), Arg.Any<string>(), Arg.Any<DateTime>(), Arg.Any<string>()); |
|
Assert.True(result); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void ValidateRequestDeviceAsynce_DisableNewDeviceLoginEmailTrue_DoesNotSendNewDeviceEmail_ReturnsTrue( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
context.KnownDevice = false; |
|
AddValidDeviceToRequest(request); |
|
_globalSettings.DisableEmailNewDevice = true; |
|
_deviceRepository.GetByIdentifierAsync(context.Device.Identifier, context.User.Id) |
|
.Returns(null as Device); |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _deviceService.Received(1).SaveAsync(context.Device); |
|
await _mailService.Received(0).SendNewDeviceLoggedInEmail( |
|
Arg.Any<string>(), Arg.Any<string>(), Arg.Any<DateTime>(), Arg.Any<string>()); |
|
Assert.True(result); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData("webauthn")] |
|
[BitAutoData("refresh_token")] |
|
[BitAutoData("authorization_code")] |
|
[BitAutoData("client_credentials")] |
|
public async void ValidateRequestDeviceAsync_GrantTypeNotPassword_SavesDevice_ReturnsTrue( |
|
string grantType, |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
context.KnownDevice = false; |
|
ArrangeForHandleNewDeviceVerificationTest(context, request); |
|
AddValidDeviceToRequest(request); |
|
_deviceRepository.GetByIdentifierAsync(context.Device.Identifier, context.User.Id) |
|
.Returns(null as Device); |
|
|
|
request.GrantType = grantType; |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _deviceService.Received(1).SaveAsync(context.Device); |
|
Assert.True(result); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void ValidateRequestDeviceAsync_IsAuthRequest_SavesDevice_ReturnsTrue( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
context.KnownDevice = false; |
|
ArrangeForHandleNewDeviceVerificationTest(context, request); |
|
AddValidDeviceToRequest(request); |
|
_deviceRepository.GetByIdentifierAsync(context.Device.Identifier, context.User.Id) |
|
.Returns(null as Device); |
|
|
|
request.Raw.Add("AuthRequest", "authRequest"); |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _deviceService.Received(1).SaveAsync(context.Device); |
|
Assert.True(result); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void ValidateRequestDeviceAsync_TwoFactorRequired_SavesDevice_ReturnsTrue( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
context.KnownDevice = false; |
|
ArrangeForHandleNewDeviceVerificationTest(context, request); |
|
AddValidDeviceToRequest(request); |
|
_deviceRepository.GetByIdentifierAsync(context.Device.Identifier, context.User.Id) |
|
.Returns(null as Device); |
|
|
|
context.TwoFactorRequired = true; |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _deviceService.Received(1).SaveAsync(context.Device); |
|
Assert.True(result); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void ValidateRequestDeviceAsync_SsoRequired_SavesDevice_ReturnsTrue( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
context.KnownDevice = false; |
|
ArrangeForHandleNewDeviceVerificationTest(context, request); |
|
AddValidDeviceToRequest(request); |
|
_deviceRepository.GetByIdentifierAsync(context.Device.Identifier, context.User.Id) |
|
.Returns(null as Device); |
|
|
|
context.SsoRequired = true; |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _deviceService.Received(1).SaveAsync(context.Device); |
|
Assert.True(result); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void HandleNewDeviceVerificationAsync_UserNull_ContextModified_ReturnsInvalidUser( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
ArrangeForHandleNewDeviceVerificationTest(context, request); |
|
_globalSettings.EnableNewDeviceVerification = true; |
|
|
|
context.User = null; |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _deviceService.Received(0).SaveAsync(Arg.Any<Device>()); |
|
|
|
Assert.False(result); |
|
Assert.NotNull(context.CustomResponse["ErrorModel"]); |
|
// PM-13340: The error message should be "invalid user" instead of "no device information provided" |
|
var expectedErrorMessage = "no device information provided"; |
|
var actualResponse = (ErrorResponseModel)context.CustomResponse["ErrorModel"]; |
|
Assert.Equal(expectedErrorMessage, actualResponse.Message); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void HandleNewDeviceVerificationAsync_VerifyDevicesFalse_ReturnsSuccess( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
ArrangeForHandleNewDeviceVerificationTest(context, request); |
|
_globalSettings.EnableNewDeviceVerification = true; |
|
context.User.VerifyDevices = false; |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _userService.Received(0).SendOTPAsync(context.User); |
|
await _deviceService.Received(1).SaveAsync(Arg.Any<Device>()); |
|
|
|
Assert.True(result); |
|
Assert.False(context.CustomResponse.ContainsKey("ErrorModel")); |
|
Assert.Equal(context.User.Id, context.Device.UserId); |
|
Assert.NotNull(context.Device); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void HandleNewDeviceVerificationAsync_NewlyCreated_ReturnsSuccess( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
ArrangeForHandleNewDeviceVerificationTest(context, request); |
|
_globalSettings.EnableNewDeviceVerification = true; |
|
_distributedCache.GetAsync(Arg.Any<string>()).Returns(null as byte[]); |
|
context.User.CreationDate = DateTime.UtcNow - TimeSpan.FromHours(23); |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _userService.Received(0).SendOTPAsync(context.User); |
|
await _deviceService.Received(1).SaveAsync(Arg.Any<Device>()); |
|
|
|
Assert.True(result); |
|
Assert.False(context.CustomResponse.ContainsKey("ErrorModel")); |
|
Assert.Equal(context.User.Id, context.Device.UserId); |
|
Assert.NotNull(context.Device); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void HandleNewDeviceVerificationAsync_UserHasCacheValue_ReturnsSuccess( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
ArrangeForHandleNewDeviceVerificationTest(context, request); |
|
_globalSettings.EnableNewDeviceVerification = true; |
|
_distributedCache.GetAsync(Arg.Any<string>()).Returns([1]); |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _userService.Received(0).SendOTPAsync(context.User); |
|
await _deviceService.Received(1).SaveAsync(Arg.Any<Device>()); |
|
|
|
Assert.True(result); |
|
Assert.False(context.CustomResponse.ContainsKey("ErrorModel")); |
|
Assert.Equal(context.User.Id, context.Device.UserId); |
|
Assert.NotNull(context.Device); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void HandleNewDeviceVerificationAsync_NewDeviceOtpValid_ReturnsSuccess( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
ArrangeForHandleNewDeviceVerificationTest(context, request); |
|
_globalSettings.EnableNewDeviceVerification = true; |
|
_distributedCache.GetAsync(Arg.Any<string>()).Returns(null as byte[]); |
|
|
|
var newDeviceOtp = "123456"; |
|
request.Raw.Add("NewDeviceOtp", newDeviceOtp); |
|
|
|
_userService.VerifyOTPAsync(context.User, newDeviceOtp).Returns(true); |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _userService.Received(0).SendOTPAsync(context.User); |
|
await _deviceService.Received(1).SaveAsync(context.Device); |
|
|
|
Assert.True(result); |
|
Assert.False(context.CustomResponse.ContainsKey("ErrorModel")); |
|
Assert.Equal(context.User.Id, context.Device.UserId); |
|
Assert.NotNull(context.Device); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData("")] |
|
[BitAutoData("123456")] |
|
public async void HandleNewDeviceVerificationAsync_NewDeviceOtpInvalid_ReturnsInvalidNewDeviceOtp( |
|
string newDeviceOtp, |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
ArrangeForHandleNewDeviceVerificationTest(context, request); |
|
_globalSettings.EnableNewDeviceVerification = true; |
|
_distributedCache.GetAsync(Arg.Any<string>()).Returns(null as byte[]); |
|
|
|
request.Raw.Add("NewDeviceOtp", newDeviceOtp); |
|
|
|
_userService.VerifyOTPAsync(context.User, newDeviceOtp).Returns(false); |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _userService.DidNotReceive().SendOTPAsync(Arg.Any<User>()); |
|
await _deviceService.Received(0).SaveAsync(Arg.Any<Device>()); |
|
|
|
Assert.False(result); |
|
Assert.NotNull(context.CustomResponse["ErrorModel"]); |
|
var expectedErrorMessage = "invalid new device otp"; |
|
var actualResponse = (ErrorResponseModel)context.CustomResponse["ErrorModel"]; |
|
Assert.Equal(expectedErrorMessage, actualResponse.Message); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void HandleNewDeviceVerificationAsync_UserHasNoDevices_ReturnsSuccess( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
ArrangeForHandleNewDeviceVerificationTest(context, request); |
|
_globalSettings.EnableNewDeviceVerification = true; |
|
_distributedCache.GetAsync(Arg.Any<string>()).Returns([1]); |
|
_deviceRepository.GetManyByUserIdAsync(context.User.Id).Returns([]); |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _userService.Received(0).VerifyOTPAsync(Arg.Any<User>(), Arg.Any<string>()); |
|
await _userService.Received(0).SendOTPAsync(Arg.Any<User>()); |
|
await _deviceService.Received(1).SaveAsync(context.Device); |
|
|
|
Assert.True(result); |
|
Assert.False(context.CustomResponse.ContainsKey("ErrorModel")); |
|
Assert.Equal(context.User.Id, context.Device.UserId); |
|
Assert.NotNull(context.Device); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public async void HandleNewDeviceVerificationAsync_NewDeviceOtpEmpty_UserHasDevices_ReturnsNewDeviceVerificationRequired( |
|
CustomValidatorRequestContext context, |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
ArrangeForHandleNewDeviceVerificationTest(context, request); |
|
_globalSettings.EnableNewDeviceVerification = true; |
|
_deviceRepository.GetManyByUserIdAsync(context.User.Id).Returns([new Device()]); |
|
_distributedCache.GetAsync(Arg.Any<string>()).Returns(null as byte[]); |
|
|
|
// Act |
|
var result = await _sut.ValidateRequestDeviceAsync(request, context); |
|
|
|
// Assert |
|
await _userService.Received(1).SendNewDeviceVerificationEmailAsync(context.User); |
|
await _deviceService.Received(0).SaveAsync(Arg.Any<Device>()); |
|
|
|
Assert.False(result); |
|
Assert.NotNull(context.CustomResponse["ErrorModel"]); |
|
var expectedErrorMessage = "new device verification required"; |
|
var actualResponse = (ErrorResponseModel)context.CustomResponse["ErrorModel"]; |
|
Assert.Equal(expectedErrorMessage, actualResponse.Message); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public void NewDeviceOtpRequest_NewDeviceOtpNull_ReturnsFalse( |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
// Autodata arranges |
|
|
|
// Act |
|
var result = DeviceValidator.NewDeviceOtpRequest(request); |
|
|
|
// Assert |
|
Assert.False(result); |
|
} |
|
|
|
[Theory, BitAutoData] |
|
public void NewDeviceOtpRequest_NewDeviceOtpNotNull_ReturnsTrue( |
|
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest request) |
|
{ |
|
// Arrange |
|
request.Raw["NewDeviceOtp"] = "123456"; |
|
|
|
// Act |
|
var result = DeviceValidator.NewDeviceOtpRequest(request); |
|
|
|
// Assert |
|
Assert.True(result); |
|
} |
|
|
|
private static void AddValidDeviceToRequest(ValidatedTokenRequest request) |
|
{ |
|
request.Raw["DeviceIdentifier"] = "DeviceIdentifier"; |
|
request.Raw["DeviceType"] = "Android"; // must be valid device type |
|
request.Raw["DeviceName"] = "DeviceName"; |
|
request.Raw["DevicePushToken"] = "DevicePushToken"; |
|
} |
|
|
|
/// <summary> |
|
/// Configures the request context to facilitate testing the HandleNewDeviceVerificationAsync method. |
|
/// </summary> |
|
/// <param name="context">test context</param> |
|
/// <param name="request">test request</param> |
|
private static void ArrangeForHandleNewDeviceVerificationTest( |
|
CustomValidatorRequestContext context, |
|
ValidatedTokenRequest request) |
|
{ |
|
context.KnownDevice = false; |
|
request.GrantType = "password"; |
|
context.TwoFactorRequired = false; |
|
context.SsoRequired = false; |
|
if (context.User != null) |
|
{ |
|
context.User.CreationDate = DateTime.UtcNow - TimeSpan.FromDays(365); |
|
} |
|
} |
|
}
|
|
|