mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 15:42:48 -05:00
[PM-6666] Two factor Validator refactor (#4894)
* initial device removal * Unit Testing * Finalized tests * initial commit refactoring two factor * initial tests * Unit Tests * initial device removal * Unit Testing * Finalized tests * initial commit refactoring two factor * initial tests * Unit Tests * Fixing some tests * renaming and reorganizing * refactored two factor flows * fixed a possible issue with object mapping. * Update TwoFactorAuthenticationValidator.cs removed unused code
This commit is contained in:
@ -1,8 +1,7 @@
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
@ -11,8 +10,8 @@ using Bit.Core.Models.Api;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tokens;
|
||||
using Bit.Identity.IdentityServer;
|
||||
using Bit.Identity.IdentityServer.RequestValidators;
|
||||
using Bit.Identity.Test.Wrappers;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Duende.IdentityServer.Validation;
|
||||
@ -32,18 +31,14 @@ public class BaseRequestValidatorTests
|
||||
private readonly IUserService _userService;
|
||||
private readonly IEventService _eventService;
|
||||
private readonly IDeviceValidator _deviceValidator;
|
||||
private readonly IOrganizationDuoWebTokenProvider _organizationDuoWebTokenProvider;
|
||||
private readonly ITemporaryDuoWebV4SDKService _duoWebV4SDKService;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly ITwoFactorAuthenticationValidator _twoFactorAuthenticationValidator;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IApplicationCacheService _applicationCacheService;
|
||||
private readonly IMailService _mailService;
|
||||
private readonly ILogger<BaseRequestValidatorTests> _logger;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> _tokenDataFactory;
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
||||
private readonly IUserDecryptionOptionsBuilder _userDecryptionOptionsBuilder;
|
||||
@ -52,43 +47,35 @@ public class BaseRequestValidatorTests
|
||||
|
||||
public BaseRequestValidatorTests()
|
||||
{
|
||||
_userManager = SubstituteUserManager();
|
||||
_userService = Substitute.For<IUserService>();
|
||||
_eventService = Substitute.For<IEventService>();
|
||||
_deviceValidator = Substitute.For<IDeviceValidator>();
|
||||
_organizationDuoWebTokenProvider = Substitute.For<IOrganizationDuoWebTokenProvider>();
|
||||
_duoWebV4SDKService = Substitute.For<ITemporaryDuoWebV4SDKService>();
|
||||
_organizationRepository = Substitute.For<IOrganizationRepository>();
|
||||
_twoFactorAuthenticationValidator = Substitute.For<ITwoFactorAuthenticationValidator>();
|
||||
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||
_applicationCacheService = Substitute.For<IApplicationCacheService>();
|
||||
_mailService = Substitute.For<IMailService>();
|
||||
_logger = Substitute.For<ILogger<BaseRequestValidatorTests>>();
|
||||
_currentContext = Substitute.For<ICurrentContext>();
|
||||
_globalSettings = Substitute.For<GlobalSettings>();
|
||||
_userRepository = Substitute.For<IUserRepository>();
|
||||
_policyService = Substitute.For<IPolicyService>();
|
||||
_tokenDataFactory = Substitute.For<IDataProtectorTokenFactory<SsoEmail2faSessionTokenable>>();
|
||||
_featureService = Substitute.For<IFeatureService>();
|
||||
_ssoConfigRepository = Substitute.For<ISsoConfigRepository>();
|
||||
_userDecryptionOptionsBuilder = Substitute.For<IUserDecryptionOptionsBuilder>();
|
||||
_userManager = SubstituteUserManager();
|
||||
|
||||
_sut = new BaseRequestValidatorTestWrapper(
|
||||
_userManager,
|
||||
_userService,
|
||||
_eventService,
|
||||
_deviceValidator,
|
||||
_organizationDuoWebTokenProvider,
|
||||
_duoWebV4SDKService,
|
||||
_organizationRepository,
|
||||
_twoFactorAuthenticationValidator,
|
||||
_organizationUserRepository,
|
||||
_applicationCacheService,
|
||||
_mailService,
|
||||
_logger,
|
||||
_currentContext,
|
||||
_globalSettings,
|
||||
_userRepository,
|
||||
_policyService,
|
||||
_tokenDataFactory,
|
||||
_featureService,
|
||||
_ssoConfigRepository,
|
||||
_userDecryptionOptionsBuilder);
|
||||
@ -116,7 +103,7 @@ public class BaseRequestValidatorTests
|
||||
|
||||
var errorResponse = (ErrorResponseModel)context.GrantResult.CustomResponse["ErrorModel"];
|
||||
|
||||
// Assert
|
||||
// Assert
|
||||
await _eventService.Received(1)
|
||||
.LogUserEventAsync(context.CustomValidatorRequestContext.User.Id,
|
||||
Core.Enums.EventType.User_FailedLogIn);
|
||||
@ -127,7 +114,7 @@ public class BaseRequestValidatorTests
|
||||
/* Logic path
|
||||
ValidateAsync -> UpdateFailedAuthDetailsAsync -> _mailService.SendFailedLoginAttemptsEmailAsync
|
||||
|-> BuildErrorResultAsync -> _eventService.LogUserEventAsync
|
||||
(self hosted) |-> _logger.LogWarning()
|
||||
(self hosted) |-> _logger.LogWarning()
|
||||
|-> SetErrorResult
|
||||
*/
|
||||
[Theory, BitAutoData]
|
||||
@ -154,7 +141,7 @@ public class BaseRequestValidatorTests
|
||||
|
||||
/* Logic path
|
||||
ValidateAsync -> UpdateFailedAuthDetailsAsync -> _mailService.SendFailedLoginAttemptsEmailAsync
|
||||
|-> BuildErrorResultAsync -> _eventService.LogUserEventAsync
|
||||
|-> BuildErrorResultAsync -> _eventService.LogUserEventAsync
|
||||
|-> SetErrorResult
|
||||
*/
|
||||
[Theory, BitAutoData]
|
||||
@ -202,6 +189,9 @@ public class BaseRequestValidatorTests
|
||||
{
|
||||
// Arrange
|
||||
var context = CreateContext(tokenRequest, requestContext, grantResult);
|
||||
_twoFactorAuthenticationValidator
|
||||
.RequiresTwoFactorAsync(Arg.Any<User>(), Arg.Any<ValidatedTokenRequest>())
|
||||
.Returns(Task.FromResult(new Tuple<bool, Organization>(false, default)));
|
||||
|
||||
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
|
||||
_sut.isValid = true;
|
||||
@ -230,6 +220,9 @@ public class BaseRequestValidatorTests
|
||||
{
|
||||
// Arrange
|
||||
var context = CreateContext(tokenRequest, requestContext, grantResult);
|
||||
_twoFactorAuthenticationValidator
|
||||
.RequiresTwoFactorAsync(Arg.Any<User>(), Arg.Any<ValidatedTokenRequest>())
|
||||
.Returns(Task.FromResult(new Tuple<bool, Organization>(false, null)));
|
||||
|
||||
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
|
||||
_sut.isValid = true;
|
||||
@ -237,7 +230,7 @@ public class BaseRequestValidatorTests
|
||||
context.CustomValidatorRequestContext.User.CreationDate = DateTime.UtcNow - TimeSpan.FromDays(1);
|
||||
_globalSettings.DisableEmailNewDevice = false;
|
||||
|
||||
context.ValidatedTokenRequest.GrantType = "client_credentials"; // This || AuthCode will allow process to continue to get device
|
||||
context.ValidatedTokenRequest.GrantType = "client_credentials"; // This || AuthCode will allow process to continue to get device
|
||||
|
||||
_deviceValidator.SaveDeviceAsync(Arg.Any<User>(), Arg.Any<ValidatedTokenRequest>())
|
||||
.Returns(device);
|
||||
@ -267,10 +260,13 @@ public class BaseRequestValidatorTests
|
||||
context.CustomValidatorRequestContext.User.CreationDate = DateTime.UtcNow - TimeSpan.FromDays(1);
|
||||
_globalSettings.DisableEmailNewDevice = false;
|
||||
|
||||
context.ValidatedTokenRequest.GrantType = "client_credentials"; // This || AuthCode will allow process to continue to get device
|
||||
context.ValidatedTokenRequest.GrantType = "client_credentials"; // This || AuthCode will allow process to continue to get device
|
||||
|
||||
_deviceValidator.SaveDeviceAsync(Arg.Any<User>(), Arg.Any<ValidatedTokenRequest>())
|
||||
.Returns(device);
|
||||
_twoFactorAuthenticationValidator
|
||||
.RequiresTwoFactorAsync(Arg.Any<User>(), Arg.Any<ValidatedTokenRequest>())
|
||||
.Returns(Task.FromResult(new Tuple<bool, Organization>(false, null)));
|
||||
// Act
|
||||
await _sut.ValidateAsync(context);
|
||||
|
||||
@ -306,10 +302,13 @@ public class BaseRequestValidatorTests
|
||||
_policyService.AnyPoliciesApplicableToUserAsync(
|
||||
Arg.Any<Guid>(), PolicyType.RequireSso, OrganizationUserStatusType.Confirmed)
|
||||
.Returns(Task.FromResult(true));
|
||||
_twoFactorAuthenticationValidator
|
||||
.RequiresTwoFactorAsync(Arg.Any<User>(), Arg.Any<ValidatedTokenRequest>())
|
||||
.Returns(Task.FromResult(new Tuple<bool, Organization>(false, null)));
|
||||
// Act
|
||||
await _sut.ValidateAsync(context);
|
||||
|
||||
// Assert
|
||||
// Assert
|
||||
Assert.True(context.GrantResult.IsError);
|
||||
var errorResponse = (ErrorResponseModel)context.GrantResult.CustomResponse["ErrorModel"];
|
||||
Assert.Equal("SSO authentication is required.", errorResponse.Message);
|
||||
@ -330,6 +329,9 @@ public class BaseRequestValidatorTests
|
||||
context.ValidatedTokenRequest.ClientId = "Not Web";
|
||||
_sut.isValid = true;
|
||||
_featureService.IsEnabled(FeatureFlagKeys.BlockLegacyUsers).Returns(true);
|
||||
_twoFactorAuthenticationValidator
|
||||
.RequiresTwoFactorAsync(Arg.Any<User>(), Arg.Any<ValidatedTokenRequest>())
|
||||
.Returns(Task.FromResult(new Tuple<bool, Organization>(false, null)));
|
||||
|
||||
// Act
|
||||
await _sut.ValidateAsync(context);
|
||||
@ -341,28 +343,6 @@ public class BaseRequestValidatorTests
|
||||
, errorResponse.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RequiresTwoFactorAsync_ClientCredentialsGrantType_ShouldReturnFalse(
|
||||
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
|
||||
CustomValidatorRequestContext requestContext,
|
||||
GrantValidationResult grantResult)
|
||||
{
|
||||
// Arrange
|
||||
var context = CreateContext(tokenRequest, requestContext, grantResult);
|
||||
|
||||
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
|
||||
context.ValidatedTokenRequest.GrantType = "client_credentials";
|
||||
|
||||
// Act
|
||||
var result = await _sut.TestRequiresTwoFactorAsync(
|
||||
context.CustomValidatorRequestContext.User,
|
||||
context.ValidatedTokenRequest);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.Item1);
|
||||
Assert.Null(result.Item2);
|
||||
}
|
||||
|
||||
private BaseRequestValidationContextFake CreateContext(
|
||||
ValidatedTokenRequest tokenRequest,
|
||||
CustomValidatorRequestContext requestContext,
|
||||
|
Reference in New Issue
Block a user