mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 15:42:48 -05:00
[PM-8107] Remove Duo v2 from server (#4934)
refactor(TwoFactorAuthentication): Remove references to old Duo SDK version 2 code and replace them with the Duo SDK version 4 supported library DuoUniversal code. Increased unit test coverage in the Two Factor Authentication code space. We opted to use DI instead of Inheritance for the Duo and OrganizaitonDuo two factor tokens to increase testability, since creating a testing mock of the Duo.Client was non-trivial. Reviewed-by: @JaredSnider-Bitwarden
This commit is contained in:
295
test/Api.Test/Auth/Controllers/TwoFactorControllerTests.cs
Normal file
295
test/Api.Test/Auth/Controllers/TwoFactorControllerTests.cs
Normal file
@ -0,0 +1,295 @@
|
||||
using Bit.Api.Auth.Controllers;
|
||||
using Bit.Api.Auth.Models.Request;
|
||||
using Bit.Api.Auth.Models.Request.Accounts;
|
||||
using Bit.Api.Auth.Models.Response.TwoFactor;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Api.Test.Auth.Controllers;
|
||||
|
||||
[ControllerCustomize(typeof(TwoFactorController))]
|
||||
[SutProviderCustomize]
|
||||
public class TwoFactorControllerTests
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public async Task CheckAsync_UserNull_ThrowsUnauthorizedException(SecretVerificationRequestModel request, SutProvider<TwoFactorController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByPrincipalAsync(default)
|
||||
.ReturnsForAnyArgs(null as User);
|
||||
|
||||
// Act
|
||||
var result = () => sutProvider.Sut.GetDuo(request);
|
||||
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task CheckAsync_BadSecret_ThrowsBadRequestException(User user, SecretVerificationRequestModel request, SutProvider<TwoFactorController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByPrincipalAsync(default)
|
||||
.ReturnsForAnyArgs(user);
|
||||
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.VerifySecretAsync(default, default)
|
||||
.ReturnsForAnyArgs(false);
|
||||
|
||||
// Act
|
||||
try
|
||||
{
|
||||
await sutProvider.Sut.GetDuo(request);
|
||||
}
|
||||
catch (BadRequestException e)
|
||||
{
|
||||
// Assert
|
||||
Assert.Equal("The model state is invalid.", e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task CheckAsync_CannotAccessPremium_ThrowsBadRequestException(User user, SecretVerificationRequestModel request, SutProvider<TwoFactorController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByPrincipalAsync(default)
|
||||
.ReturnsForAnyArgs(user);
|
||||
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.VerifySecretAsync(default, default)
|
||||
.ReturnsForAnyArgs(true);
|
||||
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.CanAccessPremium(default)
|
||||
.ReturnsForAnyArgs(false);
|
||||
|
||||
// Act
|
||||
try
|
||||
{
|
||||
await sutProvider.Sut.GetDuo(request);
|
||||
}
|
||||
catch (BadRequestException e)
|
||||
{
|
||||
// Assert
|
||||
Assert.Equal("Premium status is required.", e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetDuo_Success(User user, SecretVerificationRequestModel request, SutProvider<TwoFactorController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.TwoFactorProviders = GetUserTwoFactorDuoProvidersJson();
|
||||
SetupCheckAsyncToPass(sutProvider, user);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetDuo(request);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<TwoFactorDuoResponseModel>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PutDuo_InvalidConfiguration_ThrowsBadRequestException(User user, UpdateTwoFactorDuoRequestModel request, SutProvider<TwoFactorController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
SetupCheckAsyncToPass(sutProvider, user);
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.ValidateDuoConfiguration(default, default, default)
|
||||
.Returns(false);
|
||||
|
||||
// Act
|
||||
try
|
||||
{
|
||||
await sutProvider.Sut.PutDuo(request);
|
||||
}
|
||||
catch (BadRequestException e)
|
||||
{
|
||||
// Assert
|
||||
Assert.Equal("Duo configuration settings are not valid. Please re-check the Duo Admin panel.", e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PutDuo_Success(User user, UpdateTwoFactorDuoRequestModel request, SutProvider<TwoFactorController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.TwoFactorProviders = GetUserTwoFactorDuoProvidersJson();
|
||||
SetupCheckAsyncToPass(sutProvider, user);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.ValidateDuoConfiguration(default, default, default)
|
||||
.ReturnsForAnyArgs(true);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.PutDuo(request);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<TwoFactorDuoResponseModel>(result);
|
||||
Assert.Equal(user.TwoFactorProviders, request.ToUser(user).TwoFactorProviders);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task CheckOrganizationAsync_ManagePolicies_ThrowsNotFoundException(
|
||||
User user, Organization organization, SecretVerificationRequestModel request, SutProvider<TwoFactorController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
organization.TwoFactorProviders = GetOrganizationTwoFactorDuoProvidersJson();
|
||||
SetupCheckAsyncToPass(sutProvider, user);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.ManagePolicies(default)
|
||||
.ReturnsForAnyArgs(false);
|
||||
|
||||
// Act
|
||||
var result = () => sutProvider.Sut.GetOrganizationDuo(organization.Id.ToString(), request);
|
||||
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<NotFoundException>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task CheckOrganizationAsync_GetByIdAsync_ThrowsNotFoundException(
|
||||
User user, Organization organization, SecretVerificationRequestModel request, SutProvider<TwoFactorController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
organization.TwoFactorProviders = GetOrganizationTwoFactorDuoProvidersJson();
|
||||
SetupCheckAsyncToPass(sutProvider, user);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.ManagePolicies(default)
|
||||
.ReturnsForAnyArgs(true);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(default)
|
||||
.ReturnsForAnyArgs(null as Organization);
|
||||
|
||||
// Act
|
||||
var result = () => sutProvider.Sut.GetOrganizationDuo(organization.Id.ToString(), request);
|
||||
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<NotFoundException>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetOrganizationDuo_Success(
|
||||
User user, Organization organization, SecretVerificationRequestModel request, SutProvider<TwoFactorController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
organization.TwoFactorProviders = GetOrganizationTwoFactorDuoProvidersJson();
|
||||
SetupCheckAsyncToPass(sutProvider, user);
|
||||
SetupCheckOrganizationAsyncToPass(sutProvider, organization);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetOrganizationDuo(organization.Id.ToString(), request);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<TwoFactorDuoResponseModel>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PutOrganizationDuo_InvalidConfiguration_ThrowsBadRequestException(
|
||||
User user, Organization organization, UpdateTwoFactorDuoRequestModel request, SutProvider<TwoFactorController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
SetupCheckAsyncToPass(sutProvider, user);
|
||||
SetupCheckOrganizationAsyncToPass(sutProvider, organization);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.ValidateDuoConfiguration(default, default, default)
|
||||
.ReturnsForAnyArgs(false);
|
||||
|
||||
// Act
|
||||
try
|
||||
{
|
||||
await sutProvider.Sut.PutOrganizationDuo(organization.Id.ToString(), request);
|
||||
}
|
||||
catch (BadRequestException e)
|
||||
{
|
||||
// Assert
|
||||
Assert.Equal("Duo configuration settings are not valid. Please re-check the Duo Admin panel.", e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PutOrganizationDuo_Success(
|
||||
User user, Organization organization, UpdateTwoFactorDuoRequestModel request, SutProvider<TwoFactorController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
SetupCheckAsyncToPass(sutProvider, user);
|
||||
SetupCheckOrganizationAsyncToPass(sutProvider, organization);
|
||||
organization.TwoFactorProviders = GetUserTwoFactorDuoProvidersJson();
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.ValidateDuoConfiguration(default, default, default)
|
||||
.ReturnsForAnyArgs(true);
|
||||
|
||||
// Act
|
||||
var result =
|
||||
await sutProvider.Sut.PutOrganizationDuo(organization.Id.ToString(), request);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<TwoFactorDuoResponseModel>(result);
|
||||
Assert.Equal(organization.TwoFactorProviders, request.ToOrganization(organization).TwoFactorProviders);
|
||||
}
|
||||
|
||||
|
||||
private string GetUserTwoFactorDuoProvidersJson()
|
||||
{
|
||||
return
|
||||
"{\"2\":{\"Enabled\":true,\"MetaData\":{\"ClientSecret\":\"secretClientSecret\",\"ClientId\":\"clientId\",\"Host\":\"example.com\"}}}";
|
||||
}
|
||||
|
||||
private string GetOrganizationTwoFactorDuoProvidersJson()
|
||||
{
|
||||
return
|
||||
"{\"6\":{\"Enabled\":true,\"MetaData\":{\"ClientSecret\":\"secretClientSecret\",\"ClientId\":\"clientId\",\"Host\":\"example.com\"}}}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the CheckAsync method to pass.
|
||||
/// </summary>
|
||||
/// <param name="sutProvider">uses bit auto data</param>
|
||||
/// <param name="user">uses bit auto data</param>
|
||||
private void SetupCheckAsyncToPass(SutProvider<TwoFactorController> sutProvider, User user)
|
||||
{
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByPrincipalAsync(default)
|
||||
.ReturnsForAnyArgs(user);
|
||||
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.VerifySecretAsync(default, default)
|
||||
.ReturnsForAnyArgs(true);
|
||||
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.CanAccessPremium(default)
|
||||
.ReturnsForAnyArgs(true);
|
||||
}
|
||||
|
||||
private void SetupCheckOrganizationAsyncToPass(SutProvider<TwoFactorController> sutProvider, Organization organization)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.ManagePolicies(default)
|
||||
.ReturnsForAnyArgs(true);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(default)
|
||||
.ReturnsForAnyArgs(organization);
|
||||
}
|
||||
}
|
@ -18,8 +18,6 @@ public class OrganizationTwoFactorDuoRequestModelTests
|
||||
{
|
||||
ClientId = "clientId",
|
||||
ClientSecret = "clientSecret",
|
||||
IntegrationKey = "integrationKey",
|
||||
SecretKey = "secretKey",
|
||||
Host = "example.com"
|
||||
};
|
||||
|
||||
@ -30,8 +28,6 @@ public class OrganizationTwoFactorDuoRequestModelTests
|
||||
Assert.True(result.GetTwoFactorProviders().ContainsKey(TwoFactorProviderType.OrganizationDuo));
|
||||
Assert.Equal("clientId", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["ClientId"]);
|
||||
Assert.Equal("clientSecret", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["ClientSecret"]);
|
||||
Assert.Equal("clientId", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["IKey"]);
|
||||
Assert.Equal("clientSecret", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["SKey"]);
|
||||
Assert.Equal("example.com", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["Host"]);
|
||||
Assert.True(result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].Enabled);
|
||||
}
|
||||
@ -49,8 +45,6 @@ public class OrganizationTwoFactorDuoRequestModelTests
|
||||
{
|
||||
ClientId = "newClientId",
|
||||
ClientSecret = "newClientSecret",
|
||||
IntegrationKey = "newIntegrationKey",
|
||||
SecretKey = "newSecretKey",
|
||||
Host = "newExample.com"
|
||||
};
|
||||
|
||||
@ -61,61 +55,7 @@ public class OrganizationTwoFactorDuoRequestModelTests
|
||||
Assert.True(result.GetTwoFactorProviders().ContainsKey(TwoFactorProviderType.OrganizationDuo));
|
||||
Assert.Equal("newClientId", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["ClientId"]);
|
||||
Assert.Equal("newClientSecret", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["ClientSecret"]);
|
||||
Assert.Equal("newClientId", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["IKey"]);
|
||||
Assert.Equal("newClientSecret", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["SKey"]);
|
||||
Assert.Equal("newExample.com", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["Host"]);
|
||||
Assert.True(result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].Enabled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuoV2ParamsSync_WhenExistingProviderDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
var existingOrg = new Organization();
|
||||
var model = new UpdateTwoFactorDuoRequestModel
|
||||
{
|
||||
IntegrationKey = "integrationKey",
|
||||
SecretKey = "secretKey",
|
||||
Host = "example.com"
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = model.ToOrganization(existingOrg);
|
||||
|
||||
// Assert
|
||||
// IKey and SKey should be the same as ClientId and ClientSecret
|
||||
Assert.True(result.GetTwoFactorProviders().ContainsKey(TwoFactorProviderType.OrganizationDuo));
|
||||
Assert.Equal("integrationKey", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["ClientId"]);
|
||||
Assert.Equal("secretKey", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["ClientSecret"]);
|
||||
Assert.Equal("integrationKey", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["IKey"]);
|
||||
Assert.Equal("secretKey", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["SKey"]);
|
||||
Assert.Equal("example.com", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["Host"]);
|
||||
Assert.True(result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].Enabled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuoV4ParamsSync_WhenExistingProviderDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
var existingOrg = new Organization();
|
||||
var model = new UpdateTwoFactorDuoRequestModel
|
||||
{
|
||||
ClientId = "clientId",
|
||||
ClientSecret = "clientSecret",
|
||||
Host = "example.com"
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = model.ToOrganization(existingOrg);
|
||||
|
||||
// Assert
|
||||
// IKey and SKey should be the same as ClientId and ClientSecret
|
||||
Assert.True(result.GetTwoFactorProviders().ContainsKey(TwoFactorProviderType.OrganizationDuo));
|
||||
Assert.Equal("clientId", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["ClientId"]);
|
||||
Assert.Equal("clientSecret", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["ClientSecret"]);
|
||||
Assert.Equal("clientId", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["IKey"]);
|
||||
Assert.Equal("clientSecret", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["SKey"]);
|
||||
Assert.Equal("example.com", result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].MetaData["Host"]);
|
||||
Assert.True(result.GetTwoFactorProviders()[TwoFactorProviderType.OrganizationDuo].Enabled);
|
||||
}
|
||||
}
|
||||
|
@ -39,12 +39,9 @@ public class TwoFactorDuoRequestModelValidationTests
|
||||
var result = model.Validate(new ValidationContext(model));
|
||||
|
||||
// Assert
|
||||
Assert.Single(result);
|
||||
Assert.Equal("Neither v2 or v4 values are valid.", result.First().ErrorMessage);
|
||||
Assert.Contains("ClientId", result.First().MemberNames);
|
||||
Assert.Contains("ClientSecret", result.First().MemberNames);
|
||||
Assert.Contains("IntegrationKey", result.First().MemberNames);
|
||||
Assert.Contains("SecretKey", result.First().MemberNames);
|
||||
Assert.NotEmpty(result);
|
||||
Assert.True(result.Select(x => x.MemberNames.Contains("ClientId")).Any());
|
||||
Assert.True(result.Select(x => x.MemberNames.Contains("ClientSecret")).Any());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -17,8 +17,6 @@ public class UserTwoFactorDuoRequestModelTests
|
||||
{
|
||||
ClientId = "clientId",
|
||||
ClientSecret = "clientSecret",
|
||||
IntegrationKey = "integrationKey",
|
||||
SecretKey = "secretKey",
|
||||
Host = "example.com"
|
||||
};
|
||||
|
||||
@ -26,12 +24,9 @@ public class UserTwoFactorDuoRequestModelTests
|
||||
var result = model.ToUser(existingUser);
|
||||
|
||||
// Assert
|
||||
// IKey and SKey should be the same as ClientId and ClientSecret
|
||||
Assert.True(result.GetTwoFactorProviders().ContainsKey(TwoFactorProviderType.Duo));
|
||||
Assert.Equal("clientId", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["ClientId"]);
|
||||
Assert.Equal("clientSecret", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["ClientSecret"]);
|
||||
Assert.Equal("clientId", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["IKey"]);
|
||||
Assert.Equal("clientSecret", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["SKey"]);
|
||||
Assert.Equal("example.com", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["Host"]);
|
||||
Assert.True(result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].Enabled);
|
||||
}
|
||||
@ -49,8 +44,6 @@ public class UserTwoFactorDuoRequestModelTests
|
||||
{
|
||||
ClientId = "newClientId",
|
||||
ClientSecret = "newClientSecret",
|
||||
IntegrationKey = "newIntegrationKey",
|
||||
SecretKey = "newSecretKey",
|
||||
Host = "newExample.com"
|
||||
};
|
||||
|
||||
@ -58,65 +51,10 @@ public class UserTwoFactorDuoRequestModelTests
|
||||
var result = model.ToUser(existingUser);
|
||||
|
||||
// Assert
|
||||
// IKey and SKey should be the same as ClientId and ClientSecret
|
||||
Assert.True(result.GetTwoFactorProviders().ContainsKey(TwoFactorProviderType.Duo));
|
||||
Assert.Equal("newClientId", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["ClientId"]);
|
||||
Assert.Equal("newClientSecret", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["ClientSecret"]);
|
||||
Assert.Equal("newClientId", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["IKey"]);
|
||||
Assert.Equal("newClientSecret", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["SKey"]);
|
||||
Assert.Equal("newExample.com", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["Host"]);
|
||||
Assert.True(result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].Enabled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuoV2ParamsSync_WhenExistingProviderDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
var existingUser = new User();
|
||||
var model = new UpdateTwoFactorDuoRequestModel
|
||||
{
|
||||
IntegrationKey = "integrationKey",
|
||||
SecretKey = "secretKey",
|
||||
Host = "example.com"
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = model.ToUser(existingUser);
|
||||
|
||||
// Assert
|
||||
// IKey and SKey should be the same as ClientId and ClientSecret
|
||||
Assert.True(result.GetTwoFactorProviders().ContainsKey(TwoFactorProviderType.Duo));
|
||||
Assert.Equal("integrationKey", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["ClientId"]);
|
||||
Assert.Equal("secretKey", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["ClientSecret"]);
|
||||
Assert.Equal("integrationKey", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["IKey"]);
|
||||
Assert.Equal("secretKey", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["SKey"]);
|
||||
Assert.Equal("example.com", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["Host"]);
|
||||
Assert.True(result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].Enabled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuoV4ParamsSync_WhenExistingProviderDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
var existingUser = new User();
|
||||
var model = new UpdateTwoFactorDuoRequestModel
|
||||
{
|
||||
ClientId = "clientId",
|
||||
ClientSecret = "clientSecret",
|
||||
Host = "example.com"
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = model.ToUser(existingUser);
|
||||
|
||||
// Assert
|
||||
// IKey and SKey should be the same as ClientId and ClientSecret
|
||||
Assert.True(result.GetTwoFactorProviders().ContainsKey(TwoFactorProviderType.Duo));
|
||||
Assert.Equal("clientId", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["ClientId"]);
|
||||
Assert.Equal("clientSecret", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["ClientSecret"]);
|
||||
Assert.Equal("clientId", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["IKey"]);
|
||||
Assert.Equal("clientSecret", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["SKey"]);
|
||||
Assert.Equal("example.com", result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].MetaData["Host"]);
|
||||
Assert.True(result.GetTwoFactorProviders()[TwoFactorProviderType.Duo].Enabled);
|
||||
}
|
||||
}
|
||||
|
@ -8,42 +8,6 @@ namespace Bit.Api.Test.Auth.Models.Response;
|
||||
|
||||
public class OrganizationTwoFactorDuoResponseModelTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void Organization_WithDuoV4_ShouldBuildModel(Organization organization)
|
||||
{
|
||||
// Arrange
|
||||
organization.TwoFactorProviders = GetTwoFactorOrganizationDuoV4ProvidersJson();
|
||||
|
||||
// Act
|
||||
var model = new TwoFactorDuoResponseModel(organization);
|
||||
|
||||
// Assert if v4 data Ikey and Skey are set to clientId and clientSecret
|
||||
Assert.NotNull(model);
|
||||
Assert.Equal("clientId", model.ClientId);
|
||||
Assert.Equal("secret************", model.ClientSecret);
|
||||
Assert.Equal("clientId", model.IntegrationKey);
|
||||
Assert.Equal("secret************", model.SecretKey);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void Organization_WithDuoV2_ShouldBuildModel(Organization organization)
|
||||
{
|
||||
// Arrange
|
||||
organization.TwoFactorProviders = GetTwoFactorOrganizationDuoV2ProvidersJson();
|
||||
|
||||
// Act
|
||||
var model = new TwoFactorDuoResponseModel(organization);
|
||||
|
||||
// Assert if only v2 data clientId and clientSecret are set to Ikey and Sk
|
||||
Assert.NotNull(model);
|
||||
Assert.Equal("IKey", model.ClientId);
|
||||
Assert.Equal("SKey", model.ClientSecret);
|
||||
Assert.Equal("IKey", model.IntegrationKey);
|
||||
Assert.Equal("SKey", model.SecretKey);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void Organization_WithDuo_ShouldBuildModel(Organization organization)
|
||||
@ -54,12 +18,10 @@ public class OrganizationTwoFactorDuoResponseModelTests
|
||||
// Act
|
||||
var model = new TwoFactorDuoResponseModel(organization);
|
||||
|
||||
/// Assert Even if both versions are present priority is given to v4 data
|
||||
// Assert
|
||||
Assert.NotNull(model);
|
||||
Assert.Equal("clientId", model.ClientId);
|
||||
Assert.Equal("secret************", model.ClientSecret);
|
||||
Assert.Equal("clientId", model.IntegrationKey);
|
||||
Assert.Equal("secret************", model.SecretKey);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@ -72,38 +34,33 @@ public class OrganizationTwoFactorDuoResponseModelTests
|
||||
// Act
|
||||
var model = new TwoFactorDuoResponseModel(organization);
|
||||
|
||||
/// Assert
|
||||
// Assert
|
||||
Assert.False(model.Enabled);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void Organization_WithTwoFactorProvidersNull_ShouldFail(Organization organization)
|
||||
public void Organization_WithTwoFactorProvidersNull_ShouldThrow(Organization organization)
|
||||
{
|
||||
// Arrange
|
||||
organization.TwoFactorProviders = "{\"6\" : {}}";
|
||||
organization.TwoFactorProviders = null;
|
||||
|
||||
// Act
|
||||
var model = new TwoFactorDuoResponseModel(organization);
|
||||
try
|
||||
{
|
||||
var model = new TwoFactorDuoResponseModel(organization);
|
||||
|
||||
/// Assert
|
||||
Assert.False(model.Enabled);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Assert
|
||||
Assert.IsType<ArgumentNullException>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTwoFactorOrganizationDuoProvidersJson()
|
||||
{
|
||||
return
|
||||
"{\"6\":{\"Enabled\":true,\"MetaData\":{\"SKey\":\"SKey\",\"IKey\":\"IKey\",\"ClientSecret\":\"secretClientSecret\",\"ClientId\":\"clientId\",\"Host\":\"example.com\"}}}";
|
||||
}
|
||||
|
||||
private string GetTwoFactorOrganizationDuoV4ProvidersJson()
|
||||
{
|
||||
return
|
||||
"{\"6\":{\"Enabled\":true,\"MetaData\":{\"ClientSecret\":\"secretClientSecret\",\"ClientId\":\"clientId\",\"Host\":\"example.com\"}}}";
|
||||
}
|
||||
|
||||
private string GetTwoFactorOrganizationDuoV2ProvidersJson()
|
||||
{
|
||||
return "{\"6\":{\"Enabled\":true,\"MetaData\":{\"SKey\":\"SKey\",\"IKey\":\"IKey\",\"Host\":\"example.com\"}}}";
|
||||
}
|
||||
}
|
||||
|
@ -10,38 +10,21 @@ public class UserTwoFactorDuoResponseModelTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void User_WithDuoV4_ShouldBuildModel(User user)
|
||||
public void User_WithDuo_UserNull_ThrowsArgumentException(User user)
|
||||
{
|
||||
// Arrange
|
||||
user.TwoFactorProviders = GetTwoFactorDuoV4ProvidersJson();
|
||||
user.TwoFactorProviders = GetTwoFactorDuoProvidersJson();
|
||||
|
||||
// Act
|
||||
var model = new TwoFactorDuoResponseModel(user);
|
||||
|
||||
// Assert if v4 data Ikey and Skey are set to clientId and clientSecret
|
||||
Assert.NotNull(model);
|
||||
Assert.Equal("clientId", model.ClientId);
|
||||
Assert.Equal("secret************", model.ClientSecret);
|
||||
Assert.Equal("clientId", model.IntegrationKey);
|
||||
Assert.Equal("secret************", model.SecretKey);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void User_WithDuov2_ShouldBuildModel(User user)
|
||||
{
|
||||
// Arrange
|
||||
user.TwoFactorProviders = GetTwoFactorDuoV2ProvidersJson();
|
||||
|
||||
// Act
|
||||
var model = new TwoFactorDuoResponseModel(user);
|
||||
|
||||
// Assert if only v2 data clientId and clientSecret are set to Ikey and Skey
|
||||
Assert.NotNull(model);
|
||||
Assert.Equal("IKey", model.ClientId);
|
||||
Assert.Equal("SKey", model.ClientSecret);
|
||||
Assert.Equal("IKey", model.IntegrationKey);
|
||||
Assert.Equal("SKey", model.SecretKey);
|
||||
try
|
||||
{
|
||||
var model = new TwoFactorDuoResponseModel(null as User);
|
||||
}
|
||||
catch (ArgumentNullException e)
|
||||
{
|
||||
// Assert
|
||||
Assert.Equal("Value cannot be null. (Parameter 'user')", e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@ -54,12 +37,10 @@ public class UserTwoFactorDuoResponseModelTests
|
||||
// Act
|
||||
var model = new TwoFactorDuoResponseModel(user);
|
||||
|
||||
// Assert Even if both versions are present priority is given to v4 data
|
||||
// Assert
|
||||
Assert.NotNull(model);
|
||||
Assert.Equal("clientId", model.ClientId);
|
||||
Assert.Equal("secret************", model.ClientSecret);
|
||||
Assert.Equal("clientId", model.IntegrationKey);
|
||||
Assert.Equal("secret************", model.SecretKey);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@ -84,26 +65,23 @@ public class UserTwoFactorDuoResponseModelTests
|
||||
user.TwoFactorProviders = null;
|
||||
|
||||
// Act
|
||||
var model = new TwoFactorDuoResponseModel(user);
|
||||
try
|
||||
{
|
||||
var model = new TwoFactorDuoResponseModel(user);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Assert
|
||||
Assert.IsType<ArgumentNullException>(ex);
|
||||
|
||||
}
|
||||
|
||||
/// Assert
|
||||
Assert.False(model.Enabled);
|
||||
}
|
||||
|
||||
private string GetTwoFactorDuoProvidersJson()
|
||||
{
|
||||
return
|
||||
"{\"2\":{\"Enabled\":true,\"MetaData\":{\"SKey\":\"SKey\",\"IKey\":\"IKey\",\"ClientSecret\":\"secretClientSecret\",\"ClientId\":\"clientId\",\"Host\":\"example.com\"}}}";
|
||||
}
|
||||
|
||||
private string GetTwoFactorDuoV4ProvidersJson()
|
||||
{
|
||||
return
|
||||
"{\"2\":{\"Enabled\":true,\"MetaData\":{\"ClientSecret\":\"secretClientSecret\",\"ClientId\":\"clientId\",\"Host\":\"example.com\"}}}";
|
||||
}
|
||||
|
||||
private string GetTwoFactorDuoV2ProvidersJson()
|
||||
{
|
||||
return "{\"2\":{\"Enabled\":true,\"MetaData\":{\"SKey\":\"SKey\",\"IKey\":\"IKey\",\"Host\":\"example.com\"}}}";
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ namespace Bit.Test.Common.AutoFixture.Attributes;
|
||||
public abstract class BitCustomizeAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// /// Gets a customization for the method's parameters.
|
||||
/// Gets a customization for the method's parameters.
|
||||
/// </summary>
|
||||
/// <returns>A customization for the method's paramters.</returns>
|
||||
/// <returns>A customization for the method's parameters.</returns>
|
||||
public abstract ICustomization GetCustomization();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
|
@ -19,7 +19,6 @@ public abstract class BaseTokenProviderTests<T>
|
||||
{
|
||||
public abstract TwoFactorProviderType TwoFactorProviderType { get; }
|
||||
|
||||
#region Helpers
|
||||
protected static IEnumerable<object[]> SetupCanGenerateData(params (Dictionary<string, object> MetaData, bool ExpectedResponse)[] data)
|
||||
{
|
||||
return data.Select(d =>
|
||||
@ -48,6 +47,9 @@ public abstract class BaseTokenProviderTests<T>
|
||||
userService
|
||||
.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType, user)
|
||||
.Returns(true);
|
||||
userService
|
||||
.CanAccessPremium(user)
|
||||
.Returns(true);
|
||||
}
|
||||
|
||||
protected static UserManager<User> SubstituteUserManager()
|
||||
@ -76,7 +78,6 @@ public abstract class BaseTokenProviderTests<T>
|
||||
|
||||
user.TwoFactorProviders = JsonHelpers.LegacySerialize(providers);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public virtual async Task RunCanGenerateTwoFactorTokenAsync(Dictionary<string, object> metaData, bool expectedResponse,
|
||||
User user, SutProvider<T> sutProvider)
|
||||
|
@ -0,0 +1,262 @@
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Auth.Models;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Tokens;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using Duo = DuoUniversal;
|
||||
|
||||
namespace Bit.Core.Test.Auth.Identity;
|
||||
|
||||
public class DuoUniversalTwoFactorTokenProviderTests : BaseTokenProviderTests<DuoUniversalTokenProvider>
|
||||
{
|
||||
private readonly IDuoUniversalTokenService _duoUniversalTokenService = Substitute.For<IDuoUniversalTokenService>();
|
||||
public override TwoFactorProviderType TwoFactorProviderType => TwoFactorProviderType.Duo;
|
||||
|
||||
public static IEnumerable<object[]> CanGenerateTwoFactorTokenAsyncData
|
||||
=> SetupCanGenerateData(
|
||||
( // correct data
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["ClientId"] = new string('c', 20),
|
||||
["ClientSecret"] = new string('s', 40),
|
||||
["Host"] = "https://api-abcd1234.duosecurity.com",
|
||||
},
|
||||
true
|
||||
),
|
||||
( // correct data duo federal
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["ClientId"] = new string('c', 20),
|
||||
["ClientSecret"] = new string('s', 40),
|
||||
["Host"] = "https://api-abcd1234.duofederal.com",
|
||||
},
|
||||
true
|
||||
),
|
||||
( // correct data duo federal
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["ClientId"] = new string('c', 20),
|
||||
["ClientSecret"] = new string('s', 40),
|
||||
["Host"] = "https://api-abcd1234.duofederal.com",
|
||||
},
|
||||
true
|
||||
),
|
||||
( // invalid host
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["ClientId"] = new string('c', 20),
|
||||
["ClientSecret"] = new string('s', 40),
|
||||
["Host"] = "",
|
||||
},
|
||||
false
|
||||
),
|
||||
( // clientId missing
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["ClientSecret"] = new string('s', 40),
|
||||
["Host"] = "https://api-abcd1234.duofederal.com",
|
||||
},
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
public static IEnumerable<object[]> NonPremiumCanGenerateTwoFactorTokenAsyncData
|
||||
=> SetupCanGenerateData(
|
||||
( // correct data
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["ClientId"] = new string('c', 20),
|
||||
["ClientSecret"] = new string('s', 40),
|
||||
["Host"] = "https://api-abcd1234.duosecurity.com",
|
||||
},
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
[Theory, BitMemberAutoData(nameof(CanGenerateTwoFactorTokenAsyncData))]
|
||||
public override async Task RunCanGenerateTwoFactorTokenAsync(Dictionary<string, object> metaData, bool expectedResponse,
|
||||
User user, SutProvider<DuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.Premium = true;
|
||||
user.PremiumExpirationDate = DateTime.UtcNow.AddDays(1);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.HasProperDuoMetadata(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(expectedResponse);
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
await base.RunCanGenerateTwoFactorTokenAsync(metaData, expectedResponse, user, sutProvider);
|
||||
}
|
||||
|
||||
[Theory, BitMemberAutoData(nameof(NonPremiumCanGenerateTwoFactorTokenAsyncData))]
|
||||
public async Task CanGenerateTwoFactorTokenAsync_UserCanNotAccessPremium_ReturnsNull(Dictionary<string, object> metaData, bool expectedResponse,
|
||||
User user, SutProvider<DuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.Premium = false;
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.HasProperDuoMetadata(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(expectedResponse);
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
await base.RunCanGenerateTwoFactorTokenAsync(metaData, expectedResponse, user, sutProvider);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GenerateToken_Success_ReturnsAuthUrl(
|
||||
User user, SutProvider<DuoUniversalTokenProvider> sutProvider, string authUrl)
|
||||
{
|
||||
// Arrange
|
||||
SetUpProperDuoUniversalTokenService(user, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.GenerateAuthUrl(
|
||||
Arg.Any<Duo.Client>(),
|
||||
Arg.Any<IDataProtectorTokenFactory<DuoUserStateTokenable>>(),
|
||||
user)
|
||||
.Returns(authUrl);
|
||||
|
||||
// Act
|
||||
var token = await sutProvider.Sut.GenerateAsync("purpose", SubstituteUserManager(), user);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(token);
|
||||
Assert.Equal(token, authUrl);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GenerateToken_DuoClientNull_ReturnsNull(
|
||||
User user, SutProvider<DuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.Premium = true;
|
||||
user.TwoFactorProviders = GetTwoFactorDuoProvidersJson();
|
||||
AdditionalSetup(sutProvider, user);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.HasProperDuoMetadata(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.BuildDuoTwoFactorClientAsync(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(null as Duo.Client);
|
||||
|
||||
// Act
|
||||
var token = await sutProvider.Sut.GenerateAsync("purpose", SubstituteUserManager(), user);
|
||||
|
||||
// Assert
|
||||
Assert.Null(token);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GenerateToken_UserCanNotAccessPremium_ReturnsNull(
|
||||
User user, SutProvider<DuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
user.Premium = false;
|
||||
user.TwoFactorProviders = GetTwoFactorDuoProvidersJson();
|
||||
AdditionalSetup(sutProvider, user);
|
||||
|
||||
// Act
|
||||
var token = await sutProvider.Sut.GenerateAsync("purpose", SubstituteUserManager(), user);
|
||||
|
||||
// Assert
|
||||
Assert.Null(token);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateToken_ValidToken_ReturnsTrue(
|
||||
User user, SutProvider<DuoUniversalTokenProvider> sutProvider, string token)
|
||||
{
|
||||
// Arrange
|
||||
SetUpProperDuoUniversalTokenService(user, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.RequestDuoValidationAsync(
|
||||
Arg.Any<Duo.Client>(),
|
||||
Arg.Any<IDataProtectorTokenFactory<DuoUserStateTokenable>>(),
|
||||
user,
|
||||
token)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var response = await sutProvider.Sut.ValidateAsync("purpose", token, SubstituteUserManager(), user);
|
||||
|
||||
// Assert
|
||||
Assert.True(response);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateToken_DuoClientNull_ReturnsFalse(
|
||||
User user, SutProvider<DuoUniversalTokenProvider> sutProvider, string token)
|
||||
{
|
||||
user.Premium = true;
|
||||
user.TwoFactorProviders = GetTwoFactorDuoProvidersJson();
|
||||
AdditionalSetup(sutProvider, user);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.HasProperDuoMetadata(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.BuildDuoTwoFactorClientAsync(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(null as Duo.Client);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync("purpose", token, SubstituteUserManager(), user);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the IDuoUniversalTokenService is properly setup for the test.
|
||||
/// This ensures that the private GetDuoClientAsync, and GetDuoTwoFactorProvider
|
||||
/// methods will return true enabling the test to execute on the correct path.
|
||||
/// </summary>
|
||||
/// <param name="user">user from calling test</param>
|
||||
/// <param name="sutProvider">self</param>
|
||||
private void SetUpProperDuoUniversalTokenService(User user, SutProvider<DuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
user.Premium = true;
|
||||
user.TwoFactorProviders = GetTwoFactorDuoProvidersJson();
|
||||
var client = BuildDuoClient();
|
||||
|
||||
AdditionalSetup(sutProvider, user);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.HasProperDuoMetadata(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.BuildDuoTwoFactorClientAsync(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(client);
|
||||
}
|
||||
|
||||
private Duo.Client BuildDuoClient()
|
||||
{
|
||||
var clientId = new string('c', 20);
|
||||
var clientSecret = new string('s', 40);
|
||||
return new Duo.ClientBuilder(clientId, clientSecret, "api-abcd1234.duosecurity.com", "redirectUrl").Build();
|
||||
}
|
||||
|
||||
private string GetTwoFactorDuoProvidersJson()
|
||||
{
|
||||
return
|
||||
"{\"2\":{\"Enabled\":true,\"MetaData\":{\"ClientSecret\":\"secretClientSecret\",\"ClientId\":\"clientId\",\"Host\":\"example.com\"}}}";
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
|
@ -0,0 +1,289 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Auth.Models;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Tokens;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using Duo = DuoUniversal;
|
||||
|
||||
namespace Bit.Core.Test.Auth.Identity;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class OrganizationDuoUniversalTwoFactorTokenProviderTests
|
||||
{
|
||||
private readonly IDuoUniversalTokenService _duoUniversalTokenService = Substitute.For<IDuoUniversalTokenService>();
|
||||
private readonly IDataProtectorTokenFactory<DuoUserStateTokenable> _tokenDataFactory = Substitute.For<IDataProtectorTokenFactory<DuoUserStateTokenable>>();
|
||||
|
||||
// Happy path
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task CanGenerateTwoFactorTokenAsync_ReturnsTrue(
|
||||
Organization organization, SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
organization.Enabled = true;
|
||||
organization.Use2fa = true;
|
||||
SetUpProperOrganizationDuoUniversalTokenService(null, organization, sutProvider);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.CanGenerateTwoFactorTokenAsync(organization);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task CanGenerateTwoFactorTokenAsync_DuoTwoFactorNotEnabled_ReturnsFalse(
|
||||
Organization organization, SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
organization.TwoFactorProviders = GetTwoFactorOrganizationDuoProviderNotEnabledJson();
|
||||
organization.Use2fa = true;
|
||||
organization.Enabled = true;
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.HasProperDuoMetadata(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(true);
|
||||
// Act
|
||||
var result = await sutProvider.Sut.CanGenerateTwoFactorTokenAsync(null);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task CanGenerateTwoFactorTokenAsync_BadMetaData_ProviderNull_ReturnsFalse(
|
||||
Organization organization, SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
organization.TwoFactorProviders = GetTwoFactorOrganizationDuoProviderJson();
|
||||
organization.Use2fa = true;
|
||||
organization.Enabled = true;
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.HasProperDuoMetadata(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(false);
|
||||
// Act
|
||||
var result = await sutProvider.Sut.CanGenerateTwoFactorTokenAsync(null);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetDuoTwoFactorProvider_OrganizationNull_ReturnsNull(
|
||||
SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
// Act
|
||||
var result = await sutProvider.Sut.CanGenerateTwoFactorTokenAsync(null);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetDuoTwoFactorProvider_OrganizationNotEnabled_ReturnsNull(
|
||||
Organization organization, SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
SetUpProperOrganizationDuoUniversalTokenService(null, organization, sutProvider);
|
||||
organization.Enabled = false;
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.CanGenerateTwoFactorTokenAsync(organization);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetDuoTwoFactorProvider_OrganizationUse2FAFalse_ReturnsNull(
|
||||
Organization organization, SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
SetUpProperOrganizationDuoUniversalTokenService(null, organization, sutProvider);
|
||||
organization.Use2fa = false;
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.CanGenerateTwoFactorTokenAsync(organization);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetDuoClient_ProviderNull_ReturnsNull(
|
||||
SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GenerateAsync(null, default);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetDuoClient_DuoClientNull_ReturnsNull(
|
||||
SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider,
|
||||
Organization organization)
|
||||
{
|
||||
// Arrange
|
||||
organization.TwoFactorProviders = GetTwoFactorOrganizationDuoProviderJson();
|
||||
organization.Use2fa = true;
|
||||
organization.Enabled = true;
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.HasProperDuoMetadata(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.BuildDuoTwoFactorClientAsync(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(null as Duo.Client);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GenerateAsync(organization, default);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GenerateAsync_ReturnsAuthUrl(
|
||||
SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider,
|
||||
Organization organization,
|
||||
User user,
|
||||
string AuthUrl)
|
||||
{
|
||||
// Arrange
|
||||
SetUpProperOrganizationDuoUniversalTokenService(BuildDuoClient(), organization, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.GenerateAuthUrl(Arg.Any<Duo.Client>(), Arg.Any<IDataProtectorTokenFactory<DuoUserStateTokenable>>(), user)
|
||||
.Returns(AuthUrl);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GenerateAsync(organization, user);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(AuthUrl, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GenerateAsync_ClientNull_ReturnsNull(
|
||||
SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider,
|
||||
Organization organization,
|
||||
User user)
|
||||
{
|
||||
// Arrange
|
||||
SetUpProperOrganizationDuoUniversalTokenService(null, organization, sutProvider);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GenerateAsync(organization, user);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_TokenValid_ReturnsTrue(
|
||||
SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider,
|
||||
Organization organization,
|
||||
User user,
|
||||
string token)
|
||||
{
|
||||
// Arrange
|
||||
SetUpProperOrganizationDuoUniversalTokenService(BuildDuoClient(), organization, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.RequestDuoValidationAsync(Arg.Any<Duo.Client>(), Arg.Any<IDataProtectorTokenFactory<DuoUserStateTokenable>>(), user, token)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync(token, organization, user);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_ClientNull_ReturnsFalse(
|
||||
SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider,
|
||||
Organization organization,
|
||||
User user,
|
||||
string token)
|
||||
{
|
||||
// Arrange
|
||||
SetUpProperOrganizationDuoUniversalTokenService(null, organization, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.RequestDuoValidationAsync(Arg.Any<Duo.Client>(), Arg.Any<IDataProtectorTokenFactory<DuoUserStateTokenable>>(), user, token)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync(token, organization, user);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the IDuoUniversalTokenService is properly setup for the test.
|
||||
/// This ensures that the private GetDuoClientAsync, and GetDuoTwoFactorProvider
|
||||
/// methods will return true enabling the test to execute on the correct path.
|
||||
///
|
||||
/// BitAutoData cannot create the Duo.Client since it does not have a public constructor
|
||||
/// so we have to use the ClientBUilder(), the client is not used meaningfully in the tests.
|
||||
/// </summary>
|
||||
/// <param name="user">user from calling test</param>
|
||||
/// <param name="sutProvider">self</param>
|
||||
private void SetUpProperOrganizationDuoUniversalTokenService(
|
||||
Duo.Client client, Organization organization, SutProvider<OrganizationDuoUniversalTokenProvider> sutProvider)
|
||||
{
|
||||
organization.TwoFactorProviders = GetTwoFactorOrganizationDuoProviderJson();
|
||||
organization.Enabled = true;
|
||||
organization.Use2fa = true;
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.HasProperDuoMetadata(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IDuoUniversalTokenService>()
|
||||
.BuildDuoTwoFactorClientAsync(Arg.Any<TwoFactorProvider>())
|
||||
.Returns(client);
|
||||
}
|
||||
|
||||
private Duo.Client BuildDuoClient()
|
||||
{
|
||||
var clientId = new string('c', 20);
|
||||
var clientSecret = new string('s', 40);
|
||||
return new Duo.ClientBuilder(clientId, clientSecret, "api-abcd1234.duosecurity.com", "redirectUrl").Build();
|
||||
}
|
||||
|
||||
private string GetTwoFactorOrganizationDuoProviderJson()
|
||||
{
|
||||
return
|
||||
"{\"6\":{\"Enabled\":true,\"MetaData\":{\"ClientSecret\":\"secretClientSecret\",\"ClientId\":\"clientId\",\"Host\":\"example.com\"}}}";
|
||||
}
|
||||
|
||||
private string GetTwoFactorOrganizationDuoProviderNotEnabledJson()
|
||||
{
|
||||
return
|
||||
"{\"6\":{\"Enabled\":false,\"MetaData\":{\"ClientSecret\":\"secretClientSecret\",\"ClientId\":\"clientId\",\"Host\":\"example.com\"}}}";
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Auth.Models;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Auth.Services;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class DuoUniversalTokenServiceTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData("", "ClientId", "ClientSecret")]
|
||||
[BitAutoData("api-valid.duosecurity.com", "", "ClientSecret")]
|
||||
[BitAutoData("api-valid.duosecurity.com", "ClientId", "")]
|
||||
public async void ValidateDuoConfiguration_InvalidConfig_ReturnsFalse(
|
||||
string host, string clientId, string clientSecret, SutProvider<DuoUniversalTokenService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
/* AutoData handles arrangement */
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateDuoConfiguration(clientSecret, clientId, host);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(true, "api-valid.duosecurity.com")]
|
||||
[BitAutoData(false, "invalid")]
|
||||
[BitAutoData(false, "api-valid.duosecurity.com", null, "clientSecret")]
|
||||
[BitAutoData(false, "api-valid.duosecurity.com", "ClientId", null)]
|
||||
[BitAutoData(false, "api-valid.duosecurity.com", null, null)]
|
||||
public void HasProperDuoMetadata_ReturnMatchesExpected(
|
||||
bool expectedResponse, string host, string clientId,
|
||||
string clientSecret, SutProvider<DuoUniversalTokenService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var metaData = new Dictionary<string, object> { ["Host"] = host };
|
||||
|
||||
if (clientId != null)
|
||||
{
|
||||
metaData.Add("ClientId", clientId);
|
||||
}
|
||||
|
||||
if (clientSecret != null)
|
||||
{
|
||||
metaData.Add("ClientSecret", clientSecret);
|
||||
}
|
||||
|
||||
var provider = new TwoFactorProvider
|
||||
{
|
||||
MetaData = metaData
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.HasProperDuoMetadata(provider);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(result, expectedResponse);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void HasProperDuoMetadata_ProviderIsNull_ReturnsFalse(
|
||||
SutProvider<DuoUniversalTokenService> sutProvider)
|
||||
{
|
||||
// Act
|
||||
var result = sutProvider.Sut.HasProperDuoMetadata(null);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData("api-valid.duosecurity.com", true)]
|
||||
[BitAutoData("api-valid.duofederal.com", true)]
|
||||
[BitAutoData("invalid", false)]
|
||||
public void ValidDuoHost_HostIsValid_ReturnTrue(
|
||||
string host, bool expectedResponse)
|
||||
{
|
||||
// Act
|
||||
var result = DuoUniversalTokenService.ValidDuoHost(host);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(result, expectedResponse);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -28,7 +28,7 @@ namespace Bit.Identity.IntegrationTest.Endpoints;
|
||||
|
||||
public class IdentityServerTwoFactorTests : IClassFixture<IdentityApplicationFactory>
|
||||
{
|
||||
const string _organizationTwoFactor = """{"6":{"Enabled":true,"MetaData":{"IKey":"DIEFB13LB49IEB3459N2","SKey":"0ZnsZHav0KcNPBZTS6EOUwqLPoB0sfMd5aJeWExQ","Host":"api-example.duosecurity.com"}}}""";
|
||||
const string _organizationTwoFactor = """{"6":{"Enabled":true,"MetaData":{"ClientId":"DIEFB13LB49IEB3459N2","ClientSecret":"0ZnsZHav0KcNPBZTS6EOUwqLPoB0sfMd5aJeWExQ","Host":"api-example.duosecurity.com"}}}""";
|
||||
const string _testEmail = "test+2farequired@email.com";
|
||||
const string _testPassword = "master_password_hash";
|
||||
const string _userEmailTwoFactor = """{"1": { "Enabled": true, "MetaData": { "Email": "test+2farequired@email.com"}}}""";
|
||||
@ -140,7 +140,7 @@ public class IdentityServerTwoFactorTests : IClassFixture<IdentityApplicationFac
|
||||
{ "password", _testPassword },
|
||||
}), context => context.Request.Headers.Append("Auth-Email", CoreHelpers.Base64UrlEncodeString(_testEmail)));
|
||||
|
||||
// Assert
|
||||
// Assert
|
||||
using var responseBody = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
||||
var root = responseBody.RootElement;
|
||||
var error = AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String).GetString();
|
||||
@ -168,7 +168,7 @@ public class IdentityServerTwoFactorTests : IClassFixture<IdentityApplicationFac
|
||||
[Theory, BitAutoData]
|
||||
public async Task TokenEndpoint_GrantTypeClientCredential_OrgTwoFactorRequired_Success(Organization organization, OrganizationApiKey organizationApiKey)
|
||||
{
|
||||
// Arrange
|
||||
// Arrange
|
||||
organization.Enabled = true;
|
||||
organization.UseApi = true;
|
||||
organization.Use2fa = true;
|
||||
@ -258,7 +258,7 @@ public class IdentityServerTwoFactorTests : IClassFixture<IdentityApplicationFac
|
||||
{ "redirect_uri", "https://localhost:8080/sso-connector.html" }
|
||||
}), context => context.Request.Headers.Append("Auth-Email", CoreHelpers.Base64UrlEncodeString(_testEmail)));
|
||||
|
||||
// Assert
|
||||
// Assert
|
||||
using var responseBody = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
||||
var root = responseBody.RootElement;
|
||||
var error = AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String).GetString();
|
||||
@ -320,7 +320,7 @@ public class IdentityServerTwoFactorTests : IClassFixture<IdentityApplicationFac
|
||||
}), context => context.Request.Headers.Append("Auth-Email", CoreHelpers.Base64UrlEncodeString(_testEmail)));
|
||||
|
||||
|
||||
// Assert
|
||||
// Assert
|
||||
var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(twoFactorProvidedContext);
|
||||
var root = body.RootElement;
|
||||
|
||||
@ -338,6 +338,7 @@ public class IdentityServerTwoFactorTests : IClassFixture<IdentityApplicationFac
|
||||
{
|
||||
MemberDecryptionType = MemberDecryptionType.MasterPassword,
|
||||
};
|
||||
|
||||
await CreateSsoOrganizationAndUserAsync(
|
||||
localFactory, ssoConfigData, challenge, _testEmail, orgTwoFactor: _organizationTwoFactor);
|
||||
|
||||
@ -355,7 +356,7 @@ public class IdentityServerTwoFactorTests : IClassFixture<IdentityApplicationFac
|
||||
{ "redirect_uri", "https://localhost:8080/sso-connector.html" }
|
||||
}), context => context.Request.Headers.Append("Auth-Email", CoreHelpers.Base64UrlEncodeString(_testEmail)));
|
||||
|
||||
// Assert
|
||||
// Assert
|
||||
using var responseBody = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
||||
var root = responseBody.RootElement;
|
||||
var error = AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String).GetString();
|
||||
|
@ -1,8 +1,6 @@
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.Models;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
@ -28,8 +26,7 @@ public class TwoFactorAuthenticationValidatorTests
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly UserManagerTestWrapper<User> _userManager;
|
||||
private readonly IOrganizationDuoWebTokenProvider _organizationDuoWebTokenProvider;
|
||||
private readonly ITemporaryDuoWebV4SDKService _temporaryDuoWebV4SDKService;
|
||||
private readonly IOrganizationDuoUniversalTokenProvider _organizationDuoUniversalTokenProvider;
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly IApplicationCacheService _applicationCacheService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
@ -42,8 +39,7 @@ public class TwoFactorAuthenticationValidatorTests
|
||||
{
|
||||
_userService = Substitute.For<IUserService>();
|
||||
_userManager = SubstituteUserManager();
|
||||
_organizationDuoWebTokenProvider = Substitute.For<IOrganizationDuoWebTokenProvider>();
|
||||
_temporaryDuoWebV4SDKService = Substitute.For<ITemporaryDuoWebV4SDKService>();
|
||||
_organizationDuoUniversalTokenProvider = Substitute.For<IOrganizationDuoUniversalTokenProvider>();
|
||||
_featureService = Substitute.For<IFeatureService>();
|
||||
_applicationCacheService = Substitute.For<IApplicationCacheService>();
|
||||
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||
@ -54,8 +50,7 @@ public class TwoFactorAuthenticationValidatorTests
|
||||
_sut = new TwoFactorAuthenticationValidator(
|
||||
_userService,
|
||||
_userManager,
|
||||
_organizationDuoWebTokenProvider,
|
||||
_temporaryDuoWebV4SDKService,
|
||||
_organizationDuoUniversalTokenProvider,
|
||||
_featureService,
|
||||
_applicationCacheService,
|
||||
_organizationUserRepository,
|
||||
@ -439,7 +434,7 @@ public class TwoFactorAuthenticationValidatorTests
|
||||
string token)
|
||||
{
|
||||
// Arrange
|
||||
_organizationDuoWebTokenProvider.ValidateAsync(
|
||||
_organizationDuoUniversalTokenProvider.ValidateAsync(
|
||||
token, organization, user).Returns(true);
|
||||
|
||||
_userManager.TWO_FACTOR_ENABLED = true;
|
||||
@ -457,70 +452,6 @@ public class TwoFactorAuthenticationValidatorTests
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(TwoFactorProviderType.Duo)]
|
||||
[BitAutoData(TwoFactorProviderType.OrganizationDuo)]
|
||||
public async void VerifyTwoFactorAsync_TemporaryDuoService_ValidToken_ReturnsTrue(
|
||||
TwoFactorProviderType providerType,
|
||||
User user,
|
||||
Organization organization,
|
||||
string token)
|
||||
{
|
||||
// Arrange
|
||||
_featureService.IsEnabled(FeatureFlagKeys.DuoRedirect).Returns(true);
|
||||
_userService.TwoFactorProviderIsEnabledAsync(providerType, user).Returns(true);
|
||||
_temporaryDuoWebV4SDKService.ValidateAsync(
|
||||
token, Arg.Any<TwoFactorProvider>(), user).Returns(true);
|
||||
|
||||
user.TwoFactorProviders = GetTwoFactorIndividualProviderJson(providerType);
|
||||
organization.Use2fa = true;
|
||||
organization.TwoFactorProviders = GetTwoFactorOrganizationDuoProviderJson();
|
||||
organization.Enabled = true;
|
||||
|
||||
_userManager.TWO_FACTOR_ENABLED = true;
|
||||
_userManager.TWO_FACTOR_TOKEN = token;
|
||||
_userManager.TWO_FACTOR_TOKEN_VERIFIED = true;
|
||||
|
||||
// Act
|
||||
var result = await _sut.VerifyTwoFactor(
|
||||
user, organization, providerType, token);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(TwoFactorProviderType.Duo)]
|
||||
[BitAutoData(TwoFactorProviderType.OrganizationDuo)]
|
||||
public async void VerifyTwoFactorAsync_TemporaryDuoService_InvalidToken_ReturnsFalse(
|
||||
TwoFactorProviderType providerType,
|
||||
User user,
|
||||
Organization organization,
|
||||
string token)
|
||||
{
|
||||
// Arrange
|
||||
_featureService.IsEnabled(FeatureFlagKeys.DuoRedirect).Returns(true);
|
||||
_userService.TwoFactorProviderIsEnabledAsync(providerType, user).Returns(true);
|
||||
_temporaryDuoWebV4SDKService.ValidateAsync(
|
||||
token, Arg.Any<TwoFactorProvider>(), user).Returns(true);
|
||||
|
||||
user.TwoFactorProviders = GetTwoFactorIndividualProviderJson(providerType);
|
||||
organization.Use2fa = true;
|
||||
organization.TwoFactorProviders = GetTwoFactorOrganizationDuoProviderJson();
|
||||
organization.Enabled = true;
|
||||
|
||||
_userManager.TWO_FACTOR_ENABLED = true;
|
||||
_userManager.TWO_FACTOR_TOKEN = token;
|
||||
_userManager.TWO_FACTOR_TOKEN_VERIFIED = false;
|
||||
|
||||
// Act
|
||||
var result = await _sut.VerifyTwoFactor(
|
||||
user, organization, providerType, token);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
private static UserManagerTestWrapper<User> SubstituteUserManager()
|
||||
{
|
||||
return new UserManagerTestWrapper<User>(
|
||||
|
Reference in New Issue
Block a user