1
0
mirror of https://github.com/bitwarden/server.git synced 2024-12-03 14:03:33 +01:00
bitwarden-server/test/Core.Test/Auth/Identity/OrganizationDuoUniversalTwoFactorTokenProviderTests.cs
Ike ab5d4738d6
[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
2024-11-18 15:58:05 -08:00

290 lines
9.9 KiB
C#

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\"}}}";
}
}