1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 07:36:14 -05:00

Merge branch 'main' into ac/pm-19145-refactor-OrganizationService.ImportAsync

This commit is contained in:
Brandon Treston
2025-05-12 14:18:03 -04:00
committed by GitHub
177 changed files with 1425 additions and 1584 deletions

View File

@ -0,0 +1,39 @@
using System.Net;
using System.Net.Http.Headers;
using Bit.Api.IntegrationTest.Factories;
using Bit.Seeder.Recipes;
using Xunit;
using Xunit.Abstractions;
namespace Bit.Api.IntegrationTest.AdminConsole.Controllers;
public class OrganizationUsersControllerPerformanceTest(ITestOutputHelper testOutputHelper)
{
[Theory(Skip = "Performance test")]
[InlineData(100)]
[InlineData(60000)]
public async Task GetAsync(int seats)
{
await using var factory = new ApiApplicationFactory();
var client = factory.CreateClient();
var db = factory.GetDatabaseContext();
var seeder = new OrganizationWithUsersRecipe(db);
var orgId = seeder.Seed("Org", seats, "large.test");
var tokens = await factory.LoginAsync("admin@large.test", "c55hlJ/cfdvTd4awTXUqow6X3cOQCfGwn11o3HblnPs=");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token);
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
var response = await client.GetAsync($"/organizations/{orgId}/users?includeCollections=true");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.NotEmpty(result);
stopwatch.Stop();
testOutputHelper.WriteLine($"Seed: {seats}; Request duration: {stopwatch.ElapsedMilliseconds} ms");
}
}

View File

@ -18,6 +18,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Api\Api.csproj" />
<ProjectReference Include="..\..\util\Seeder\Seeder.csproj" />
<ProjectReference Include="..\IntegrationTestCommon\IntegrationTestCommon.csproj" />
<Content Include="..\..\src\Api\appsettings.*.json">

View File

@ -304,7 +304,7 @@ public class GroupsControllerPutTests
// Arrange repositories
sutProvider.GetDependency<IGroupRepository>().GetManyUserIdsByIdAsync(group.Id).Returns(currentGroupUsers ?? []);
sutProvider.GetDependency<IGroupRepository>().GetByIdWithCollectionsAsync(group.Id)
.Returns(new Tuple<Group, ICollection<CollectionAccessSelection>>(group, currentCollectionAccess ?? []));
.Returns(new Tuple<Group?, ICollection<CollectionAccessSelection>>(group, currentCollectionAccess ?? []));
if (savingUser != null)
{
sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(orgId, savingUser.UserId!.Value)

View File

@ -2,8 +2,6 @@
<PropertyGroup>
<IsPackable>false</IsPackable>
<!-- Temp exclusions until warnings are fixed -->
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS8620;CS0169</WarningsNotAsErrors>
</PropertyGroup>
<ItemGroup>

View File

@ -14,6 +14,7 @@ using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
@ -40,6 +41,7 @@ public class AccountsControllerTests : IDisposable
private readonly IPolicyService _policyService;
private readonly ISetInitialMasterPasswordCommand _setInitialMasterPasswordCommand;
private readonly IRotateUserKeyCommand _rotateUserKeyCommand;
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
private readonly ITdeOffboardingPasswordCommand _tdeOffboardingPasswordCommand;
private readonly IFeatureService _featureService;
@ -64,6 +66,7 @@ public class AccountsControllerTests : IDisposable
_policyService = Substitute.For<IPolicyService>();
_setInitialMasterPasswordCommand = Substitute.For<ISetInitialMasterPasswordCommand>();
_rotateUserKeyCommand = Substitute.For<IRotateUserKeyCommand>();
_twoFactorIsEnabledQuery = Substitute.For<ITwoFactorIsEnabledQuery>();
_tdeOffboardingPasswordCommand = Substitute.For<ITdeOffboardingPasswordCommand>();
_featureService = Substitute.For<IFeatureService>();
_cipherValidator =
@ -87,6 +90,7 @@ public class AccountsControllerTests : IDisposable
_setInitialMasterPasswordCommand,
_tdeOffboardingPasswordCommand,
_rotateUserKeyCommand,
_twoFactorIsEnabledQuery,
_featureService,
_cipherValidator,
_folderValidator,

View File

@ -8,7 +8,6 @@ using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
@ -23,7 +22,6 @@ public class DevicesControllerTest
private readonly IUntrustDevicesCommand _untrustDevicesCommand;
private readonly IUserRepository _userRepositoryMock;
private readonly ICurrentContext _currentContextMock;
private readonly IGlobalSettings _globalSettingsMock;
private readonly ILogger<DevicesController> _loggerMock;
private readonly DevicesController _sut;

View File

@ -216,6 +216,12 @@ public class OrganizationSponsorshipsControllerTests
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(sponsoringOrg.Id).Returns(sponsorships);
// Set IsAdminInitiated to true for all test sponsorships
foreach (var sponsorship in sponsorships)
{
sponsorship.IsAdminInitiated = true;
}
// Act
var result = await sutProvider.Sut.GetSponsoredOrganizations(sponsoringOrg.Id);

View File

@ -1,107 +0,0 @@
using Bit.Api.Utilities;
using Bit.Core.Models.Commands;
using Bit.Core.Vault.Entities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Xunit;
namespace Bit.Api.Test.Utilities;
public class CommandResultExtensionTests
{
public static IEnumerable<object[]> WithGenericTypeTestCases()
{
yield return new object[]
{
new NoRecordFoundFailure<Cipher>(new[] { "Error 1", "Error 2" }),
new ObjectResult(new[] { "Error 1", "Error 2" }) { StatusCode = StatusCodes.Status404NotFound }
};
yield return new object[]
{
new BadRequestFailure<Cipher>("Error 3"),
new ObjectResult(new[] { "Error 3" }) { StatusCode = StatusCodes.Status400BadRequest }
};
yield return new object[]
{
new Failure<Cipher>("Error 4"),
new ObjectResult(new[] { "Error 4" }) { StatusCode = StatusCodes.Status400BadRequest }
};
var cipher = new Cipher() { Id = Guid.NewGuid() };
yield return new object[]
{
new Success<Cipher>(cipher),
new ObjectResult(cipher) { StatusCode = StatusCodes.Status200OK }
};
}
[Theory]
[MemberData(nameof(WithGenericTypeTestCases))]
public void MapToActionResult_WithGenericType_ShouldMapToHttpResponse(CommandResult<Cipher> input, ObjectResult expected)
{
var result = input.MapToActionResult();
Assert.Equivalent(expected, result);
}
[Fact]
public void MapToActionResult_WithGenericType_ShouldThrowExceptionForUnhandledCommandResult()
{
var result = new NotImplementedCommandResult();
Assert.Throws<InvalidOperationException>(() => result.MapToActionResult());
}
public static IEnumerable<object[]> TestCases()
{
yield return new object[]
{
new NoRecordFoundFailure(new[] { "Error 1", "Error 2" }),
new ObjectResult(new[] { "Error 1", "Error 2" }) { StatusCode = StatusCodes.Status404NotFound }
};
yield return new object[]
{
new BadRequestFailure("Error 3"),
new ObjectResult(new[] { "Error 3" }) { StatusCode = StatusCodes.Status400BadRequest }
};
yield return new object[]
{
new Failure("Error 4"),
new ObjectResult(new[] { "Error 4" }) { StatusCode = StatusCodes.Status400BadRequest }
};
yield return new object[]
{
new Success(),
new ObjectResult(new { }) { StatusCode = StatusCodes.Status200OK }
};
}
[Theory]
[MemberData(nameof(TestCases))]
public void MapToActionResult_ShouldMapToHttpResponse(CommandResult input, ObjectResult expected)
{
var result = input.MapToActionResult();
Assert.Equivalent(expected, result);
}
[Fact]
public void MapToActionResult_ShouldThrowExceptionForUnhandledCommandResult()
{
var result = new NotImplementedCommandResult<Cipher>();
Assert.Throws<InvalidOperationException>(() => result.MapToActionResult());
}
}
public class NotImplementedCommandResult<T> : CommandResult<T>
{
}
public class NotImplementedCommandResult : CommandResult
{
}

View File

@ -8,6 +8,7 @@ using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Models.Data.Provider;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.Models;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
@ -64,6 +65,7 @@ public class SyncControllerTests
{
// Get dependencies
var userService = sutProvider.GetDependency<IUserService>();
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var providerUserRepository = sutProvider.GetDependency<IProviderUserRepository>();
var folderRepository = sutProvider.GetDependency<IFolderRepository>();
@ -119,7 +121,7 @@ public class SyncControllerTests
collectionRepository.GetManyByUserIdAsync(user.Id).Returns(collections);
collectionCipherRepository.GetManyByUserIdAsync(user.Id).Returns(new List<CollectionCipher>());
// Back to standard test setup
userService.TwoFactorIsEnabledAsync(user).Returns(false);
twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user).Returns(false);
userService.HasPremiumFromOrganization(user).Returns(false);
// Execute GET
@ -129,7 +131,7 @@ public class SyncControllerTests
// Asserts
// Assert that methods are called
var hasEnabledOrgs = organizationUserDetails.Any(o => o.Enabled);
await this.AssertMethodsCalledAsync(userService, organizationUserRepository, providerUserRepository, folderRepository,
await this.AssertMethodsCalledAsync(userService, twoFactorIsEnabledQuery, organizationUserRepository, providerUserRepository, folderRepository,
cipherRepository, sendRepository, collectionRepository, collectionCipherRepository, hasEnabledOrgs);
Assert.IsType<SyncResponseModel>(result);
@ -155,6 +157,7 @@ public class SyncControllerTests
{
// Get dependencies
var userService = sutProvider.GetDependency<IUserService>();
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var providerUserRepository = sutProvider.GetDependency<IProviderUserRepository>();
var folderRepository = sutProvider.GetDependency<IFolderRepository>();
@ -205,7 +208,7 @@ public class SyncControllerTests
policyRepository.GetManyByUserIdAsync(user.Id).Returns(policies);
userService.TwoFactorIsEnabledAsync(user).Returns(false);
twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user).Returns(false);
userService.HasPremiumFromOrganization(user).Returns(false);
// Execute GET
@ -216,7 +219,7 @@ public class SyncControllerTests
// Assert that methods are called
var hasEnabledOrgs = organizationUserDetails.Any(o => o.Enabled);
await this.AssertMethodsCalledAsync(userService, organizationUserRepository, providerUserRepository, folderRepository,
await this.AssertMethodsCalledAsync(userService, twoFactorIsEnabledQuery, organizationUserRepository, providerUserRepository, folderRepository,
cipherRepository, sendRepository, collectionRepository, collectionCipherRepository, hasEnabledOrgs);
Assert.IsType<SyncResponseModel>(result);
@ -244,6 +247,7 @@ public class SyncControllerTests
{
// Get dependencies
var userService = sutProvider.GetDependency<IUserService>();
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var providerUserRepository = sutProvider.GetDependency<IProviderUserRepository>();
var folderRepository = sutProvider.GetDependency<IFolderRepository>();
@ -283,7 +287,7 @@ public class SyncControllerTests
collectionRepository.GetManyByUserIdAsync(user.Id).Returns(collections);
collectionCipherRepository.GetManyByUserIdAsync(user.Id).Returns(new List<CollectionCipher>());
// Back to standard test setup
userService.TwoFactorIsEnabledAsync(user).Returns(false);
twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user).Returns(false);
userService.HasPremiumFromOrganization(user).Returns(false);
// Execute GET
@ -293,7 +297,7 @@ public class SyncControllerTests
// Assert that methods are called
var hasEnabledOrgs = organizationUserDetails.Any(o => o.Enabled);
await this.AssertMethodsCalledAsync(userService, organizationUserRepository, providerUserRepository, folderRepository,
await this.AssertMethodsCalledAsync(userService, twoFactorIsEnabledQuery, organizationUserRepository, providerUserRepository, folderRepository,
cipherRepository, sendRepository, collectionRepository, collectionCipherRepository, hasEnabledOrgs);
Assert.IsType<SyncResponseModel>(result);
@ -315,6 +319,7 @@ public class SyncControllerTests
private async Task AssertMethodsCalledAsync(IUserService userService,
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
IOrganizationUserRepository organizationUserRepository,
IProviderUserRepository providerUserRepository, IFolderRepository folderRepository,
ICipherRepository cipherRepository, ISendRepository sendRepository,
@ -356,7 +361,7 @@ public class SyncControllerTests
.GetManyByUserIdAsync(default);
}
await userService.ReceivedWithAnyArgs(1)
await twoFactorIsEnabledQuery.ReceivedWithAnyArgs(1)
.TwoFactorIsEnabledAsync(default(ITwoFactorProvidersUser));
await userService.ReceivedWithAnyArgs(1)
.HasPremiumFromOrganization(default);

View File

@ -2,6 +2,7 @@
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
using Bit.Core.Billing.Enums;
using Bit.Core.Entities;
using Bit.Core.Enums;
@ -28,6 +29,7 @@ namespace Bit.Core.Test.OrganizationFeatures.OrganizationUsers;
public class AcceptOrgUserCommandTests
{
private readonly IUserService _userService = Substitute.For<IUserService>();
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery = Substitute.For<ITwoFactorIsEnabledQuery>();
private readonly IOrgUserInviteTokenableFactory _orgUserInviteTokenableFactory = Substitute.For<IOrgUserInviteTokenableFactory>();
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory = new FakeDataProtectorTokenFactory<OrgUserInviteTokenable>();
@ -165,7 +167,7 @@ public class AcceptOrgUserCommandTests
SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails);
// User doesn't have 2FA enabled
_userService.TwoFactorIsEnabledAsync(user).Returns(false);
_twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user).Returns(false);
// Organization they are trying to join requires 2FA
var twoFactorPolicy = new OrganizationUserPolicyDetails { OrganizationId = orgUser.OrganizationId };
@ -646,7 +648,7 @@ public class AcceptOrgUserCommandTests
.Returns(false);
// User doesn't have 2FA enabled
_userService.TwoFactorIsEnabledAsync(user).Returns(false);
_twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user).Returns(false);
// Org does not require 2FA
sutProvider.GetDependency<IPolicyService>().GetPoliciesApplicableToUserAsync(user.Id,

View File

@ -2,7 +2,6 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Errors;
using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.AdminConsole.Models.Data.Provider;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
@ -11,12 +10,13 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.M
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Shared.Validation;
using Bit.Core.AdminConsole.Utilities.Commands;
using Bit.Core.AdminConsole.Utilities.Errors;
using Bit.Core.AdminConsole.Utilities.Validation;
using Bit.Core.Billing.Models.StaticStore.Plans;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Business;
using Bit.Core.Models.Commands;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Models.StaticStore;
@ -79,7 +79,7 @@ public class InviteOrganizationUserCommandTests
// Assert
Assert.IsType<Failure<ScimInviteOrganizationUsersResponse>>(result);
Assert.Equal(NoUsersToInviteError.Code, (result as Failure<ScimInviteOrganizationUsersResponse>).ErrorMessage);
Assert.Equal(NoUsersToInviteError.Code, (result as Failure<ScimInviteOrganizationUsersResponse>)!.Error.Message);
await sutProvider.GetDependency<IPaymentService>()
.DidNotReceiveWithAnyArgs()
@ -208,7 +208,7 @@ public class InviteOrganizationUserCommandTests
Assert.IsType<Failure<ScimInviteOrganizationUsersResponse>>(result);
var failure = result as Failure<ScimInviteOrganizationUsersResponse>;
Assert.Equal(errorMessage, failure!.ErrorMessage);
Assert.Equal(errorMessage, failure!.Error.Message);
await sutProvider.GetDependency<IOrganizationUserRepository>()
.DidNotReceive()
@ -570,7 +570,7 @@ public class InviteOrganizationUserCommandTests
// Assert
Assert.IsType<Failure<ScimInviteOrganizationUsersResponse>>(result);
Assert.Equal(FailedToInviteUsersError.Code, (result as Failure<ScimInviteOrganizationUsersResponse>)!.ErrorMessage);
Assert.Equal(FailedToInviteUsersError.Code, (result as Failure<ScimInviteOrganizationUsersResponse>)!.Error.Message);
// org user revert
await orgUserRepository.Received(1).DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(x => x.Count() == 1));

View File

@ -2,7 +2,7 @@
using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
using Bit.Core.AdminConsole.Shared.Validation;
using Bit.Core.AdminConsole.Utilities.Validation;
using Bit.Core.Billing.Models.StaticStore.Plans;
using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
@ -155,6 +155,6 @@ public class InviteOrganizationUsersValidatorTests
var result = await sutProvider.Sut.ValidateAsync(request);
Assert.IsType<Invalid<InviteOrganizationUsersValidationRequest>>(result);
Assert.Equal("Some Secrets Manager Failure", (result as Invalid<InviteOrganizationUsersValidationRequest>)!.ErrorMessageString);
Assert.Equal("Some Secrets Manager Failure", (result as Invalid<InviteOrganizationUsersValidationRequest>)!.Error.Message);
}
}

View File

@ -1,7 +1,7 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
using Bit.Core.AdminConsole.Shared.Validation;
using Bit.Core.AdminConsole.Utilities.Validation;
using Bit.Core.Billing.Models.StaticStore.Plans;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
@ -36,7 +36,7 @@ public class InviteUserOrganizationValidationTests
var result = await sutProvider.Sut.ValidateAsync(inviteOrganization);
Assert.IsType<Invalid<InviteOrganization>>(result);
Assert.Equal(OrganizationNoPaymentMethodFoundError.Code, (result as Invalid<InviteOrganization>)!.ErrorMessageString);
Assert.Equal(OrganizationNoPaymentMethodFoundError.Code, (result as Invalid<InviteOrganization>)!.Error.Message);
}
[Theory]
@ -53,6 +53,6 @@ public class InviteUserOrganizationValidationTests
var result = await sutProvider.Sut.ValidateAsync(inviteOrganization);
Assert.IsType<Invalid<InviteOrganization>>(result);
Assert.Equal(OrganizationNoSubscriptionFoundError.Code, (result as Invalid<InviteOrganization>)!.ErrorMessageString);
Assert.Equal(OrganizationNoSubscriptionFoundError.Code, (result as Invalid<InviteOrganization>)!.Error.Message);
}
}

View File

@ -3,7 +3,7 @@ using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Payments;
using Bit.Core.AdminConsole.Shared.Validation;
using Bit.Core.AdminConsole.Utilities.Validation;
using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models.StaticStore.Plans;
@ -39,7 +39,7 @@ public class InviteUserPaymentValidationTests
});
Assert.IsType<Invalid<PaymentsSubscription>>(result);
Assert.Equal(PaymentCancelledSubscriptionError.Code, (result as Invalid<PaymentsSubscription>)!.ErrorMessageString);
Assert.Equal(PaymentCancelledSubscriptionError.Code, (result as Invalid<PaymentsSubscription>)!.Error.Message);
}
[Fact]

View File

@ -1,7 +1,7 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
using Bit.Core.AdminConsole.Shared.Validation;
using Bit.Core.AdminConsole.Utilities.Validation;
using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models.StaticStore.Plans;
using Bit.Test.Common.AutoFixture;
@ -67,7 +67,7 @@ public class InviteUsersPasswordManagerValidatorTests
var result = await sutProvider.Sut.ValidateAsync(subscriptionUpdate);
Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result);
Assert.Equal(PasswordManagerSeatLimitHasBeenReachedError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>)!.ErrorMessageString);
Assert.Equal(PasswordManagerSeatLimitHasBeenReachedError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>)!.Error.Message);
}
[Theory]
@ -88,6 +88,6 @@ public class InviteUsersPasswordManagerValidatorTests
var result = await sutProvider.Sut.ValidateAsync(subscriptionUpdate);
Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result);
Assert.Equal(PasswordManagerPlanDoesNotAllowAdditionalSeatsError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>)!.ErrorMessageString);
Assert.Equal(PasswordManagerPlanDoesNotAllowAdditionalSeatsError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>)!.Error.Message);
}
}

View File

@ -4,6 +4,7 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
using Bit.Core.AdminConsole.Utilities.Commands;
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Data;
@ -11,7 +12,6 @@ using Bit.Core.Auth.Repositories;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Commands;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Repositories;
using Bit.Core.Services;

View File

@ -4,11 +4,11 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
using Bit.Core.AdminConsole.Utilities.Commands;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Commands;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Repositories;
using Bit.Core.Services;

View File

@ -26,8 +26,8 @@ public class EventRouteServiceTests
await Subject.CreateAsync(eventMessage);
_broadcastEventWriteService.DidNotReceiveWithAnyArgs().CreateAsync(Arg.Any<EventMessage>());
_storageEventWriteService.Received(1).CreateAsync(eventMessage);
await _broadcastEventWriteService.DidNotReceiveWithAnyArgs().CreateAsync(Arg.Any<EventMessage>());
await _storageEventWriteService.Received(1).CreateAsync(eventMessage);
}
[Theory, BitAutoData]
@ -37,8 +37,8 @@ public class EventRouteServiceTests
await Subject.CreateAsync(eventMessage);
_broadcastEventWriteService.Received(1).CreateAsync(eventMessage);
_storageEventWriteService.DidNotReceiveWithAnyArgs().CreateAsync(Arg.Any<EventMessage>());
await _broadcastEventWriteService.Received(1).CreateAsync(eventMessage);
await _storageEventWriteService.DidNotReceiveWithAnyArgs().CreateAsync(Arg.Any<EventMessage>());
}
[Theory, BitAutoData]
@ -48,8 +48,8 @@ public class EventRouteServiceTests
await Subject.CreateManyAsync(eventMessages);
_broadcastEventWriteService.DidNotReceiveWithAnyArgs().CreateManyAsync(Arg.Any<IEnumerable<EventMessage>>());
_storageEventWriteService.Received(1).CreateManyAsync(eventMessages);
await _broadcastEventWriteService.DidNotReceiveWithAnyArgs().CreateManyAsync(Arg.Any<IEnumerable<EventMessage>>());
await _storageEventWriteService.Received(1).CreateManyAsync(eventMessages);
}
[Theory, BitAutoData]
@ -59,7 +59,7 @@ public class EventRouteServiceTests
await Subject.CreateManyAsync(eventMessages);
_broadcastEventWriteService.Received(1).CreateManyAsync(eventMessages);
_storageEventWriteService.DidNotReceiveWithAnyArgs().CreateManyAsync(Arg.Any<IEnumerable<EventMessage>>());
await _broadcastEventWriteService.Received(1).CreateManyAsync(eventMessages);
await _storageEventWriteService.DidNotReceiveWithAnyArgs().CreateManyAsync(Arg.Any<IEnumerable<EventMessage>>());
}
}

View File

@ -89,7 +89,7 @@ public class SlackEventHandlerTests
var sutProvider = GetSutProvider(OneConfiguration());
await sutProvider.Sut.HandleEventAsync(eventMessage);
sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
await sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
Arg.Is(AssertHelper.AssertPropertyEqual(_token)),
Arg.Is(AssertHelper.AssertPropertyEqual(
$"Date: {eventMessage.Date}, Type: {eventMessage.Type}, UserId: {eventMessage.UserId}")),
@ -103,13 +103,13 @@ public class SlackEventHandlerTests
var sutProvider = GetSutProvider(TwoConfigurations());
await sutProvider.Sut.HandleEventAsync(eventMessage);
sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
await sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
Arg.Is(AssertHelper.AssertPropertyEqual(_token)),
Arg.Is(AssertHelper.AssertPropertyEqual(
$"Date: {eventMessage.Date}, Type: {eventMessage.Type}, UserId: {eventMessage.UserId}")),
Arg.Is(AssertHelper.AssertPropertyEqual(_channelId))
);
sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
await sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
Arg.Is(AssertHelper.AssertPropertyEqual(_token2)),
Arg.Is(AssertHelper.AssertPropertyEqual(
$"Date: {eventMessage.Date}, Type: {eventMessage.Type}, UserId: {eventMessage.UserId}")),

View File

@ -1,5 +1,5 @@
using Bit.Core.AdminConsole.Errors;
using Bit.Core.AdminConsole.Shared.Validation;
using Bit.Core.AdminConsole.Utilities.Errors;
using Bit.Core.AdminConsole.Utilities.Validation;
using Xunit;
namespace Bit.Core.Test.AdminConsole.Shared;
@ -22,13 +22,11 @@ public class IValidatorTests
{
if (string.IsNullOrWhiteSpace(value.Name))
{
return Task.FromResult<ValidationResult<TestClass>>(new Invalid<TestClass>
{
Errors = [new InvalidRequestError<TestClass>(value)]
});
return Task.FromResult<ValidationResult<TestClass>>(
new Invalid<TestClass>(new InvalidRequestError<TestClass>(value)));
}
return Task.FromResult<ValidationResult<TestClass>>(new Valid<TestClass> { Value = value });
return Task.FromResult<ValidationResult<TestClass>>(new Valid<TestClass>(value));
}
}
@ -41,7 +39,7 @@ public class IValidatorTests
Assert.IsType<Invalid<TestClass>>(result);
var invalidResult = result as Invalid<TestClass>;
Assert.Equal(InvalidRequestError<TestClass>.Code, invalidResult.Errors.First().Message);
Assert.Equal(InvalidRequestError<TestClass>.Code, invalidResult!.Error.Message);
}
[Fact]

View File

@ -1,9 +1,9 @@
using Bit.Core.AdminConsole.Errors;
using Bit.Core.Models.Commands;
using Bit.Core.AdminConsole.Utilities.Commands;
using Bit.Core.AdminConsole.Utilities.Errors;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.Models.Commands;
namespace Bit.Core.Test.AdminConsole.Utilities.Commands;
public class CommandResultTests
{

View File

@ -44,9 +44,6 @@ public abstract class BaseTokenProviderTests<T>
protected virtual void SetupUserService(IUserService userService, User user)
{
userService
.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType, user)
.Returns(true);
userService
.CanAccessPremium(user)
.Returns(true);
@ -85,8 +82,6 @@ public abstract class BaseTokenProviderTests<T>
var userManager = SubstituteUserManager();
MockDatabase(user, metaData);
AdditionalSetup(sutProvider, user);
var response = await sutProvider.Sut.CanGenerateTwoFactorTokenAsync(userManager, user);
Assert.Equal(expectedResponse, response);
}

View File

@ -83,6 +83,7 @@ public class DuoUniversalTwoFactorTokenProviderTests : BaseTokenProviderTests<Du
User user, SutProvider<DuoUniversalTokenProvider> sutProvider)
{
// Arrange
AdditionalSetup(sutProvider, user);
user.Premium = true;
user.PremiumExpirationDate = DateTime.UtcNow.AddDays(1);
@ -100,6 +101,8 @@ public class DuoUniversalTwoFactorTokenProviderTests : BaseTokenProviderTests<Du
User user, SutProvider<DuoUniversalTokenProvider> sutProvider)
{
// Arrange
AdditionalSetup(sutProvider, user);
user.Premium = false;
sutProvider.GetDependency<IDuoUniversalTokenService>()

View File

@ -1,87 +0,0 @@
using AutoFixture.Xunit2;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Entities;
using Bit.Core.Tokens;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.Auth.Models.Business.Tokenables;
public class HCaptchaTokenableTests
{
[Fact]
public void CanHandleNullUser()
{
var token = new HCaptchaTokenable(null);
Assert.Equal(default, token.Id);
Assert.Equal(default, token.Email);
}
[Fact]
public void TokenWithNullUserIsInvalid()
{
var token = new HCaptchaTokenable(null)
{
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
};
Assert.False(token.Valid);
}
[Theory, BitAutoData]
public void TokenValidityCheckNullUserIdIsInvalid(User user)
{
var token = new HCaptchaTokenable(user)
{
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
};
Assert.False(token.TokenIsValid(null));
}
[Theory, AutoData]
public void CanUpdateExpirationToNonStandard(User user)
{
var token = new HCaptchaTokenable(user)
{
ExpirationDate = DateTime.MinValue
};
Assert.Equal(DateTime.MinValue, token.ExpirationDate, TimeSpan.FromMilliseconds(10));
}
[Theory, AutoData]
public void SetsDataFromUser(User user)
{
var token = new HCaptchaTokenable(user);
Assert.Equal(user.Id, token.Id);
Assert.Equal(user.Email, token.Email);
}
[Theory, AutoData]
public void SerializationSetsCorrectDateTime(User user)
{
var expectedDateTime = DateTime.UtcNow.AddHours(-5);
var token = new HCaptchaTokenable(user)
{
ExpirationDate = expectedDateTime
};
var result = Tokenable.FromToken<HCaptchaTokenable>(token.ToToken());
Assert.Equal(expectedDateTime, result.ExpirationDate, TimeSpan.FromMilliseconds(10));
}
[Theory, AutoData]
public void IsInvalidIfIdentifierIsWrong(User user)
{
var token = new HCaptchaTokenable(user)
{
Identifier = "not correct"
};
Assert.False(token.Valid);
}
}

View File

@ -67,7 +67,7 @@ public class SsoTokenableTests
ExpirationDate = expectedDateTime
};
var result = Tokenable.FromToken<HCaptchaTokenable>(token.ToToken());
var result = Tokenable.FromToken<SsoTokenable>(token.ToToken());
Assert.Equal(expectedDateTime, result.ExpirationDate, TimeSpan.FromMilliseconds(10));
}

View File

@ -5,6 +5,7 @@ using Bit.Core.Entities;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Repositories;
using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
@ -53,6 +54,39 @@ public class TwoFactorIsEnabledQueryTests
}
}
[Theory, BitAutoData]
public async Task TwoFactorIsEnabledQuery_DatabaseReturnsEmpty_ResultEmpty(
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
List<UserWithCalculatedPremium> usersWithCalculatedPremium)
{
// Arrange
var userIds = usersWithCalculatedPremium.Select(u => u.Id).ToList();
sutProvider.GetDependency<IUserRepository>()
.GetManyWithCalculatedPremiumAsync(Arg.Any<IEnumerable<Guid>>())
.Returns([]);
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(userIds);
// Assert
Assert.Empty(result);
}
[Theory]
[BitAutoData((IEnumerable<Guid>)null)]
[BitAutoData([])]
public async Task TwoFactorIsEnabledQuery_UserIdsNullorEmpty_ResultEmpty(
IEnumerable<Guid> userIds,
SutProvider<TwoFactorIsEnabledQuery> sutProvider)
{
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(userIds);
// Assert
Assert.Empty(result);
}
[Theory]
[BitAutoData]
public async Task TwoFactorIsEnabledQuery_WithNoTwoFactorEnabled_ReturnsAllTwoFactorDisabled(
@ -122,8 +156,11 @@ public class TwoFactorIsEnabledQueryTests
}
[Theory]
[BitAutoData]
public async Task TwoFactorIsEnabledQuery_WithNullTwoFactorProviders_ReturnsAllTwoFactorDisabled(
[BitAutoData("")]
[BitAutoData("{}")]
[BitAutoData((string)null)]
public async Task TwoFactorIsEnabledQuery_WithNullOrEmptyTwoFactorProviders_ReturnsAllTwoFactorDisabled(
string twoFactorProviders,
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
List<UserWithCalculatedPremium> usersWithCalculatedPremium)
{
@ -132,7 +169,7 @@ public class TwoFactorIsEnabledQueryTests
foreach (var user in usersWithCalculatedPremium)
{
user.TwoFactorProviders = null; // No two-factor providers configured
user.TwoFactorProviders = twoFactorProviders; // No two-factor providers configured
}
sutProvider.GetDependency<IUserRepository>()
@ -176,6 +213,24 @@ public class TwoFactorIsEnabledQueryTests
.GetManyWithCalculatedPremiumAsync(default);
}
[Theory]
[BitAutoData]
public async Task TwoFactorIsEnabledQuery_UserIdNull_ReturnsFalse(
SutProvider<TwoFactorIsEnabledQuery> sutProvider)
{
// Arrange
var user = new TestTwoFactorProviderUser
{
Id = null
};
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(user);
// Assert
Assert.False(result);
}
[Theory]
[BitAutoData(TwoFactorProviderType.Authenticator)]
[BitAutoData(TwoFactorProviderType.Email)]
@ -193,10 +248,8 @@ public class TwoFactorIsEnabledQueryTests
{ freeProviderType, new TwoFactorProvider { Enabled = true } }
};
user.Premium = false;
user.SetTwoFactorProviders(twoFactorProviders);
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(user);
@ -205,7 +258,7 @@ public class TwoFactorIsEnabledQueryTests
await sutProvider.GetDependency<IUserRepository>()
.DidNotReceiveWithAnyArgs()
.GetManyWithCalculatedPremiumAsync(default);
.GetCalculatedPremiumAsync(default);
}
[Theory]
@ -230,7 +283,7 @@ public class TwoFactorIsEnabledQueryTests
await sutProvider.GetDependency<IUserRepository>()
.DidNotReceiveWithAnyArgs()
.GetManyWithCalculatedPremiumAsync(default);
.GetCalculatedPremiumAsync(default);
}
[Theory]
@ -252,14 +305,18 @@ public class TwoFactorIsEnabledQueryTests
user.SetTwoFactorProviders(twoFactorProviders);
sutProvider.GetDependency<IUserRepository>()
.GetManyWithCalculatedPremiumAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(user.Id)))
.Returns(new List<UserWithCalculatedPremium> { user });
.GetCalculatedPremiumAsync(user.Id)
.Returns(user);
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(user);
// Assert
Assert.False(result);
await sutProvider.GetDependency<IUserRepository>()
.ReceivedWithAnyArgs(1)
.GetCalculatedPremiumAsync(default);
}
[Theory]
@ -268,7 +325,7 @@ public class TwoFactorIsEnabledQueryTests
public async Task TwoFactorIsEnabledQuery_WithProviderTypeRequiringPremium_WithUserPremium_ReturnsTrue(
TwoFactorProviderType premiumProviderType,
SutProvider<TwoFactorIsEnabledQuery> sutProvider,
User user)
UserWithCalculatedPremium user)
{
// Arrange
var twoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
@ -276,9 +333,14 @@ public class TwoFactorIsEnabledQueryTests
{ premiumProviderType, new TwoFactorProvider { Enabled = true } }
};
user.Premium = true;
user.Premium = false;
user.HasPremiumAccess = true;
user.SetTwoFactorProviders(twoFactorProviders);
sutProvider.GetDependency<IUserRepository>()
.GetCalculatedPremiumAsync(user.Id)
.Returns(user);
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(user);
@ -286,8 +348,8 @@ public class TwoFactorIsEnabledQueryTests
Assert.True(result);
await sutProvider.GetDependency<IUserRepository>()
.DidNotReceiveWithAnyArgs()
.GetManyWithCalculatedPremiumAsync(default);
.ReceivedWithAnyArgs(1)
.GetCalculatedPremiumAsync(default);
}
[Theory]
@ -309,14 +371,18 @@ public class TwoFactorIsEnabledQueryTests
user.SetTwoFactorProviders(twoFactorProviders);
sutProvider.GetDependency<IUserRepository>()
.GetManyWithCalculatedPremiumAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(user.Id)))
.Returns(new List<UserWithCalculatedPremium> { user });
.GetCalculatedPremiumAsync(user.Id)
.Returns(user);
// Act
var result = await sutProvider.Sut.TwoFactorIsEnabledAsync(user);
// Assert
Assert.True(result);
await sutProvider.GetDependency<IUserRepository>()
.ReceivedWithAnyArgs(1)
.GetCalculatedPremiumAsync(default);
}
[Theory]
@ -333,5 +399,29 @@ public class TwoFactorIsEnabledQueryTests
// Assert
Assert.False(result);
await sutProvider.GetDependency<IUserRepository>()
.DidNotReceiveWithAnyArgs()
.GetCalculatedPremiumAsync(default);
}
private class TestTwoFactorProviderUser : ITwoFactorProvidersUser
{
public Guid? Id { get; set; }
public string TwoFactorProviders { get; set; }
public bool Premium { get; set; }
public Dictionary<TwoFactorProviderType, TwoFactorProvider> GetTwoFactorProviders()
{
return JsonHelpers.LegacyDeserialize<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(TwoFactorProviders);
}
public Guid? GetUserId()
{
return Id;
}
public bool GetPremium()
{
return Premium;
}
}
}

View File

@ -4,7 +4,6 @@ using Bit.Core.Entities;
using Bit.Core.Models.BitStripe;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Stripe;
using Xunit;
@ -22,8 +21,7 @@ public class PaymentHistoryServiceTests
var stripeAdapter = Substitute.For<IStripeAdapter>();
stripeAdapter.InvoiceListAsync(Arg.Any<StripeInvoiceListOptions>()).Returns(invoices);
var transactionRepository = Substitute.For<ITransactionRepository>();
var logger = Substitute.For<ILogger<PaymentHistoryService>>();
var paymentHistoryService = new PaymentHistoryService(stripeAdapter, transactionRepository, logger);
var paymentHistoryService = new PaymentHistoryService(stripeAdapter, transactionRepository);
// Act
var result = await paymentHistoryService.GetInvoiceHistoryAsync(subscriber);
@ -40,8 +38,7 @@ public class PaymentHistoryServiceTests
// Arrange
var paymentHistoryService = new PaymentHistoryService(
Substitute.For<IStripeAdapter>(),
Substitute.For<ITransactionRepository>(),
Substitute.For<ILogger<PaymentHistoryService>>());
Substitute.For<ITransactionRepository>());
// Act
var result = await paymentHistoryService.GetInvoiceHistoryAsync(null);
@ -59,8 +56,7 @@ public class PaymentHistoryServiceTests
var transactionRepository = Substitute.For<ITransactionRepository>();
transactionRepository.GetManyByOrganizationIdAsync(subscriber.Id, Arg.Any<int>(), Arg.Any<DateTime?>()).Returns(transactions);
var stripeAdapter = Substitute.For<IStripeAdapter>();
var logger = Substitute.For<ILogger<PaymentHistoryService>>();
var paymentHistoryService = new PaymentHistoryService(stripeAdapter, transactionRepository, logger);
var paymentHistoryService = new PaymentHistoryService(stripeAdapter, transactionRepository);
// Act
var result = await paymentHistoryService.GetTransactionHistoryAsync(subscriber);
@ -77,8 +73,7 @@ public class PaymentHistoryServiceTests
// Arrange
var paymentHistoryService = new PaymentHistoryService(
Substitute.For<IStripeAdapter>(),
Substitute.For<ITransactionRepository>(),
Substitute.For<ILogger<PaymentHistoryService>>());
Substitute.For<ITransactionRepository>());
// Act
var result = await paymentHistoryService.GetTransactionHistoryAsync(null);

View File

@ -2,8 +2,6 @@
<PropertyGroup>
<IsPackable>false</IsPackable>
<RootNamespace>Bit.Core.Test</RootNamespace>
<!-- Temp exclusions until warnings are fixed -->
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS4014</WarningsNotAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="$(CoverletCollectorVersion)">

View File

@ -9,6 +9,7 @@ using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
using Bit.Core.Billing.Services;
using Bit.Core.Context;
using Bit.Core.Entities;
@ -324,6 +325,7 @@ public class UserServiceTests
sutProvider.GetDependency<IPremiumUserBillingService>(),
sutProvider.GetDependency<IRemoveOrganizationUserCommand>(),
sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>(),
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>(),
sutProvider.GetDependency<IDistributedCache>()
);
@ -476,6 +478,9 @@ public class UserServiceTests
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization.Id)
.Returns(organization);
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
.TwoFactorIsEnabledAsync(user)
.Returns(true);
var expectedSavedProviders = JsonHelpers.LegacySerialize(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
[TwoFactorProviderType.Remember] = new() { Enabled = true }
@ -911,6 +916,7 @@ public class UserServiceTests
sutProvider.GetDependency<IPremiumUserBillingService>(),
sutProvider.GetDependency<IRemoveOrganizationUserCommand>(),
sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>(),
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>(),
sutProvider.GetDependency<IDistributedCache>()
);
}

View File

@ -40,12 +40,12 @@ public class GetTasksForOrganizationQueryTests
var result = await sutProvider.Sut.GetTasksAsync(org.Id, status);
Assert.Equal(2, result.Count);
sutProvider.GetDependency<IAuthorizationService>().Received(1).AuthorizeAsync(
await sutProvider.GetDependency<IAuthorizationService>().Received(1).AuthorizeAsync(
Arg.Any<ClaimsPrincipal>(), org, Arg.Is<IEnumerable<IAuthorizationRequirement>>(
e => e.Contains(SecurityTaskOperations.ListAllForOrganization)
)
);
sutProvider.GetDependency<ISecurityTaskRepository>().Received(1).GetManyByOrganizationIdStatusAsync(org.Id, SecurityTaskStatus.Pending);
await sutProvider.GetDependency<ISecurityTaskRepository>().Received(1).GetManyByOrganizationIdStatusAsync(org.Id, SecurityTaskStatus.Pending);
}
[Theory, BitAutoData]
@ -82,11 +82,11 @@ public class GetTasksForOrganizationQueryTests
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetTasksAsync(org.Id));
sutProvider.GetDependency<IAuthorizationService>().Received(1).AuthorizeAsync(
await sutProvider.GetDependency<IAuthorizationService>().Received(1).AuthorizeAsync(
Arg.Any<ClaimsPrincipal>(), org, Arg.Is<IEnumerable<IAuthorizationRequirement>>(
e => e.Contains(SecurityTaskOperations.ListAllForOrganization)
)
);
sutProvider.GetDependency<ISecurityTaskRepository>().Received(0).GetManyByOrganizationIdStatusAsync(org.Id, SecurityTaskStatus.Pending);
await sutProvider.GetDependency<ISecurityTaskRepository>().Received(0).GetManyByOrganizationIdStatusAsync(org.Id, SecurityTaskStatus.Pending);
}
}

View File

@ -3,7 +3,6 @@ using System.Text;
using Bit.Core;
using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.Services;
using Bit.Core.Auth.UserFeatures.Registration;
using Bit.Core.Auth.UserFeatures.WebAuthnLogin;
using Bit.Core.Context;
@ -38,7 +37,6 @@ public class AccountsControllerTests : IDisposable
private readonly ILogger<AccountsController> _logger;
private readonly IUserRepository _userRepository;
private readonly IRegisterUserCommand _registerUserCommand;
private readonly ICaptchaValidationService _captchaValidationService;
private readonly IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> _assertionOptionsDataProtector;
private readonly IGetWebAuthnLoginCredentialAssertionOptionsCommand _getWebAuthnLoginCredentialAssertionOptionsCommand;
private readonly ISendVerificationEmailForRegistrationCommand _sendVerificationEmailForRegistrationCommand;
@ -54,7 +52,6 @@ public class AccountsControllerTests : IDisposable
_logger = Substitute.For<ILogger<AccountsController>>();
_userRepository = Substitute.For<IUserRepository>();
_registerUserCommand = Substitute.For<IRegisterUserCommand>();
_captchaValidationService = Substitute.For<ICaptchaValidationService>();
_assertionOptionsDataProtector = Substitute.For<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>();
_getWebAuthnLoginCredentialAssertionOptionsCommand = Substitute.For<IGetWebAuthnLoginCredentialAssertionOptionsCommand>();
_sendVerificationEmailForRegistrationCommand = Substitute.For<ISendVerificationEmailForRegistrationCommand>();
@ -68,7 +65,6 @@ public class AccountsControllerTests : IDisposable
_logger,
_userRepository,
_registerUserCommand,
_captchaValidationService,
_assertionOptionsDataProtector,
_getWebAuthnLoginCredentialAssertionOptionsCommand,
_sendVerificationEmailForRegistrationCommand,

View File

@ -2,8 +2,6 @@
<PropertyGroup>
<IsPackable>false</IsPackable>
<!-- Temp exclusions until warnings are fixed -->
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS0672;CS1998</WarningsNotAsErrors>
</PropertyGroup>
<ItemGroup>

View File

@ -33,7 +33,6 @@ public class BaseRequestValidatorTests
private readonly IDeviceValidator _deviceValidator;
private readonly ITwoFactorAuthenticationValidator _twoFactorAuthenticationValidator;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IMailService _mailService;
private readonly ILogger<BaseRequestValidatorTests> _logger;
private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
@ -54,7 +53,6 @@ public class BaseRequestValidatorTests
_deviceValidator = Substitute.For<IDeviceValidator>();
_twoFactorAuthenticationValidator = Substitute.For<ITwoFactorAuthenticationValidator>();
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
_mailService = Substitute.For<IMailService>();
_logger = Substitute.For<ILogger<BaseRequestValidatorTests>>();
_currentContext = Substitute.For<ICurrentContext>();
_globalSettings = Substitute.For<GlobalSettings>();
@ -72,7 +70,6 @@ public class BaseRequestValidatorTests
_deviceValidator,
_twoFactorAuthenticationValidator,
_organizationUserRepository,
_mailService,
_logger,
_currentContext,
_globalSettings,
@ -84,36 +81,6 @@ public class BaseRequestValidatorTests
_policyRequirementQuery);
}
/* Logic path
* ValidateAsync -> _Logger.LogInformation
* |-> BuildErrorResultAsync -> _eventService.LogUserEventAsync
* |-> SetErrorResult
*/
[Theory, BitAutoData]
public async Task ValidateAsync_IsBot_UserNotNull_ShouldBuildErrorResult_ShouldLogFailedLoginEvent(
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
CustomValidatorRequestContext requestContext,
GrantValidationResult grantResult)
{
// Arrange
var context = CreateContext(tokenRequest, requestContext, grantResult);
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = true;
_sut.isValid = true;
// Act
await _sut.ValidateAsync(context);
var errorResponse = (ErrorResponseModel)context.GrantResult.CustomResponse["ErrorModel"];
// Assert
await _eventService.Received(1)
.LogUserEventAsync(context.CustomValidatorRequestContext.User.Id,
EventType.User_FailedLogIn);
Assert.True(context.GrantResult.IsError);
Assert.Equal("Username or password is incorrect. Try again.", errorResponse.Message);
}
/* Logic path
* ValidateAsync -> UpdateFailedAuthDetailsAsync -> _mailService.SendFailedLoginAttemptsEmailAsync
* |-> BuildErrorResultAsync -> _eventService.LogUserEventAsync
@ -128,8 +95,6 @@ public class BaseRequestValidatorTests
{
// Arrange
var context = CreateContext(tokenRequest, requestContext, grantResult);
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
_globalSettings.Captcha.Returns(new GlobalSettings.CaptchaSettings());
_globalSettings.SelfHosted = true;
_sut.isValid = false;
@ -142,44 +107,6 @@ public class BaseRequestValidatorTests
Assert.Equal("Username or password is incorrect. Try again.", errorResponse.Message);
}
/* Logic path
* ValidateAsync -> UpdateFailedAuthDetailsAsync -> _mailService.SendFailedLoginAttemptsEmailAsync
* |-> BuildErrorResultAsync -> _eventService.LogUserEventAsync
* |-> SetErrorResult
*/
[Theory, BitAutoData]
public async Task ValidateAsync_ContextNotValid_MaxAttemptLogin_ShouldSendEmail(
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
CustomValidatorRequestContext requestContext,
GrantValidationResult grantResult)
{
// Arrange
var context = CreateContext(tokenRequest, requestContext, grantResult);
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
// This needs to be n-1 of the max failed login attempts
context.CustomValidatorRequestContext.User.FailedLoginCount = 2;
context.CustomValidatorRequestContext.KnownDevice = false;
_globalSettings.Captcha.Returns(
new GlobalSettings.CaptchaSettings
{
MaximumFailedLoginAttempts = 3
});
_sut.isValid = false;
// Act
await _sut.ValidateAsync(context);
// Assert
await _mailService.Received(1)
.SendFailedLoginAttemptsEmailAsync(
Arg.Any<string>(), Arg.Any<DateTime>(), Arg.Any<string>());
Assert.True(context.GrantResult.IsError);
var errorResponse = (ErrorResponseModel)context.GrantResult.CustomResponse["ErrorModel"];
Assert.Equal("Username or password is incorrect. Try again.", errorResponse.Message);
}
[Theory, BitAutoData]
public async Task ValidateAsync_DeviceNotValidated_ShouldLogError(
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
@ -189,7 +116,6 @@ public class BaseRequestValidatorTests
// Arrange
var context = CreateContext(tokenRequest, requestContext, grantResult);
// 1 -> to pass
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
_sut.isValid = true;
// 2 -> will result to false with no extra configuration
@ -226,7 +152,6 @@ public class BaseRequestValidatorTests
// Arrange
var context = CreateContext(tokenRequest, requestContext, grantResult);
// 1 -> to pass
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
_sut.isValid = true;
// 2 -> will result to false with no extra configuration
@ -263,7 +188,6 @@ public class BaseRequestValidatorTests
{
// Arrange
var context = CreateContext(tokenRequest, requestContext, grantResult);
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
_sut.isValid = true;
context.ValidatedTokenRequest.GrantType = grantType;
@ -294,7 +218,6 @@ public class BaseRequestValidatorTests
// Arrange
_featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements).Returns(true);
var context = CreateContext(tokenRequest, requestContext, grantResult);
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
_sut.isValid = true;
context.ValidatedTokenRequest.GrantType = grantType;
@ -326,7 +249,6 @@ public class BaseRequestValidatorTests
// Arrange
_featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements).Returns(true);
var context = CreateContext(tokenRequest, requestContext, grantResult);
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
_sut.isValid = true;
context.ValidatedTokenRequest.GrantType = grantType;
@ -363,7 +285,6 @@ public class BaseRequestValidatorTests
{
// Arrange
var context = CreateContext(tokenRequest, requestContext, grantResult);
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
_sut.isValid = true;
context.ValidatedTokenRequest.GrantType = grantType;
@ -401,7 +322,6 @@ public class BaseRequestValidatorTests
{
// Arrange
var context = CreateContext(tokenRequest, requestContext, grantResult);
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
_sut.isValid = true;
context.ValidatedTokenRequest.GrantType = grantType;
@ -439,7 +359,6 @@ public class BaseRequestValidatorTests
var user = context.CustomValidatorRequestContext.User;
user.Key = null;
context.CustomValidatorRequestContext.CaptchaResponse.IsBot = false;
context.ValidatedTokenRequest.ClientId = "Not Web";
_sut.isValid = true;
_twoFactorAuthenticationValidator

View File

@ -1,5 +1,4 @@
using Bit.Core;
using Bit.Core.Context;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Api;
@ -28,7 +27,7 @@ public class DeviceValidatorTests
private readonly IUserService _userService;
private readonly IDistributedCache _distributedCache;
private readonly Logger<DeviceValidator> _logger;
private readonly IFeatureService _featureService;
private readonly DeviceValidator _sut;
public DeviceValidatorTests()
@ -41,7 +40,6 @@ public class DeviceValidatorTests
_userService = Substitute.For<IUserService>();
_distributedCache = Substitute.For<IDistributedCache>();
_logger = new Logger<DeviceValidator>(Substitute.For<ILoggerFactory>());
_featureService = Substitute.For<IFeatureService>();
_sut = new DeviceValidator(
_deviceService,
_deviceRepository,
@ -50,8 +48,7 @@ public class DeviceValidatorTests
_currentContext,
_userService,
_distributedCache,
_logger,
_featureService);
_logger);
}
[Theory, BitAutoData]
@ -312,8 +309,6 @@ public class DeviceValidatorTests
AddValidDeviceToRequest(request);
_deviceRepository.GetByIdentifierAsync(context.Device.Identifier, context.User.Id)
.Returns(null as Device);
_featureService.IsEnabled(FeatureFlagKeys.NewDeviceVerification)
.Returns(true);
request.GrantType = grantType;
@ -336,8 +331,6 @@ public class DeviceValidatorTests
AddValidDeviceToRequest(request);
_deviceRepository.GetByIdentifierAsync(context.Device.Identifier, context.User.Id)
.Returns(null as Device);
_featureService.IsEnabled(FeatureFlagKeys.NewDeviceVerification)
.Returns(true);
request.Raw.Add("AuthRequest", "authRequest");
@ -360,8 +353,6 @@ public class DeviceValidatorTests
AddValidDeviceToRequest(request);
_deviceRepository.GetByIdentifierAsync(context.Device.Identifier, context.User.Id)
.Returns(null as Device);
_featureService.IsEnabled(FeatureFlagKeys.NewDeviceVerification)
.Returns(true);
context.TwoFactorRequired = true;
@ -384,8 +375,6 @@ public class DeviceValidatorTests
AddValidDeviceToRequest(request);
_deviceRepository.GetByIdentifierAsync(context.Device.Identifier, context.User.Id)
.Returns(null as Device);
_featureService.IsEnabled(FeatureFlagKeys.NewDeviceVerification)
.Returns(true);
context.SsoRequired = true;
@ -404,7 +393,6 @@ public class DeviceValidatorTests
{
// Arrange
ArrangeForHandleNewDeviceVerificationTest(context, request);
_featureService.IsEnabled(FeatureFlagKeys.NewDeviceVerification).Returns(true);
_globalSettings.EnableNewDeviceVerification = true;
context.User = null;
@ -430,7 +418,6 @@ public class DeviceValidatorTests
{
// Arrange
ArrangeForHandleNewDeviceVerificationTest(context, request);
_featureService.IsEnabled(FeatureFlagKeys.NewDeviceVerification).Returns(true);
_globalSettings.EnableNewDeviceVerification = true;
context.User.VerifyDevices = false;
@ -454,7 +441,6 @@ public class DeviceValidatorTests
{
// Arrange
ArrangeForHandleNewDeviceVerificationTest(context, request);
_featureService.IsEnabled(FeatureFlagKeys.NewDeviceVerification).Returns(true);
_globalSettings.EnableNewDeviceVerification = true;
_distributedCache.GetAsync(Arg.Any<string>()).Returns(null as byte[]);
context.User.CreationDate = DateTime.UtcNow - TimeSpan.FromHours(23);
@ -479,7 +465,6 @@ public class DeviceValidatorTests
{
// Arrange
ArrangeForHandleNewDeviceVerificationTest(context, request);
_featureService.IsEnabled(FeatureFlagKeys.NewDeviceVerification).Returns(true);
_globalSettings.EnableNewDeviceVerification = true;
_distributedCache.GetAsync(Arg.Any<string>()).Returns([1]);
@ -503,7 +488,6 @@ public class DeviceValidatorTests
{
// Arrange
ArrangeForHandleNewDeviceVerificationTest(context, request);
_featureService.IsEnabled(FeatureFlagKeys.NewDeviceVerification).Returns(true);
_globalSettings.EnableNewDeviceVerification = true;
_distributedCache.GetAsync(Arg.Any<string>()).Returns(null as byte[]);
@ -535,7 +519,6 @@ public class DeviceValidatorTests
{
// Arrange
ArrangeForHandleNewDeviceVerificationTest(context, request);
_featureService.IsEnabled(FeatureFlagKeys.NewDeviceVerification).Returns(true);
_globalSettings.EnableNewDeviceVerification = true;
_distributedCache.GetAsync(Arg.Any<string>()).Returns(null as byte[]);
@ -564,7 +547,6 @@ public class DeviceValidatorTests
{
// Arrange
ArrangeForHandleNewDeviceVerificationTest(context, request);
_featureService.IsEnabled(FeatureFlagKeys.NewDeviceVerification).Returns(true);
_globalSettings.EnableNewDeviceVerification = true;
_distributedCache.GetAsync(Arg.Any<string>()).Returns([1]);
_deviceRepository.GetManyByUserIdAsync(context.User.Id).Returns([]);
@ -590,7 +572,6 @@ public class DeviceValidatorTests
{
// Arrange
ArrangeForHandleNewDeviceVerificationTest(context, request);
_featureService.IsEnabled(FeatureFlagKeys.NewDeviceVerification).Returns(true);
_globalSettings.EnableNewDeviceVerification = true;
_deviceRepository.GetManyByUserIdAsync(context.User.Id).Returns([new Device()]);
_distributedCache.GetAsync(Arg.Any<string>()).Returns(null as byte[]);

View File

@ -2,6 +2,7 @@
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Identity.TokenProviders;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Models.Data.Organizations;
@ -27,11 +28,11 @@ public class TwoFactorAuthenticationValidatorTests
private readonly IUserService _userService;
private readonly UserManagerTestWrapper<User> _userManager;
private readonly IOrganizationDuoUniversalTokenProvider _organizationDuoUniversalTokenProvider;
private readonly IFeatureService _featureService;
private readonly IApplicationCacheService _applicationCacheService;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IOrganizationRepository _organizationRepository;
private readonly IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> _ssoEmail2faSessionTokenable;
private readonly ITwoFactorIsEnabledQuery _twoFactorenabledQuery;
private readonly ICurrentContext _currentContext;
private readonly TwoFactorAuthenticationValidator _sut;
@ -40,22 +41,22 @@ public class TwoFactorAuthenticationValidatorTests
_userService = Substitute.For<IUserService>();
_userManager = SubstituteUserManager();
_organizationDuoUniversalTokenProvider = Substitute.For<IOrganizationDuoUniversalTokenProvider>();
_featureService = Substitute.For<IFeatureService>();
_applicationCacheService = Substitute.For<IApplicationCacheService>();
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
_organizationRepository = Substitute.For<IOrganizationRepository>();
_ssoEmail2faSessionTokenable = Substitute.For<IDataProtectorTokenFactory<SsoEmail2faSessionTokenable>>();
_twoFactorenabledQuery = Substitute.For<ITwoFactorIsEnabledQuery>();
_currentContext = Substitute.For<ICurrentContext>();
_sut = new TwoFactorAuthenticationValidator(
_userService,
_userManager,
_organizationDuoUniversalTokenProvider,
_featureService,
_applicationCacheService,
_organizationUserRepository,
_organizationRepository,
_ssoEmail2faSessionTokenable,
_twoFactorenabledQuery,
_currentContext);
}
@ -251,9 +252,9 @@ public class TwoFactorAuthenticationValidatorTests
[Theory]
[BitAutoData(TwoFactorProviderType.Email)]
public async void BuildTwoFactorResultAsync_IndividualEmailProvider_SendsEmail_SetsSsoToken_ReturnsNotNull(
TwoFactorProviderType providerType,
User user)
public async void BuildTwoFactorResultAsync_SetsSsoToken_ReturnsNotNull(
TwoFactorProviderType providerType,
User user)
{
// Arrange
var providerTypeInt = (int)providerType;
@ -263,9 +264,6 @@ public class TwoFactorAuthenticationValidatorTests
_userManager.SUPPORTS_TWO_FACTOR = true;
_userManager.TWO_FACTOR_PROVIDERS = [providerType.ToString()];
_userService.TwoFactorProviderIsEnabledAsync(Arg.Any<TwoFactorProviderType>(), user)
.Returns(true);
// Act
var result = await _sut.BuildTwoFactorResultAsync(user, null);
@ -278,8 +276,6 @@ public class TwoFactorAuthenticationValidatorTests
Assert.True(providers.ContainsKey(providerTypeInt.ToString()));
Assert.True(result.ContainsKey("SsoEmail2faSessionToken"));
Assert.True(result.ContainsKey("Email"));
await _userService.Received(1).SendTwoFactorEmailAsync(Arg.Any<User>());
}
[Theory]
@ -322,9 +318,6 @@ public class TwoFactorAuthenticationValidatorTests
string token)
{
// Arrange
_userService.TwoFactorProviderIsEnabledAsync(
TwoFactorProviderType.Email, user).Returns(true);
_userManager.TWO_FACTOR_PROVIDERS = ["email"];
// Act
@ -342,10 +335,8 @@ public class TwoFactorAuthenticationValidatorTests
string token)
{
// Arrange
_userService.TwoFactorProviderIsEnabledAsync(
TwoFactorProviderType.Email, user).Returns(false);
_userManager.TWO_FACTOR_PROVIDERS = ["email"];
user.TwoFactorProviders = "";
// Act
var result = await _sut.VerifyTwoFactorAsync(
@ -362,9 +353,6 @@ public class TwoFactorAuthenticationValidatorTests
string token)
{
// Arrange
_userService.TwoFactorProviderIsEnabledAsync(
TwoFactorProviderType.OrganizationDuo, user).Returns(false);
_userManager.TWO_FACTOR_PROVIDERS = ["OrganizationDuo"];
// Act
@ -387,11 +375,9 @@ public class TwoFactorAuthenticationValidatorTests
string token)
{
// Arrange
_userService.TwoFactorProviderIsEnabledAsync(
providerType, user).Returns(true);
_userManager.TWO_FACTOR_ENABLED = true;
_userManager.TWO_FACTOR_TOKEN_VERIFIED = true;
user.TwoFactorProviders = GetTwoFactorIndividualProviderJson(providerType);
// Act
var result = await _sut.VerifyTwoFactorAsync(user, null, providerType, token);
@ -412,11 +398,9 @@ public class TwoFactorAuthenticationValidatorTests
string token)
{
// Arrange
_userService.TwoFactorProviderIsEnabledAsync(
providerType, user).Returns(true);
_userManager.TWO_FACTOR_ENABLED = true;
_userManager.TWO_FACTOR_TOKEN_VERIFIED = false;
user.TwoFactorProviders = GetTwoFactorIndividualProviderJson(providerType);
// Act
var result = await _sut.VerifyTwoFactorAsync(user, null, providerType, token);

View File

@ -54,7 +54,6 @@ IBaseRequestValidatorTestWrapper
IDeviceValidator deviceValidator,
ITwoFactorAuthenticationValidator twoFactorAuthenticationValidator,
IOrganizationUserRepository organizationUserRepository,
IMailService mailService,
ILogger logger,
ICurrentContext currentContext,
GlobalSettings globalSettings,
@ -71,7 +70,6 @@ IBaseRequestValidatorTestWrapper
deviceValidator,
twoFactorAuthenticationValidator,
organizationUserRepository,
mailService,
logger,
currentContext,
globalSettings,
@ -96,6 +94,7 @@ IBaseRequestValidatorTestWrapper
return context.ValidatedTokenRequest.Subject ?? new ClaimsPrincipal();
}
[Obsolete]
protected override void SetErrorResult(
BaseRequestValidationContextFake context,
Dictionary<string, object> customResponse)
@ -103,6 +102,7 @@ IBaseRequestValidatorTestWrapper
context.GrantResult = new GrantValidationResult(TokenRequestErrors.InvalidGrant, customResponse: customResponse);
}
[Obsolete]
protected override void SetSsoResult(
BaseRequestValidationContextFake context,
Dictionary<string, object> customResponse)
@ -121,6 +121,7 @@ IBaseRequestValidatorTestWrapper
return Task.CompletedTask;
}
[Obsolete]
protected override void SetTwoFactorResult(
BaseRequestValidationContextFake context,
Dictionary<string, object> customResponse)

View File

@ -56,9 +56,9 @@ public class UserManagerTestWrapper<TUser> : UserManager<TUser> where TUser : cl
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public override async Task<bool> GetTwoFactorEnabledAsync(TUser user)
public override Task<bool> GetTwoFactorEnabledAsync(TUser user)
{
return TWO_FACTOR_ENABLED;
return Task.FromResult(TWO_FACTOR_ENABLED);
}
/// <summary>
@ -66,9 +66,9 @@ public class UserManagerTestWrapper<TUser> : UserManager<TUser> where TUser : cl
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public override async Task<IList<string>> GetValidTwoFactorProvidersAsync(TUser user)
public override Task<IList<string>> GetValidTwoFactorProvidersAsync(TUser user)
{
return TWO_FACTOR_PROVIDERS;
return Task.FromResult(TWO_FACTOR_PROVIDERS);
}
/// <summary>
@ -77,9 +77,9 @@ public class UserManagerTestWrapper<TUser> : UserManager<TUser> where TUser : cl
/// <param name="user"></param>
/// <param name="tokenProvider"></param>
/// <returns></returns>
public override async Task<string> GenerateTwoFactorTokenAsync(TUser user, string tokenProvider)
public override Task<string> GenerateTwoFactorTokenAsync(TUser user, string tokenProvider)
{
return TWO_FACTOR_TOKEN;
return Task.FromResult(TWO_FACTOR_TOKEN);
}
/// <summary>
@ -89,8 +89,8 @@ public class UserManagerTestWrapper<TUser> : UserManager<TUser> where TUser : cl
/// <param name="tokenProvider"></param>
/// <param name="token"></param>
/// <returns></returns>
public override async Task<bool> VerifyTwoFactorTokenAsync(TUser user, string tokenProvider, string token)
public override Task<bool> VerifyTwoFactorTokenAsync(TUser user, string tokenProvider, string token)
{
return TWO_FACTOR_TOKEN_VERIFIED;
return Task.FromResult(TWO_FACTOR_TOKEN_VERIFIED);
}
}

View File

@ -316,6 +316,29 @@ public class OrganizationUserRepositoryTests
BillingEmail = user1.Email, // TODO: EF does not enforce this being NOT NULl
Plan = "Test", // TODO: EF does not enforce this being NOT NULl
PrivateKey = "privatekey",
UsePolicies = false,
UseSso = false,
UseKeyConnector = false,
UseScim = false,
UseGroups = false,
UseDirectory = false,
UseEvents = false,
UseTotp = false,
Use2fa = false,
UseApi = false,
UseResetPassword = false,
UseSecretsManager = false,
SelfHost = false,
UsersGetPremium = false,
UseCustomPermissions = false,
Enabled = true,
UsePasswordManager = false,
LimitCollectionCreation = false,
LimitCollectionDeletion = false,
LimitItemDeletion = false,
AllowAdminAccessToAllCollectionItems = false,
UseRiskInsights = false,
UseAdminSponsoredFamilies = false
});
var organizationDomain = new OrganizationDomain
@ -335,6 +358,7 @@ public class OrganizationUserRepositoryTests
UserId = user1.Id,
Status = OrganizationUserStatusType.Confirmed,
ResetPasswordKey = "resetpasswordkey1",
AccessSecretsManager = false
});
await organizationUserRepository.CreateAsync(new OrganizationUser

View File

@ -0,0 +1,46 @@
using Bit.Core.Platform.Installations;
using Bit.Infrastructure.IntegrationTest.Comparers;
using Xunit;
namespace Bit.Infrastructure.IntegrationTest.Platform.Installations;
public class InstallationRepositoryTests
{
[DatabaseTheory, DatabaseData]
public async Task GetByIdAsync_Works(IInstallationRepository installationRepository)
{
var installation = await installationRepository.CreateAsync(new Installation
{
Email = "test@email.com",
Key = "installation_key",
Enabled = true,
});
var retrievedInstallation = await installationRepository.GetByIdAsync(installation.Id);
Assert.NotNull(retrievedInstallation);
Assert.Equal("installation_key", retrievedInstallation.Key);
}
[DatabaseTheory, DatabaseData]
public async Task UpdateAsync_Works(IInstallationRepository installationRepository)
{
var installation = await installationRepository.CreateAsync(new Installation
{
Email = "test@email.com",
Key = "installation_key",
Enabled = true,
});
var now = DateTime.UtcNow;
installation.LastActivityDate = now;
await installationRepository.ReplaceAsync(installation);
var retrievedInstallation = await installationRepository.GetByIdAsync(installation.Id);
Assert.NotNull(retrievedInstallation.LastActivityDate);
Assert.Equal(now, retrievedInstallation.LastActivityDate.Value, LaxDateTimeComparer.Default);
}
}

View File

@ -1,5 +1,4 @@
using AspNetCoreRateLimit;
using Bit.Core.Auth.Services;
using Bit.Core.Billing.Services;
using Bit.Core.Platform.Push;
using Bit.Core.Platform.Push.Internal;
@ -207,8 +206,6 @@ public abstract class WebApplicationFactoryBase<T> : WebApplicationFactory<T>
Replace<IMailDeliveryService, NoopMailDeliveryService>(services);
Replace<ICaptchaValidationService, NoopCaptchaValidationService>(services);
// TODO: Install and use azurite in CI pipeline
Replace<IInstallationDeviceRepository, NoopRepos.InstallationDeviceRepository>(services);