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 sutProvider) { // Arrange sutProvider.GetDependency() .GetUserByPrincipalAsync(default) .ReturnsForAnyArgs(null as User); // Act var result = () => sutProvider.Sut.GetDuo(request); // Assert await Assert.ThrowsAsync(result); } [Theory, BitAutoData] public async Task CheckAsync_BadSecret_ThrowsBadRequestException(User user, SecretVerificationRequestModel request, SutProvider sutProvider) { // Arrange sutProvider.GetDependency() .GetUserByPrincipalAsync(default) .ReturnsForAnyArgs(user); sutProvider.GetDependency() .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 sutProvider) { // Arrange sutProvider.GetDependency() .GetUserByPrincipalAsync(default) .ReturnsForAnyArgs(user); sutProvider.GetDependency() .VerifySecretAsync(default, default) .ReturnsForAnyArgs(true); sutProvider.GetDependency() .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 sutProvider) { // Arrange user.TwoFactorProviders = GetUserTwoFactorDuoProvidersJson(); SetupCheckAsyncToPass(sutProvider, user); // Act var result = await sutProvider.Sut.GetDuo(request); // Assert Assert.NotNull(result); Assert.IsType(result); } [Theory, BitAutoData] public async Task PutDuo_InvalidConfiguration_ThrowsBadRequestException(User user, UpdateTwoFactorDuoRequestModel request, SutProvider sutProvider) { // Arrange SetupCheckAsyncToPass(sutProvider, user); sutProvider.GetDependency() .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 sutProvider) { // Arrange user.TwoFactorProviders = GetUserTwoFactorDuoProvidersJson(); SetupCheckAsyncToPass(sutProvider, user); sutProvider.GetDependency() .ValidateDuoConfiguration(default, default, default) .ReturnsForAnyArgs(true); // Act var result = await sutProvider.Sut.PutDuo(request); // Assert Assert.NotNull(result); Assert.IsType(result); Assert.Equal(user.TwoFactorProviders, request.ToUser(user).TwoFactorProviders); } [Theory, BitAutoData] public async Task CheckOrganizationAsync_ManagePolicies_ThrowsNotFoundException( User user, Organization organization, SecretVerificationRequestModel request, SutProvider sutProvider) { // Arrange organization.TwoFactorProviders = GetOrganizationTwoFactorDuoProvidersJson(); SetupCheckAsyncToPass(sutProvider, user); sutProvider.GetDependency() .ManagePolicies(default) .ReturnsForAnyArgs(false); // Act var result = () => sutProvider.Sut.GetOrganizationDuo(organization.Id.ToString(), request); // Assert await Assert.ThrowsAsync(result); } [Theory, BitAutoData] public async Task CheckOrganizationAsync_GetByIdAsync_ThrowsNotFoundException( User user, Organization organization, SecretVerificationRequestModel request, SutProvider sutProvider) { // Arrange organization.TwoFactorProviders = GetOrganizationTwoFactorDuoProvidersJson(); SetupCheckAsyncToPass(sutProvider, user); sutProvider.GetDependency() .ManagePolicies(default) .ReturnsForAnyArgs(true); sutProvider.GetDependency() .GetByIdAsync(default) .ReturnsForAnyArgs(null as Organization); // Act var result = () => sutProvider.Sut.GetOrganizationDuo(organization.Id.ToString(), request); // Assert await Assert.ThrowsAsync(result); } [Theory, BitAutoData] public async Task GetOrganizationDuo_Success( User user, Organization organization, SecretVerificationRequestModel request, SutProvider 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(result); } [Theory, BitAutoData] public async Task PutOrganizationDuo_InvalidConfiguration_ThrowsBadRequestException( User user, Organization organization, UpdateTwoFactorDuoRequestModel request, SutProvider sutProvider) { // Arrange SetupCheckAsyncToPass(sutProvider, user); SetupCheckOrganizationAsyncToPass(sutProvider, organization); sutProvider.GetDependency() .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 sutProvider) { // Arrange SetupCheckAsyncToPass(sutProvider, user); SetupCheckOrganizationAsyncToPass(sutProvider, organization); organization.TwoFactorProviders = GetUserTwoFactorDuoProvidersJson(); sutProvider.GetDependency() .ValidateDuoConfiguration(default, default, default) .ReturnsForAnyArgs(true); // Act var result = await sutProvider.Sut.PutOrganizationDuo(organization.Id.ToString(), request); // Assert Assert.NotNull(result); Assert.IsType(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\"}}}"; } /// /// Sets up the CheckAsync method to pass. /// /// uses bit auto data /// uses bit auto data private void SetupCheckAsyncToPass(SutProvider sutProvider, User user) { sutProvider.GetDependency() .GetUserByPrincipalAsync(default) .ReturnsForAnyArgs(user); sutProvider.GetDependency() .VerifySecretAsync(default, default) .ReturnsForAnyArgs(true); sutProvider.GetDependency() .CanAccessPremium(default) .ReturnsForAnyArgs(true); } private void SetupCheckOrganizationAsyncToPass(SutProvider sutProvider, Organization organization) { sutProvider.GetDependency() .ManagePolicies(default) .ReturnsForAnyArgs(true); sutProvider.GetDependency() .GetByIdAsync(default) .ReturnsForAnyArgs(organization); } }