From ef403b436290e6c496e5b42c5c943a75402f73ec Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Wed, 8 Jun 2022 08:44:28 -0500 Subject: [PATCH] [PS-616] [PS-795] Fix/auto enroll master password reset without user verification (#2038) * Fix parameter name to match entity * Deserialize policy data in object * Add policy with config type to fixtures * Return policy with deserialized config * Use CoreHelper serializers * Add master password reset on accept request * Simplify policy data parsing * Linter --- .../OrganizationUsersController.cs | 28 ++++++-- .../Controllers/OrganizationsController.cs | 2 +- .../OrganizationUserRequestModels.cs | 2 + src/Core/Entities/Policy.cs | 20 ++++++ .../Policies/IPolicyDataModel.cs | 6 ++ .../Policies}/ResetPasswordDataModel.cs | 4 +- .../Policies}/SendOptionsPolicyData.cs | 4 +- src/Core/Repositories/IPolicyRepository.cs | 2 + src/Core/Services/IOrganizationService.cs | 2 +- .../Implementations/OrganizationService.cs | 5 +- .../Services/Implementations/PolicyService.cs | 1 - .../Services/Implementations/SendService.cs | 10 +-- .../Repositories/PolicyRepository.cs | 4 ++ .../Repositories/PolicyRepository.cs | 3 + .../OrganizationUsersControllerTests.cs | 68 +++++++++++++++++++ test/Core.Test/AutoFixture/PolicyFixtures.cs | 12 ++++ test/Core.Test/Services/PolicyServiceTests.cs | 47 +++++++------ test/Core.Test/Services/SendServiceTests.cs | 1 + 18 files changed, 182 insertions(+), 39 deletions(-) create mode 100644 src/Core/Models/Data/Organizations/Policies/IPolicyDataModel.cs rename src/Core/Models/Data/{ => Organizations/Policies}/ResetPasswordDataModel.cs (61%) rename src/Core/Models/Data/{ => Organizations/Policies}/SendOptionsPolicyData.cs (58%) create mode 100644 test/Api.Test/Controllers/OrganizationUsersControllerTests.cs diff --git a/src/Api/Controllers/OrganizationUsersController.cs b/src/Api/Controllers/OrganizationUsersController.cs index 82f910957..08cce27a4 100644 --- a/src/Api/Controllers/OrganizationUsersController.cs +++ b/src/Api/Controllers/OrganizationUsersController.cs @@ -10,6 +10,7 @@ using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Business; using Bit.Core.Models.Data.Organizations.OrganizationUsers; +using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.Repositories; using Bit.Core.Services; using Microsoft.AspNetCore.Authorization; @@ -27,6 +28,7 @@ namespace Bit.Api.Controllers private readonly ICollectionRepository _collectionRepository; private readonly IGroupRepository _groupRepository; private readonly IUserService _userService; + private readonly IPolicyRepository _policyRepository; private readonly ICurrentContext _currentContext; public OrganizationUsersController( @@ -36,6 +38,7 @@ namespace Bit.Api.Controllers ICollectionRepository collectionRepository, IGroupRepository groupRepository, IUserService userService, + IPolicyRepository policyRepository, ICurrentContext currentContext) { _organizationRepository = organizationRepository; @@ -44,6 +47,7 @@ namespace Bit.Api.Controllers _collectionRepository = collectionRepository; _groupRepository = groupRepository; _userService = userService; + _policyRepository = policyRepository; _currentContext = currentContext; } @@ -169,8 +173,8 @@ namespace Bit.Api.Controllers await _organizationService.ResendInviteAsync(orgGuidId, userId.Value, new Guid(id)); } - [HttpPost("{id}/accept")] - public async Task Accept(string orgId, string id, [FromBody] OrganizationUserAcceptRequestModel model) + [HttpPost("{organizationUserId}/accept")] + public async Task Accept(Guid orgId, Guid organizationUserId, [FromBody] OrganizationUserAcceptRequestModel model) { var user = await _userService.GetUserByPrincipalAsync(User); if (user == null) @@ -178,7 +182,23 @@ namespace Bit.Api.Controllers throw new UnauthorizedAccessException(); } - var result = await _organizationService.AcceptUserAsync(new Guid(id), user, model.Token, _userService); + var masterPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.MasterPassword); + var useMasterPasswordPolicy = masterPasswordPolicy != null && + masterPasswordPolicy.Enabled && + masterPasswordPolicy.DataModel.AutoEnrollEnabled; + + if (useMasterPasswordPolicy && + string.IsNullOrWhiteSpace(model.ResetPasswordKey)) + { + throw new BadRequestException(string.Empty, "Master Password reset is required, but not provided."); + } + + await _organizationService.AcceptUserAsync(organizationUserId, user, model.Token, _userService); + + if (useMasterPasswordPolicy) + { + await _organizationService.UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id); + } } [HttpPost("{id}/confirm")] @@ -277,7 +297,7 @@ namespace Bit.Api.Controllers throw new UnauthorizedAccessException(); } - if (!await _userService.VerifySecretAsync(user, model.Secret)) + if (model.ResetPasswordKey != null && !await _userService.VerifySecretAsync(user, model.Secret)) { await Task.Delay(2000); throw new BadRequestException("MasterPasswordHash", "Invalid password."); diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index 5b00261ae..18d341db3 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -12,7 +12,7 @@ using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Business; -using Bit.Core.Models.Data; +using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; diff --git a/src/Api/Models/Request/Organizations/OrganizationUserRequestModels.cs b/src/Api/Models/Request/Organizations/OrganizationUserRequestModels.cs index e2e5adf8d..ef0b87a68 100644 --- a/src/Api/Models/Request/Organizations/OrganizationUserRequestModels.cs +++ b/src/Api/Models/Request/Organizations/OrganizationUserRequestModels.cs @@ -40,6 +40,8 @@ namespace Bit.Api.Models.Request.Organizations { [Required] public string Token { get; set; } + // Used to auto-enroll in master password reset + public string ResetPasswordKey { get; set; } } public class OrganizationUserConfirmRequestModel diff --git a/src/Core/Entities/Policy.cs b/src/Core/Entities/Policy.cs index fbd65c634..12731a6df 100644 --- a/src/Core/Entities/Policy.cs +++ b/src/Core/Entities/Policy.cs @@ -1,5 +1,6 @@ using System; using Bit.Core.Enums; +using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.Utilities; namespace Bit.Core.Entities @@ -18,5 +19,24 @@ namespace Bit.Core.Entities { Id = CoreHelpers.GenerateComb(); } + + public T GetDataModel() where T : IPolicyDataModel, new() + { + return CoreHelpers.LoadClassFromJsonData(Data); + } + + public void SetDataModel(T dataModel) where T : IPolicyDataModel, new() + { + Data = CoreHelpers.ClassToJsonData(dataModel); + } + } + + public class Policy : Policy where T : IPolicyDataModel, new() + { + public T DataModel + { + get => GetDataModel(); + set => SetDataModel(value); + } } } diff --git a/src/Core/Models/Data/Organizations/Policies/IPolicyDataModel.cs b/src/Core/Models/Data/Organizations/Policies/IPolicyDataModel.cs new file mode 100644 index 000000000..1d263cedb --- /dev/null +++ b/src/Core/Models/Data/Organizations/Policies/IPolicyDataModel.cs @@ -0,0 +1,6 @@ +namespace Bit.Core.Models.Data.Organizations.Policies +{ + public interface IPolicyDataModel + { + } +} diff --git a/src/Core/Models/Data/ResetPasswordDataModel.cs b/src/Core/Models/Data/Organizations/Policies/ResetPasswordDataModel.cs similarity index 61% rename from src/Core/Models/Data/ResetPasswordDataModel.cs rename to src/Core/Models/Data/Organizations/Policies/ResetPasswordDataModel.cs index 359b74d64..c77d8ef01 100644 --- a/src/Core/Models/Data/ResetPasswordDataModel.cs +++ b/src/Core/Models/Data/Organizations/Policies/ResetPasswordDataModel.cs @@ -1,8 +1,8 @@ using System.ComponentModel.DataAnnotations; -namespace Bit.Core.Models.Data +namespace Bit.Core.Models.Data.Organizations.Policies { - public class ResetPasswordDataModel + public class ResetPasswordDataModel : IPolicyDataModel { [Display(Name = "ResetPasswordAutoEnrollCheckbox")] public bool AutoEnrollEnabled { get; set; } diff --git a/src/Core/Models/Data/SendOptionsPolicyData.cs b/src/Core/Models/Data/Organizations/Policies/SendOptionsPolicyData.cs similarity index 58% rename from src/Core/Models/Data/SendOptionsPolicyData.cs rename to src/Core/Models/Data/Organizations/Policies/SendOptionsPolicyData.cs index d3898fd85..d9bb5ef9d 100644 --- a/src/Core/Models/Data/SendOptionsPolicyData.cs +++ b/src/Core/Models/Data/Organizations/Policies/SendOptionsPolicyData.cs @@ -1,8 +1,8 @@ using System.ComponentModel.DataAnnotations; -namespace Bit.Core.Models.Data +namespace Bit.Core.Models.Data.Organizations.Policies { - public class SendOptionsPolicyData + public class SendOptionsPolicyData : IPolicyDataModel { [Display(Name = "DisableHideEmail")] public bool DisableHideEmail { get; set; } diff --git a/src/Core/Repositories/IPolicyRepository.cs b/src/Core/Repositories/IPolicyRepository.cs index 9466bc3db..6aa430070 100644 --- a/src/Core/Repositories/IPolicyRepository.cs +++ b/src/Core/Repositories/IPolicyRepository.cs @@ -3,12 +3,14 @@ using System.Collections.Generic; using System.Threading.Tasks; using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.Models.Data.Organizations.Policies; namespace Bit.Core.Repositories { public interface IPolicyRepository : IRepository { Task GetByOrganizationIdTypeAsync(Guid organizationId, PolicyType type); + Task> GetByOrganizationIdTypeAsync(Guid organizationId, PolicyType type) where T : IPolicyDataModel, new(); Task> GetManyByOrganizationIdAsync(Guid organizationId); Task> GetManyByUserIdAsync(Guid userId); Task> GetManyByTypeApplicableToUserIdAsync(Guid userId, PolicyType policyType, diff --git a/src/Core/Services/IOrganizationService.cs b/src/Core/Services/IOrganizationService.cs index 7d7704924..d27391a71 100644 --- a/src/Core/Services/IOrganizationService.cs +++ b/src/Core/Services/IOrganizationService.cs @@ -51,7 +51,7 @@ namespace Bit.Core.Services Task>> DeleteUsersAsync(Guid organizationId, IEnumerable organizationUserIds, Guid? deletingUserId); Task UpdateUserGroupsAsync(OrganizationUser organizationUser, IEnumerable groupIds, Guid? loggedInUserId); - Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid organizationUserId, string resetPasswordKey, Guid? callingUserId); + Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId); Task GenerateLicenseAsync(Guid organizationId, Guid installationId); Task GenerateLicenseAsync(Organization organization, Guid installationId, int? version = null); diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 0d5b8aab2..0ce9ca066 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -10,6 +10,7 @@ using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Business; using Bit.Core.Models.Data; +using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.Repositories; using Bit.Core.Settings; using Bit.Core.Utilities; @@ -1751,10 +1752,10 @@ namespace Bit.Core.Services EventType.OrganizationUser_UpdatedGroups); } - public async Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid organizationUserId, string resetPasswordKey, Guid? callingUserId) + public async Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId) { // Org User must be the same as the calling user and the organization ID associated with the user must match passed org ID - var orgUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId, organizationUserId); + var orgUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId, userId); if (!callingUserId.HasValue || orgUser == null || orgUser.UserId != callingUserId.Value || orgUser.OrganizationId != organizationId) { diff --git a/src/Core/Services/Implementations/PolicyService.cs b/src/Core/Services/Implementations/PolicyService.cs index 497a4b478..2ca38300f 100644 --- a/src/Core/Services/Implementations/PolicyService.cs +++ b/src/Core/Services/Implementations/PolicyService.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Bit.Core.Entities; diff --git a/src/Core/Services/Implementations/SendService.cs b/src/Core/Services/Implementations/SendService.cs index ac7a60363..61722f307 100644 --- a/src/Core/Services/Implementations/SendService.cs +++ b/src/Core/Services/Implementations/SendService.cs @@ -9,6 +9,7 @@ using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Business; using Bit.Core.Models.Data; +using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.Repositories; using Bit.Core.Settings; using Bit.Core.Utilities; @@ -291,14 +292,9 @@ namespace Bit.Core.Services if (send.HideEmail.GetValueOrDefault()) { var sendOptionsPolicies = await _policyRepository.GetManyByTypeApplicableToUserIdAsync(userId.Value, PolicyType.SendOptions); - foreach (var policy in sendOptionsPolicies) + if (sendOptionsPolicies.Any(p => p.GetDataModel()?.DisableHideEmail ?? false)) { - var data = CoreHelpers.LoadClassFromJsonData(policy.Data); - if (data?.DisableHideEmail ?? false) - { - throw new BadRequestException("Due to an Enterprise Policy, you are not allowed to hide your email address from recipients when creating or editing a Send."); - } - + throw new BadRequestException("Due to an Enterprise Policy, you are not allowed to hide your email address from recipients when creating or editing a Send."); } } } diff --git a/src/Infrastructure.Dapper/Repositories/PolicyRepository.cs b/src/Infrastructure.Dapper/Repositories/PolicyRepository.cs index 9989cf200..913eb6923 100644 --- a/src/Infrastructure.Dapper/Repositories/PolicyRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/PolicyRepository.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading.Tasks; using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.Repositories; using Bit.Core.Settings; using Dapper; @@ -21,6 +22,9 @@ namespace Bit.Infrastructure.Dapper.Repositories public PolicyRepository(string connectionString, string readOnlyConnectionString) : base(connectionString, readOnlyConnectionString) { } + + public async Task> GetByOrganizationIdTypeAsync(Guid organizationId, PolicyType type) where T : IPolicyDataModel, new() => + (Policy)await GetByOrganizationIdTypeAsync(organizationId, type); public async Task GetByOrganizationIdTypeAsync(Guid organizationId, PolicyType type) { using (var connection = new SqlConnection(ConnectionString)) diff --git a/src/Infrastructure.EntityFramework/Repositories/PolicyRepository.cs b/src/Infrastructure.EntityFramework/Repositories/PolicyRepository.cs index c0f1527de..f6884123e 100644 --- a/src/Infrastructure.EntityFramework/Repositories/PolicyRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/PolicyRepository.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading.Tasks; using AutoMapper; using Bit.Core.Enums; +using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.Repositories; using Bit.Infrastructure.EntityFramework.Models; using Bit.Infrastructure.EntityFramework.Repositories.Queries; @@ -18,6 +19,8 @@ namespace Bit.Infrastructure.EntityFramework.Repositories : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Policies) { } + public async Task> GetByOrganizationIdTypeAsync(Guid organizationId, PolicyType type) where T : IPolicyDataModel, new() => + (Core.Entities.Policy)await GetByOrganizationIdTypeAsync(organizationId, type); public async Task GetByOrganizationIdTypeAsync(Guid organizationId, PolicyType type) { using (var scope = ServiceScopeFactory.CreateScope()) diff --git a/test/Api.Test/Controllers/OrganizationUsersControllerTests.cs b/test/Api.Test/Controllers/OrganizationUsersControllerTests.cs new file mode 100644 index 000000000..a900bfdc2 --- /dev/null +++ b/test/Api.Test/Controllers/OrganizationUsersControllerTests.cs @@ -0,0 +1,68 @@ +using System; +using System.Threading.Tasks; +using Bit.Api.Controllers; +using Bit.Api.Models.Request.Organizations; +using Bit.Api.Test.AutoFixture.Attributes; +using Bit.Core.Entities; +using Bit.Core.Models.Data.Organizations.Policies; +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.Controllers +{ + [ControllerCustomize(typeof(OrganizationUsersController))] + [SutProviderCustomize] + public class OrganizationUsersControllerTests + { + [Theory] + [BitAutoData] + public async Task Accept_RequiresKnownUser(Guid orgId, Guid orgUserId, OrganizationUserAcceptRequestModel model, + SutProvider sutProvider) + { + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs((User)null); + + await Assert.ThrowsAsync(() => sutProvider.Sut.Accept(orgId, orgUserId, model)); + } + + [Theory] + [BitAutoData] + public async Task Accept_NoMasterPasswordReset(Guid orgId, Guid orgUserId, + OrganizationUserAcceptRequestModel model, User user, SutProvider sutProvider) + { + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); + + await sutProvider.Sut.Accept(orgId, orgUserId, model); + + await sutProvider.GetDependency().Received(1) + .AcceptUserAsync(orgUserId, user, model.Token, sutProvider.GetDependency()); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .UpdateUserResetPasswordEnrollmentAsync(default, default, default, default); + } + + [Theory] + [BitAutoData] + public async Task Accept_RequireMasterPasswordReset(Guid orgId, Guid orgUserId, + OrganizationUserAcceptRequestModel model, User user, SutProvider sutProvider) + { + var policy = new Policy + { + Enabled = true, + DataModel = new ResetPasswordDataModel { AutoEnrollEnabled = true, }, + }; + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); + sutProvider.GetDependency().GetByOrganizationIdTypeAsync(orgId, + Core.Enums.PolicyType.MasterPassword).Returns(policy); + + await sutProvider.Sut.Accept(orgId, orgUserId, model); + + await sutProvider.GetDependency().Received(1) + .AcceptUserAsync(orgUserId, user, model.Token, sutProvider.GetDependency()); + await sutProvider.GetDependency().Received(1) + .UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id); + } + } +} diff --git a/test/Core.Test/AutoFixture/PolicyFixtures.cs b/test/Core.Test/AutoFixture/PolicyFixtures.cs index 4d5a969ae..1ea6f7456 100644 --- a/test/Core.Test/AutoFixture/PolicyFixtures.cs +++ b/test/Core.Test/AutoFixture/PolicyFixtures.cs @@ -1,10 +1,12 @@ using System; using System.Reflection; +using System.Text.Json; using AutoFixture; using AutoFixture.Kernel; using AutoFixture.Xunit2; using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.Test.AutoFixture.EntityFrameworkRepositoryFixtures; using Bit.Core.Test.AutoFixture.OrganizationFixtures; using Bit.Infrastructure.EntityFramework.Repositories; @@ -28,6 +30,16 @@ namespace Bit.Core.Test.AutoFixture.PolicyFixtures .With(o => o.OrganizationId, Guid.NewGuid()) .With(o => o.Type, Type) .With(o => o.Enabled, true)); + fixture.Customize>(composer => composer + .With(o => o.Data, JsonSerializer.Serialize(fixture.Create())) + .With(o => o.OrganizationId, Guid.NewGuid()) + .With(o => o.Type, Type) + .With(o => o.Enabled, true)); + fixture.Customize>(composer => composer + .With(o => o.Data, JsonSerializer.Serialize(fixture.Create())) + .With(o => o.OrganizationId, Guid.NewGuid()) + .With(o => o.Type, Type) + .With(o => o.Enabled, true)); } } diff --git a/test/Core.Test/Services/PolicyServiceTests.cs b/test/Core.Test/Services/PolicyServiceTests.cs index f86b81ab6..4451eaf81 100644 --- a/test/Core.Test/Services/PolicyServiceTests.cs +++ b/test/Core.Test/Services/PolicyServiceTests.cs @@ -15,10 +15,12 @@ using PolicyFixtures = Bit.Core.Test.AutoFixture.PolicyFixtures; namespace Bit.Core.Test.Services { + [SutProviderCustomize] public class PolicyServiceTests { - [Theory, CustomAutoData(typeof(SutProviderCustomization))] - public async Task SaveAsync_OrganizationDoesNotExist_ThrowsBadRequest([PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider sutProvider) + [Theory, BitAutoData] + public async Task SaveAsync_OrganizationDoesNotExist_ThrowsBadRequest( + [PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider sutProvider) { SetupOrg(sutProvider, policy.OrganizationId, null); @@ -39,8 +41,9 @@ namespace Bit.Core.Test.Services .LogPolicyEventAsync(default, default, default); } - [Theory, CustomAutoData(typeof(SutProviderCustomization))] - public async Task SaveAsync_OrganizationCannotUsePolicies_ThrowsBadRequest([PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider sutProvider) + [Theory, BitAutoData] + public async Task SaveAsync_OrganizationCannotUsePolicies_ThrowsBadRequest( + [PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider sutProvider) { var orgId = Guid.NewGuid(); @@ -66,8 +69,9 @@ namespace Bit.Core.Test.Services .LogPolicyEventAsync(default, default, default); } - [Theory, CustomAutoData(typeof(SutProviderCustomization))] - public async Task SaveAsync_SingleOrg_RequireSsoEnabled_ThrowsBadRequest([PolicyFixtures.Policy(PolicyType.SingleOrg)] Policy policy, SutProvider sutProvider) + [Theory, BitAutoData] + public async Task SaveAsync_SingleOrg_RequireSsoEnabled_ThrowsBadRequest( + [PolicyFixtures.Policy(PolicyType.SingleOrg)] Policy policy, SutProvider sutProvider) { policy.Enabled = false; @@ -98,7 +102,7 @@ namespace Bit.Core.Test.Services .LogPolicyEventAsync(default, default, default); } - [Theory, CustomAutoData(typeof(SutProviderCustomization))] + [Theory, BitAutoData] public async Task SaveAsync_SingleOrg_VaultTimeoutEnabled_ThrowsBadRequest([PolicyFixtures.Policy(Enums.PolicyType.SingleOrg)] Policy policy, SutProvider sutProvider) { policy.Enabled = false; @@ -127,8 +131,8 @@ namespace Bit.Core.Test.Services } [Theory] - [InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, Enums.PolicyType.SingleOrg)] - [InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, Enums.PolicyType.RequireSso)] + [BitAutoData(PolicyType.SingleOrg)] + [BitAutoData(PolicyType.RequireSso)] public async Task SaveAsync_PolicyRequiredByKeyConnector_DisablePolicy_ThrowsBadRequest( Enums.PolicyType policyType, Policy policy, @@ -164,8 +168,9 @@ namespace Bit.Core.Test.Services .UpsertAsync(default); } - [Theory, CustomAutoData(typeof(SutProviderCustomization))] - public async Task SaveAsync_RequireSsoPolicy_NotEnabled_ThrowsBadRequestAsync([PolicyFixtures.Policy(Enums.PolicyType.RequireSso)] Policy policy, SutProvider sutProvider) + [Theory, BitAutoData] + public async Task SaveAsync_RequireSsoPolicy_NotEnabled_ThrowsBadRequestAsync( + [PolicyFixtures.Policy(Enums.PolicyType.RequireSso)] Policy policy, SutProvider sutProvider) { policy.Enabled = true; @@ -196,8 +201,9 @@ namespace Bit.Core.Test.Services .LogPolicyEventAsync(default, default, default); } - [Theory, CustomAutoData(typeof(SutProviderCustomization))] - public async Task SaveAsync_NewPolicy_Created([PolicyFixtures.Policy(PolicyType.MasterPassword)] Policy policy, SutProvider sutProvider) + [Theory, BitAutoData] + public async Task SaveAsync_NewPolicy_Created( + [PolicyFixtures.Policy(PolicyType.MasterPassword)] Policy policy, SutProvider sutProvider) { policy.Id = default; @@ -221,8 +227,9 @@ namespace Bit.Core.Test.Services Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1)); } - [Theory, CustomAutoData(typeof(SutProviderCustomization))] - public async Task SaveAsync_VaultTimeoutPolicy_NotEnabled_ThrowsBadRequestAsync([PolicyFixtures.Policy(Enums.PolicyType.MaximumVaultTimeout)] Policy policy, SutProvider sutProvider) + [Theory, BitAutoData] + public async Task SaveAsync_VaultTimeoutPolicy_NotEnabled_ThrowsBadRequestAsync( + [PolicyFixtures.Policy(PolicyType.MaximumVaultTimeout)] Policy policy, SutProvider sutProvider) { policy.Enabled = true; @@ -253,8 +260,9 @@ namespace Bit.Core.Test.Services .LogPolicyEventAsync(default, default, default); } - [Theory, CustomAutoData(typeof(SutProviderCustomization))] - public async Task SaveAsync_ExistingPolicy_UpdateTwoFactor([PolicyFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider sutProvider) + [Theory, BitAutoData] + public async Task SaveAsync_ExistingPolicy_UpdateTwoFactor( + [PolicyFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider sutProvider) { // If the policy that this is updating isn't enabled then do some work now that the current one is enabled @@ -322,8 +330,9 @@ namespace Bit.Core.Test.Services Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1)); } - [Theory, CustomAutoData(typeof(SutProviderCustomization))] - public async Task SaveAsync_ExistingPolicy_UpdateSingleOrg([PolicyFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider sutProvider) + [Theory, BitAutoData] + public async Task SaveAsync_ExistingPolicy_UpdateSingleOrg( + [PolicyFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider sutProvider) { // If the policy that this is updating isn't enabled then do some work now that the current one is enabled diff --git a/test/Core.Test/Services/SendServiceTests.cs b/test/Core.Test/Services/SendServiceTests.cs index 8137c6252..9471f53e1 100644 --- a/test/Core.Test/Services/SendServiceTests.cs +++ b/test/Core.Test/Services/SendServiceTests.cs @@ -7,6 +7,7 @@ using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Data; +using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Test.AutoFixture.SendFixtures;