1
0
mirror of https://github.com/bitwarden/server synced 2026-02-08 20:50:13 +00:00
Files
server/test/Core.Test/Auth/Services/DuoUniversalTokenServiceTests.cs

399 lines
14 KiB
C#

using System.Reflection;
using Bit.Core.Auth.Identity.TokenProviders;
using Bit.Core.Auth.Models;
using Bit.Core.Context;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.AspNetCore.Http;
using NSubstitute;
using Xunit;
using CoreSettings = Bit.Core.Settings;
namespace Bit.Core.Test.Auth.Services;
[SutProviderCustomize]
public class DuoUniversalTokenServiceTests
{
/// <summary>
/// Helper method to invoke the private BuildDuoTwoFactorRedirectUri method via reflection.
/// </summary>
private static string InvokeBuildDuoTwoFactorRedirectUri(DuoUniversalTokenService sut)
{
var method = typeof(DuoUniversalTokenService).GetMethod(
"BuildDuoTwoFactorRedirectUri",
BindingFlags.NonPublic | BindingFlags.Instance);
return (string)method!.Invoke(sut, null)!;
}
[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);
}
[Fact]
public void BuildDuoTwoFactorRedirectUri_MobileClient_NoOverride_DefaultsToBitwardenScheme()
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = "mobile";
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = InvokeBuildDuoTwoFactorRedirectUri(sut);
// Assert
Assert.Contains("client=mobile", result);
Assert.Contains("deeplinkScheme=bitwarden", result);
}
[Theory]
[BitAutoData("https")]
[BitAutoData("bitwarden")]
public void BuildDuoTwoFactorRedirectUri_MobileClient_WithFormOverride_UsesOverrideScheme(
string schemeOverride)
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = "mobile";
httpContext.Request.ContentType = "application/x-www-form-urlencoded";
httpContext.Request.Form = new FormCollection(new Dictionary<string, Microsoft.Extensions.Primitives.StringValues>
{
{ "deeplinkScheme", schemeOverride }
});
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = InvokeBuildDuoTwoFactorRedirectUri(sut);
// Assert
Assert.Contains("client=mobile", result);
Assert.Contains($"deeplinkScheme={schemeOverride.ToLowerInvariant()}", result);
}
[Fact]
public void BuildDuoTwoFactorRedirectUri_MobileClient_FormOverrideTakesPrecedenceOverHeader()
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = "mobile";
httpContext.Request.Headers["Bitwarden-Deeplink-Scheme"] = "bitwarden";
httpContext.Request.ContentType = "application/x-www-form-urlencoded";
httpContext.Request.Form = new FormCollection(new Dictionary<string, Microsoft.Extensions.Primitives.StringValues>
{
{ "deeplinkScheme", "https" }
});
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = InvokeBuildDuoTwoFactorRedirectUri(sut);
// Assert
Assert.Contains("client=mobile", result);
Assert.Contains("deeplinkScheme=https", result);
}
[Theory]
[BitAutoData("invalid")]
[BitAutoData("unknown")]
[BitAutoData("")]
public void BuildDuoTwoFactorRedirectUri_MobileClient_InvalidOverride_DefaultsToBitwardenScheme(
string invalidScheme)
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = "mobile";
httpContext.Request.Headers["Bitwarden-Deeplink-Scheme"] = invalidScheme;
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = InvokeBuildDuoTwoFactorRedirectUri(sut);
// Assert
Assert.Contains("client=mobile", result);
Assert.Contains("deeplinkScheme=bitwarden", result);
}
[Theory]
[BitAutoData("selfhosted.example.com")]
[BitAutoData("192.168.1.100")]
public void BuildDuoTwoFactorRedirectUri_MobileClient_SelfHosted_ReturnsBitwardenScheme(
string requestHost)
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = "mobile";
httpContext.Request.Host = new HostString(requestHost);
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.example.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = InvokeBuildDuoTwoFactorRedirectUri(sut);
// Assert
Assert.Contains("client=mobile", result);
Assert.Contains("deeplinkScheme=bitwarden", result);
}
[Fact]
public void BuildDuoTwoFactorRedirectUri_DesktopClient_ReturnsBitwardenScheme()
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = "desktop";
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = InvokeBuildDuoTwoFactorRedirectUri(sut);
// Assert
Assert.Contains("client=desktop", result);
Assert.Contains("deeplinkScheme=bitwarden", result);
}
[Theory]
[BitAutoData("web")]
[BitAutoData("browser")]
[BitAutoData("cli")]
public void BuildDuoTwoFactorRedirectUri_NonMobileNonDesktopClient_NoDeeplinkScheme(
string clientName)
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = clientName;
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = InvokeBuildDuoTwoFactorRedirectUri(sut);
// Assert
Assert.Contains($"client={clientName}", result);
Assert.DoesNotContain("deeplinkScheme", result);
}
[Fact]
public void BuildDuoTwoFactorRedirectUri_NoClientHeader_DefaultsToWeb()
{
// Arrange
var httpContext = new DefaultHttpContext();
// No Bitwarden-Client-Name header set
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = InvokeBuildDuoTwoFactorRedirectUri(sut);
// Assert
Assert.Contains("client=web", result);
Assert.DoesNotContain("deeplinkScheme", result);
}
[Theory]
[BitAutoData("invalid-client")]
[BitAutoData("unknown")]
public void BuildDuoTwoFactorRedirectUri_InvalidClientHeader_DefaultsToWeb(
string invalidClientName)
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = invalidClientName;
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = InvokeBuildDuoTwoFactorRedirectUri(sut);
// Assert
Assert.Contains("client=web", result);
Assert.DoesNotContain("deeplinkScheme", result);
}
[Theory]
[BitAutoData("MOBILE")]
[BitAutoData("Mobile")]
[BitAutoData("MoBiLe")]
public void BuildDuoTwoFactorRedirectUri_ClientHeaderCaseInsensitive(
string clientName)
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = clientName;
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = InvokeBuildDuoTwoFactorRedirectUri(sut);
// Assert
Assert.Contains("client=mobile", result);
Assert.Contains("deeplinkScheme=bitwarden", result);
}
}