From edd4bc262395eb8a022a3b607c249e940ef6675b Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Thu, 4 Feb 2021 12:54:21 -0600 Subject: [PATCH] Add disable send policy (#1130) * Add Disable Send policy * Test DisableSend policy * PR Review * Update tests for using CurrentContext This required making an interface for CurrentContext and mocking out the members used. The interface can be expanded as needed for tests. I moved CurrentContext to a folder, which changes the namespace and causes a lot of file touches, but most are just adding a reference * Fix failing test * Update exemption to include all exempt users * Move all CurrentContext usages to ICurrentContext * PR review. Match messaging with Web --- .../Portal/Controllers/PoliciesController.cs | 1 + .../Portal/EnterprisePortalCurrentContext.cs | 2 +- .../src/Portal/Models/PolicyEditModel.cs | 1 + .../src/Portal/Models/PolicyModel.cs | 5 ++ bitwarden_license/src/Portal/Startup.cs | 3 +- .../src/Portal/Views/Policies/Edit.cshtml | 11 +++ bitwarden_license/src/Sso/Startup.cs | 3 +- src/Admin/Jobs/DeleteSendsJob.cs | 1 + src/Admin/Startup.cs | 3 +- src/Api/Controllers/CiphersController.cs | 5 +- src/Api/Controllers/CollectionsController.cs | 6 +- src/Api/Controllers/EventsController.cs | 6 +- src/Api/Controllers/GroupsController.cs | 6 +- src/Api/Controllers/HibpController.cs | 5 +- src/Api/Controllers/LicensesController.cs | 6 +- .../OrganizationUsersController.cs | 6 +- .../Controllers/OrganizationsController.cs | 5 +- src/Api/Controllers/PoliciesController.cs | 5 +- src/Api/Controllers/PushController.cs | 5 +- src/Api/Controllers/TwoFactorController.cs | 5 +- .../Controllers/CollectionsController.cs | 6 +- .../Public/Controllers/EventsController.cs | 6 +- .../Public/Controllers/GroupsController.cs | 6 +- .../Public/Controllers/MembersController.cs | 6 +- .../Controllers/OrganizationController.cs | 5 +- .../Public/Controllers/PoliciesController.cs | 6 +- src/Api/Startup.cs | 3 +- src/Billing/Startup.cs | 3 +- .../Context/CurrentContentOrganization.cs | 24 ++++++ src/Core/{ => Context}/CurrentContext.cs | 20 +---- src/Core/Context/ICurrentContext.cs | 49 ++++++++++++ src/Core/Enums/PolicyType.cs | 1 + src/Core/Identity/UserStore.cs | 5 +- .../IdentityServer/BaseRequestValidator.cs | 5 +- src/Core/IdentityServer/ClientStore.cs | 5 +- .../CustomTokenRequestValidator.cs | 3 +- src/Core/IdentityServer/ProfileService.cs | 5 +- .../ResourceOwnerPasswordValidator.cs | 3 +- src/Core/Models/Data/EventMessage.cs | 3 +- src/Core/Resources/SharedResources.en.resx | 11 +++ .../AzureQueuePushNotificationService.cs | 3 +- .../Services/Implementations/EventService.cs | 5 +- .../NotificationHubPushNotificationService.cs | 3 +- ...NotificationsApiPushNotificationService.cs | 3 +- .../RelayPushNotificationService.cs | 3 +- .../Services/Implementations/SendService.cs | 39 +++++++++- .../Services/Implementations/UserService.cs | 5 +- src/Core/Utilities/CoreHelpers.cs | 3 +- .../Utilities/CurrentContextMiddleware.cs | 3 +- src/Events/Controllers/CollectController.cs | 6 +- src/Events/Startup.cs | 3 +- src/Identity/Startup.cs | 3 +- src/Notifications/NotificationsHub.cs | 1 + .../AutoFixture/CurrentContextFixtures.cs | 39 ++++++++++ test/Core.Test/AutoFixture/SendFixtures.cs | 65 ++++++++++++++++ test/Core.Test/AutoFixture/SutProvider.cs | 6 +- .../AutoFixture/SutProviderCustomization.cs | 5 +- test/Core.Test/Services/EventServiceTests.cs | 1 + test/Core.Test/Services/SendServiceTests.cs | 74 +++++++++++++++++++ test/Core.Test/Services/UserServiceTests.cs | 1 + 60 files changed, 437 insertions(+), 99 deletions(-) create mode 100644 src/Core/Context/CurrentContentOrganization.cs rename src/Core/{ => Context}/CurrentContext.cs (94%) create mode 100644 src/Core/Context/ICurrentContext.cs create mode 100644 test/Core.Test/AutoFixture/CurrentContextFixtures.cs create mode 100644 test/Core.Test/AutoFixture/SendFixtures.cs create mode 100644 test/Core.Test/Services/SendServiceTests.cs diff --git a/bitwarden_license/src/Portal/Controllers/PoliciesController.cs b/bitwarden_license/src/Portal/Controllers/PoliciesController.cs index ab3e5efd32..3e7fedc5bc 100644 --- a/bitwarden_license/src/Portal/Controllers/PoliciesController.cs +++ b/bitwarden_license/src/Portal/Controllers/PoliciesController.cs @@ -136,6 +136,7 @@ namespace Bit.Portal.Controllers case PolicyType.PasswordGenerator: case PolicyType.TwoFactorAuthentication: case PolicyType.PersonalOwnership: + case PolicyType.DisableSend: break; case PolicyType.SingleOrg: diff --git a/bitwarden_license/src/Portal/EnterprisePortalCurrentContext.cs b/bitwarden_license/src/Portal/EnterprisePortalCurrentContext.cs index 8d43e5aebd..cecae76ee9 100644 --- a/bitwarden_license/src/Portal/EnterprisePortalCurrentContext.cs +++ b/bitwarden_license/src/Portal/EnterprisePortalCurrentContext.cs @@ -1,5 +1,5 @@ using System; -using Bit.Core; +using Bit.Core.Context; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; diff --git a/bitwarden_license/src/Portal/Models/PolicyEditModel.cs b/bitwarden_license/src/Portal/Models/PolicyEditModel.cs index 7ff89c8d22..fa6e870376 100644 --- a/bitwarden_license/src/Portal/Models/PolicyEditModel.cs +++ b/bitwarden_license/src/Portal/Models/PolicyEditModel.cs @@ -86,6 +86,7 @@ namespace Bit.Portal.Models case PolicyType.TwoFactorAuthentication: case PolicyType.RequireSso: case PolicyType.PersonalOwnership: + case PolicyType.DisableSend: break; default: throw new ArgumentOutOfRangeException(); diff --git a/bitwarden_license/src/Portal/Models/PolicyModel.cs b/bitwarden_license/src/Portal/Models/PolicyModel.cs index 9ab6d11b93..fb3038ff94 100644 --- a/bitwarden_license/src/Portal/Models/PolicyModel.cs +++ b/bitwarden_license/src/Portal/Models/PolicyModel.cs @@ -47,6 +47,11 @@ namespace Bit.Portal.Models DescriptionKey = "PersonalOwnershipDescription"; break; + case PolicyType.DisableSend: + NameKey = "DisableSend"; + DescriptionKey = "DisableSendDescription"; + break; + default: throw new ArgumentOutOfRangeException(); } diff --git a/bitwarden_license/src/Portal/Startup.cs b/bitwarden_license/src/Portal/Startup.cs index ccea8e8c28..5aa693a231 100644 --- a/bitwarden_license/src/Portal/Startup.cs +++ b/bitwarden_license/src/Portal/Startup.cs @@ -1,4 +1,5 @@ using Bit.Core; +using Bit.Core.Context; using Bit.Core.Utilities; using Bit.Portal.Utilities; using Microsoft.AspNetCore.Builder; @@ -40,7 +41,7 @@ namespace Bit.Portal // Context services.AddScoped(); - services.AddScoped((serviceProvider) => + services.AddScoped((serviceProvider) => serviceProvider.GetService()); // Identity diff --git a/bitwarden_license/src/Portal/Views/Policies/Edit.cshtml b/bitwarden_license/src/Portal/Views/Policies/Edit.cshtml index 70c4971cf6..07dfeb8d3f 100644 --- a/bitwarden_license/src/Portal/Views/Policies/Edit.cshtml +++ b/bitwarden_license/src/Portal/Views/Policies/Edit.cshtml @@ -61,6 +61,17 @@ @i18nService.T("PersonalOwnershipExemption") } + + @if (Model.PolicyType == PolicyType.DisableSend) + { + + }
diff --git a/bitwarden_license/src/Sso/Startup.cs b/bitwarden_license/src/Sso/Startup.cs index ef130e0784..06ebae65da 100644 --- a/bitwarden_license/src/Sso/Startup.cs +++ b/bitwarden_license/src/Sso/Startup.cs @@ -1,5 +1,6 @@ using System; using Bit.Core; +using Bit.Core.Context; using Bit.Core.Utilities; using Bit.Sso.Utilities; using IdentityServer4.Extensions; @@ -39,7 +40,7 @@ namespace Bit.Sso services.AddSqlServerRepositories(globalSettings); // Context - services.AddScoped(); + services.AddScoped(); // Mvc services.AddControllersWithViews(); diff --git a/src/Admin/Jobs/DeleteSendsJob.cs b/src/Admin/Jobs/DeleteSendsJob.cs index d84eafa188..d8a325aa3e 100644 --- a/src/Admin/Jobs/DeleteSendsJob.cs +++ b/src/Admin/Jobs/DeleteSendsJob.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Bit.Core; +using Bit.Core.Context; using Bit.Core.Jobs; using Bit.Core.Repositories; using Bit.Core.Services; diff --git a/src/Admin/Startup.cs b/src/Admin/Startup.cs index 11c79be641..6d141c8b17 100644 --- a/src/Admin/Startup.cs +++ b/src/Admin/Startup.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using Bit.Core; +using Bit.Core.Context; using Bit.Core.Identity; using Bit.Core.Utilities; using Microsoft.AspNetCore.Builder; @@ -45,7 +46,7 @@ namespace Bit.Admin services.AddSqlServerRepositories(globalSettings); // Context - services.AddScoped(); + services.AddScoped(); // Identity services.AddPasswordlessIdentityServices(globalSettings); diff --git a/src/Api/Controllers/CiphersController.cs b/src/Api/Controllers/CiphersController.cs index 54959db47c..713ef65956 100644 --- a/src/Api/Controllers/CiphersController.cs +++ b/src/Api/Controllers/CiphersController.cs @@ -8,6 +8,7 @@ using Bit.Core.Models.Api; using Bit.Core.Exceptions; using Bit.Core.Services; using Bit.Core; +using Bit.Core.Context; using Bit.Api.Utilities; using Bit.Core.Utilities; using System.Collections.Generic; @@ -23,7 +24,7 @@ namespace Bit.Api.Controllers private readonly ICollectionCipherRepository _collectionCipherRepository; private readonly ICipherService _cipherService; private readonly IUserService _userService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; private readonly GlobalSettings _globalSettings; public CiphersController( @@ -31,7 +32,7 @@ namespace Bit.Api.Controllers ICollectionCipherRepository collectionCipherRepository, ICipherService cipherService, IUserService userService, - CurrentContext currentContext, + ICurrentContext currentContext, GlobalSettings globalSettings) { _cipherRepository = cipherRepository; diff --git a/src/Api/Controllers/CollectionsController.cs b/src/Api/Controllers/CollectionsController.cs index a68cf2c948..0e82c3a871 100644 --- a/src/Api/Controllers/CollectionsController.cs +++ b/src/Api/Controllers/CollectionsController.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Authorization; using Bit.Core.Models.Api; using Bit.Core.Exceptions; using Bit.Core.Services; -using Bit.Core; +using Bit.Core.Context; using Bit.Core.Models.Table; using System.Collections.Generic; @@ -20,13 +20,13 @@ namespace Bit.Api.Controllers private readonly ICollectionRepository _collectionRepository; private readonly ICollectionService _collectionService; private readonly IUserService _userService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public CollectionsController( ICollectionRepository collectionRepository, ICollectionService collectionService, IUserService userService, - CurrentContext currentContext) + ICurrentContext currentContext) { _collectionRepository = collectionRepository; _collectionService = collectionService; diff --git a/src/Api/Controllers/EventsController.cs b/src/Api/Controllers/EventsController.cs index 213e760f34..f5fa5c803e 100644 --- a/src/Api/Controllers/EventsController.cs +++ b/src/Api/Controllers/EventsController.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Authorization; using Bit.Core.Exceptions; using Bit.Core.Models.Api; using Bit.Core.Services; -using Bit.Core; +using Bit.Core.Context; using Bit.Core.Models.Data; namespace Bit.Api.Controllers @@ -20,14 +20,14 @@ namespace Bit.Api.Controllers private readonly ICipherRepository _cipherRepository; private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IEventRepository _eventRepository; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public EventsController( IUserService userService, ICipherRepository cipherRepository, IOrganizationUserRepository organizationUserRepository, IEventRepository eventRepository, - CurrentContext currentContext) + ICurrentContext currentContext) { _userService = userService; _cipherRepository = cipherRepository; diff --git a/src/Api/Controllers/GroupsController.cs b/src/Api/Controllers/GroupsController.cs index eaa1215a84..0ba2eeaf40 100644 --- a/src/Api/Controllers/GroupsController.cs +++ b/src/Api/Controllers/GroupsController.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Authorization; using Bit.Core.Models.Api; using Bit.Core.Exceptions; using Bit.Core.Services; -using Bit.Core; +using Bit.Core.Context; using System.Collections.Generic; namespace Bit.Api.Controllers @@ -18,12 +18,12 @@ namespace Bit.Api.Controllers { private readonly IGroupRepository _groupRepository; private readonly IGroupService _groupService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public GroupsController( IGroupRepository groupRepository, IGroupService groupService, - CurrentContext currentContext) + ICurrentContext currentContext) { _groupRepository = groupRepository; _groupService = groupService; diff --git a/src/Api/Controllers/HibpController.cs b/src/Api/Controllers/HibpController.cs index 9c61578f5d..ba00493a06 100644 --- a/src/Api/Controllers/HibpController.cs +++ b/src/Api/Controllers/HibpController.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Security.Cryptography; using Bit.Core.Services; using Bit.Core; +using Bit.Core.Context; using System.Net; using Bit.Core.Exceptions; using System.Linq; @@ -22,7 +23,7 @@ namespace Bit.Api.Controllers private static HttpClient _httpClient; private readonly IUserService _userService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; private readonly GlobalSettings _globalSettings; private readonly string _userAgent; @@ -33,7 +34,7 @@ namespace Bit.Api.Controllers public HibpController( IUserService userService, - CurrentContext currentContext, + ICurrentContext currentContext, GlobalSettings globalSettings) { _userService = userService; diff --git a/src/Api/Controllers/LicensesController.cs b/src/Api/Controllers/LicensesController.cs index 1b8f753437..bd90d3b387 100644 --- a/src/Api/Controllers/LicensesController.cs +++ b/src/Api/Controllers/LicensesController.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Bit.Core.Services; using Microsoft.AspNetCore.Authorization; -using Bit.Core; +using Bit.Core.Context; using System.Threading.Tasks; using Bit.Core.Models.Business; using Bit.Core.Exceptions; @@ -21,7 +21,7 @@ namespace Bit.Api.Controllers private readonly IUserService _userService; private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationService _organizationService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public LicensesController( ILicensingService licensingService, @@ -29,7 +29,7 @@ namespace Bit.Api.Controllers IUserService userService, IOrganizationRepository organizationRepository, IOrganizationService organizationService, - CurrentContext currentContext) + ICurrentContext currentContext) { _licensingService = licensingService; _userRepository = userRepository; diff --git a/src/Api/Controllers/OrganizationUsersController.cs b/src/Api/Controllers/OrganizationUsersController.cs index 6af6eff0ca..c00653a610 100644 --- a/src/Api/Controllers/OrganizationUsersController.cs +++ b/src/Api/Controllers/OrganizationUsersController.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Authorization; using Bit.Core.Models.Api; using Bit.Core.Exceptions; using Bit.Core.Services; -using Bit.Core; +using Bit.Core.Context; using System.Collections.Generic; using Bit.Core.Models.Business; @@ -23,7 +23,7 @@ namespace Bit.Api.Controllers private readonly ICollectionRepository _collectionRepository; private readonly IGroupRepository _groupRepository; private readonly IUserService _userService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public OrganizationUsersController( IOrganizationRepository organizationRepository, @@ -32,7 +32,7 @@ namespace Bit.Api.Controllers ICollectionRepository collectionRepository, IGroupRepository groupRepository, IUserService userService, - CurrentContext currentContext) + ICurrentContext currentContext) { _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index b4afafeb6c..5afa994a1b 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -9,6 +9,7 @@ using Bit.Core.Models.Api; using Bit.Core.Exceptions; using Bit.Core.Services; using Bit.Core; +using Bit.Core.Context; using Bit.Api.Utilities; using Bit.Core.Models.Business; using Bit.Core.Utilities; @@ -24,7 +25,7 @@ namespace Bit.Api.Controllers private readonly IOrganizationService _organizationService; private readonly IUserService _userService; private readonly IPaymentService _paymentService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; private readonly GlobalSettings _globalSettings; private readonly IPolicyRepository _policyRepository; @@ -34,7 +35,7 @@ namespace Bit.Api.Controllers IOrganizationService organizationService, IUserService userService, IPaymentService paymentService, - CurrentContext currentContext, + ICurrentContext currentContext, GlobalSettings globalSettings, IPolicyRepository policyRepository) { diff --git a/src/Api/Controllers/PoliciesController.cs b/src/Api/Controllers/PoliciesController.cs index d7f1cc0d63..405837f7d1 100644 --- a/src/Api/Controllers/PoliciesController.cs +++ b/src/Api/Controllers/PoliciesController.cs @@ -8,6 +8,7 @@ using Bit.Core.Models.Api; using Bit.Core.Exceptions; using Bit.Core.Services; using Bit.Core; +using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Utilities; using Microsoft.AspNetCore.DataProtection; @@ -23,7 +24,7 @@ namespace Bit.Api.Controllers private readonly IOrganizationService _organizationService; private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IUserService _userService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; private readonly GlobalSettings _globalSettings; private readonly IDataProtector _organizationServiceDataProtector; @@ -33,7 +34,7 @@ namespace Bit.Api.Controllers IOrganizationService organizationService, IOrganizationUserRepository organizationUserRepository, IUserService userService, - CurrentContext currentContext, + ICurrentContext currentContext, GlobalSettings globalSettings, IDataProtectionProvider dataProtectionProvider) { diff --git a/src/Api/Controllers/PushController.cs b/src/Api/Controllers/PushController.cs index 860cf43ef9..d7ee5e6d24 100644 --- a/src/Api/Controllers/PushController.cs +++ b/src/Api/Controllers/PushController.cs @@ -2,6 +2,7 @@ using Bit.Core.Services; using Microsoft.AspNetCore.Authorization; using Bit.Core; +using Bit.Core.Context; using Bit.Core.Exceptions; using Bit.Core.Models.Api; using System.Threading.Tasks; @@ -21,14 +22,14 @@ namespace Bit.Api.Controllers private readonly IPushRegistrationService _pushRegistrationService; private readonly IPushNotificationService _pushNotificationService; private readonly IWebHostEnvironment _environment; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; private readonly GlobalSettings _globalSettings; public PushController( IPushRegistrationService pushRegistrationService, IPushNotificationService pushNotificationService, IWebHostEnvironment environment, - CurrentContext currentContext, + ICurrentContext currentContext, GlobalSettings globalSettings) { _currentContext = currentContext; diff --git a/src/Api/Controllers/TwoFactorController.cs b/src/Api/Controllers/TwoFactorController.cs index a659c1d82e..e9b93feaa2 100644 --- a/src/Api/Controllers/TwoFactorController.cs +++ b/src/Api/Controllers/TwoFactorController.cs @@ -10,6 +10,7 @@ using Bit.Core.Models.Table; using Bit.Core.Enums; using System.Linq; using Bit.Core; +using Bit.Core.Context; using Bit.Core.Repositories; using Bit.Core.Utilities; using Bit.Core.Utilities.Duo; @@ -25,7 +26,7 @@ namespace Bit.Api.Controllers private readonly IOrganizationService _organizationService; private readonly GlobalSettings _globalSettings; private readonly UserManager _userManager; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public TwoFactorController( IUserService userService, @@ -33,7 +34,7 @@ namespace Bit.Api.Controllers IOrganizationService organizationService, GlobalSettings globalSettings, UserManager userManager, - CurrentContext currentContext) + ICurrentContext currentContext) { _userService = userService; _organizationRepository = organizationRepository; diff --git a/src/Api/Public/Controllers/CollectionsController.cs b/src/Api/Public/Controllers/CollectionsController.cs index 62138fbbe0..dec2debecf 100644 --- a/src/Api/Public/Controllers/CollectionsController.cs +++ b/src/Api/Public/Controllers/CollectionsController.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Net; using System.Threading.Tasks; -using Bit.Core; +using Bit.Core.Context; using Bit.Core.Models.Api.Public; using Bit.Core.Repositories; using Bit.Core.Services; @@ -17,12 +17,12 @@ namespace Bit.Api.Public.Controllers { private readonly ICollectionRepository _collectionRepository; private readonly ICollectionService _collectionService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public CollectionsController( ICollectionRepository collectionRepository, ICollectionService collectionService, - CurrentContext currentContext) + ICurrentContext currentContext) { _collectionRepository = collectionRepository; _collectionService = collectionService; diff --git a/src/Api/Public/Controllers/EventsController.cs b/src/Api/Public/Controllers/EventsController.cs index 7130595f34..f7b11445c0 100644 --- a/src/Api/Public/Controllers/EventsController.cs +++ b/src/Api/Public/Controllers/EventsController.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Net; using System.Threading.Tasks; -using Bit.Core; +using Bit.Core.Context; using Bit.Core.Models.Api.Public; using Bit.Core.Models.Data; using Bit.Core.Repositories; @@ -17,12 +17,12 @@ namespace Bit.Api.Public.Controllers { private readonly IEventRepository _eventRepository; private readonly ICipherRepository _cipherRepository; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public EventsController( IEventRepository eventRepository, ICipherRepository cipherRepository, - CurrentContext currentContext) + ICurrentContext currentContext) { _eventRepository = eventRepository; _cipherRepository = cipherRepository; diff --git a/src/Api/Public/Controllers/GroupsController.cs b/src/Api/Public/Controllers/GroupsController.cs index 39c8352868..76e3a3ec7c 100644 --- a/src/Api/Public/Controllers/GroupsController.cs +++ b/src/Api/Public/Controllers/GroupsController.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; -using Bit.Core; +using Bit.Core.Context; using Bit.Core.Models.Api.Public; using Bit.Core.Repositories; using Bit.Core.Services; @@ -18,12 +18,12 @@ namespace Bit.Api.Public.Controllers { private readonly IGroupRepository _groupRepository; private readonly IGroupService _groupService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public GroupsController( IGroupRepository groupRepository, IGroupService groupService, - CurrentContext currentContext) + ICurrentContext currentContext) { _groupRepository = groupRepository; _groupService = groupService; diff --git a/src/Api/Public/Controllers/MembersController.cs b/src/Api/Public/Controllers/MembersController.cs index 58ae110560..2ef1a35e31 100644 --- a/src/Api/Public/Controllers/MembersController.cs +++ b/src/Api/Public/Controllers/MembersController.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; -using Bit.Core; +using Bit.Core.Context; using Bit.Core.Models.Api.Public; using Bit.Core.Models.Business; using Bit.Core.Repositories; @@ -21,14 +21,14 @@ namespace Bit.Api.Public.Controllers private readonly IGroupRepository _groupRepository; private readonly IOrganizationService _organizationService; private readonly IUserService _userService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public MembersController( IOrganizationUserRepository organizationUserRepository, IGroupRepository groupRepository, IOrganizationService organizationService, IUserService userService, - CurrentContext currentContext) + ICurrentContext currentContext) { _organizationUserRepository = organizationUserRepository; _groupRepository = groupRepository; diff --git a/src/Api/Public/Controllers/OrganizationController.cs b/src/Api/Public/Controllers/OrganizationController.cs index 65c7dd987b..c2ec15878e 100644 --- a/src/Api/Public/Controllers/OrganizationController.cs +++ b/src/Api/Public/Controllers/OrganizationController.cs @@ -2,6 +2,7 @@ using System.Net; using System.Threading.Tasks; using Bit.Core; +using Bit.Core.Context; using Bit.Core.Exceptions; using Bit.Core.Models.Api.Public; using Bit.Core.Services; @@ -15,12 +16,12 @@ namespace Bit.Api.Public.Controllers public class OrganizationController : Controller { private readonly IOrganizationService _organizationService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; private readonly GlobalSettings _globalSettings; public OrganizationController( IOrganizationService organizationService, - CurrentContext currentContext, + ICurrentContext currentContext, GlobalSettings globalSettings) { _organizationService = organizationService; diff --git a/src/Api/Public/Controllers/PoliciesController.cs b/src/Api/Public/Controllers/PoliciesController.cs index 85660a4dcf..c2011ff479 100644 --- a/src/Api/Public/Controllers/PoliciesController.cs +++ b/src/Api/Public/Controllers/PoliciesController.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Net; using System.Threading.Tasks; -using Bit.Core; +using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Models.Api.Public; using Bit.Core.Repositories; @@ -20,14 +20,14 @@ namespace Bit.Api.Public.Controllers private readonly IPolicyService _policyService; private readonly IUserService _userService; private readonly IOrganizationService _organizationService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public PoliciesController( IPolicyRepository policyRepository, IPolicyService policyService, IUserService userService, IOrganizationService organizationService, - CurrentContext currentContext) + ICurrentContext currentContext) { _policyRepository = policyRepository; _policyService = policyService; diff --git a/src/Api/Startup.cs b/src/Api/Startup.cs index 58e1661e34..b7b05ab046 100644 --- a/src/Api/Startup.cs +++ b/src/Api/Startup.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Bit.Api.Utilities; using Bit.Core; +using Bit.Core.Context; using Bit.Core.Identity; using Newtonsoft.Json.Serialization; using AspNetCoreRateLimit; @@ -54,7 +55,7 @@ namespace Bit.Api services.AddSqlServerRepositories(globalSettings); // Context - services.AddScoped(); + services.AddScoped(); // Caching services.AddMemoryCache(); diff --git a/src/Billing/Startup.cs b/src/Billing/Startup.cs index cd17c3a69e..484bfdc355 100644 --- a/src/Billing/Startup.cs +++ b/src/Billing/Startup.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Bit.Core; +using Bit.Core.Context; using Stripe; using Bit.Core.Utilities; using Microsoft.AspNetCore.Http; @@ -46,7 +47,7 @@ namespace Bit.Billing services.AddSingleton(); // Context - services.AddScoped(); + services.AddScoped(); // Identity services.AddCustomIdentityServices(globalSettings); diff --git a/src/Core/Context/CurrentContentOrganization.cs b/src/Core/Context/CurrentContentOrganization.cs new file mode 100644 index 0000000000..8456091153 --- /dev/null +++ b/src/Core/Context/CurrentContentOrganization.cs @@ -0,0 +1,24 @@ +using System; +using Bit.Core.Enums; +using Bit.Core.Models.Data; +using Bit.Core.Models.Table; +using Bit.Core.Utilities; + +namespace Bit.Core.Context +{ + public class CurrentContentOrganization + { + public CurrentContentOrganization() { } + + public CurrentContentOrganization(OrganizationUser orgUser) + { + Id = orgUser.OrganizationId; + Type = orgUser.Type; + Permissions = CoreHelpers.LoadClassFromJsonData(orgUser.Permissions); + } + + public Guid Id { get; set; } + public OrganizationUserType Type { get; set; } + public Permissions Permissions { get; set; } + } +} diff --git a/src/Core/CurrentContext.cs b/src/Core/Context/CurrentContext.cs similarity index 94% rename from src/Core/CurrentContext.cs rename to src/Core/Context/CurrentContext.cs index c3035a8e84..9a688d9002 100644 --- a/src/Core/CurrentContext.cs +++ b/src/Core/Context/CurrentContext.cs @@ -10,9 +10,9 @@ using System.Security.Claims; using Bit.Core.Utilities; using Bit.Core.Models.Data; -namespace Bit.Core +namespace Bit.Core.Context { - public class CurrentContext + public class CurrentContext : ICurrentContext { private bool _builtHttpContext; private bool _builtClaimsPrincipal; @@ -296,21 +296,5 @@ namespace Bit.Core ManageUsers = hasClaim("manageusers") }; } - - public class CurrentContentOrganization - { - public CurrentContentOrganization() { } - - public CurrentContentOrganization(OrganizationUser orgUser) - { - Id = orgUser.OrganizationId; - Type = orgUser.Type; - Permissions = CoreHelpers.LoadClassFromJsonData(orgUser.Permissions); - } - - public Guid Id { get; set; } - public OrganizationUserType Type { get; set; } - public Permissions Permissions { get; set; } - } } } diff --git a/src/Core/Context/ICurrentContext.cs b/src/Core/Context/ICurrentContext.cs new file mode 100644 index 0000000000..72455e271f --- /dev/null +++ b/src/Core/Context/ICurrentContext.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using Bit.Core.Enums; +using Bit.Core.Models.Table; +using Bit.Core.Repositories; +using Microsoft.AspNetCore.Http; + +namespace Bit.Core.Context +{ + public interface ICurrentContext + { + HttpContext HttpContext { get; set; } + Guid? UserId { get; set; } + User User { get; set; } + string DeviceIdentifier { get; set; } + DeviceType? DeviceType { get; set; } + string IpAddress { get; set; } + List Organizations { get; set; } + Guid? InstallationId { get; set; } + Guid? OrganizationId { get; set; } + + Task BuildAsync(HttpContext httpContext, GlobalSettings globalSettings); + Task BuildAsync(ClaimsPrincipal user, GlobalSettings globalSettings); + + Task SetContextAsync(ClaimsPrincipal user); + + + bool OrganizationUser(Guid orgId); + bool OrganizationManager(Guid orgId); + bool OrganizationAdmin(Guid orgId); + bool OrganizationOwner(Guid orgId); + bool OrganizationCustom(Guid orgId); + bool AccessBusinessPortal(Guid orgId); + bool AccessEventLogs(Guid orgId); + bool AccessImportExport(Guid orgId); + bool AccessReports(Guid orgId); + bool ManageAllCollections(Guid orgId); + bool ManageAssignedCollections(Guid orgId); + bool ManageGroups(Guid orgId); + bool ManagePolicies(Guid orgId); + bool ManageSso(Guid orgId); + bool ManageUsers(Guid orgId); + + Task> OrganizationMembershipAsync( + IOrganizationUserRepository organizationUserRepository, Guid userId); + } +} diff --git a/src/Core/Enums/PolicyType.cs b/src/Core/Enums/PolicyType.cs index e939c385f2..0d3bb68bab 100644 --- a/src/Core/Enums/PolicyType.cs +++ b/src/Core/Enums/PolicyType.cs @@ -8,5 +8,6 @@ SingleOrg = 3, RequireSso = 4, PersonalOwnership = 5, + DisableSend = 6, } } diff --git a/src/Core/Identity/UserStore.cs b/src/Core/Identity/UserStore.cs index 354b9c7c81..a1125a19eb 100644 --- a/src/Core/Identity/UserStore.cs +++ b/src/Core/Identity/UserStore.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Identity; using Bit.Core.Models.Table; using Bit.Core.Repositories; using Bit.Core.Services; +using Bit.Core.Context; using Microsoft.Extensions.DependencyInjection; namespace Bit.Core.Identity @@ -18,12 +19,12 @@ namespace Bit.Core.Identity { private readonly IServiceProvider _serviceProvider; private readonly IUserRepository _userRepository; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public UserStore( IServiceProvider serviceProvider, IUserRepository userRepository, - CurrentContext currentContext) + ICurrentContext currentContext) { _serviceProvider = serviceProvider; _userRepository = userRepository; diff --git a/src/Core/IdentityServer/BaseRequestValidator.cs b/src/Core/IdentityServer/BaseRequestValidator.cs index d881560ce7..8920674c84 100644 --- a/src/Core/IdentityServer/BaseRequestValidator.cs +++ b/src/Core/IdentityServer/BaseRequestValidator.cs @@ -17,6 +17,7 @@ using System.ComponentModel.DataAnnotations; using System.Reflection; using Microsoft.Extensions.Logging; using Bit.Core.Models.Api; +using Bit.Core.Context; namespace Bit.Core.IdentityServer { @@ -33,7 +34,7 @@ namespace Bit.Core.IdentityServer private readonly IApplicationCacheService _applicationCacheService; private readonly IMailService _mailService; private readonly ILogger _logger; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; private readonly GlobalSettings _globalSettings; private readonly IPolicyRepository _policyRepository; @@ -49,7 +50,7 @@ namespace Bit.Core.IdentityServer IApplicationCacheService applicationCacheService, IMailService mailService, ILogger logger, - CurrentContext currentContext, + ICurrentContext currentContext, GlobalSettings globalSettings, IPolicyRepository policyRepository) { diff --git a/src/Core/IdentityServer/ClientStore.cs b/src/Core/IdentityServer/ClientStore.cs index 9e8e6292c6..7e8d72b13f 100644 --- a/src/Core/IdentityServer/ClientStore.cs +++ b/src/Core/IdentityServer/ClientStore.cs @@ -9,6 +9,7 @@ using IdentityModel; using Bit.Core.Utilities; using System.Security.Claims; using Bit.Core.Services; +using Bit.Core.Context; using System.Collections.ObjectModel; namespace Bit.Core.IdentityServer @@ -21,7 +22,7 @@ namespace Bit.Core.IdentityServer private readonly GlobalSettings _globalSettings; private readonly StaticClientStore _staticClientStore; private readonly ILicensingService _licensingService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; private readonly IOrganizationUserRepository _organizationUserRepository; public ClientStore( @@ -31,7 +32,7 @@ namespace Bit.Core.IdentityServer GlobalSettings globalSettings, StaticClientStore staticClientStore, ILicensingService licensingService, - CurrentContext currentContext, + ICurrentContext currentContext, IOrganizationUserRepository organizationUserRepository) { _installationRepository = installationRepository; diff --git a/src/Core/IdentityServer/CustomTokenRequestValidator.cs b/src/Core/IdentityServer/CustomTokenRequestValidator.cs index f190891264..a113d5af88 100644 --- a/src/Core/IdentityServer/CustomTokenRequestValidator.cs +++ b/src/Core/IdentityServer/CustomTokenRequestValidator.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using Bit.Core.Services; +using Bit.Core.Context; using System.Linq; using Bit.Core.Identity; using Microsoft.Extensions.Logging; @@ -31,7 +32,7 @@ namespace Bit.Core.IdentityServer IApplicationCacheService applicationCacheService, IMailService mailService, ILogger logger, - CurrentContext currentContext, + ICurrentContext currentContext, GlobalSettings globalSettings, IPolicyRepository policyRepository) : base(userManager, deviceRepository, deviceService, userService, eventService, diff --git a/src/Core/IdentityServer/ProfileService.cs b/src/Core/IdentityServer/ProfileService.cs index 1e0d3ce19f..ac69fc0935 100644 --- a/src/Core/IdentityServer/ProfileService.cs +++ b/src/Core/IdentityServer/ProfileService.cs @@ -9,6 +9,7 @@ using System.Linq; using System; using IdentityModel; using Bit.Core.Utilities; +using Bit.Core.Context; namespace Bit.Core.IdentityServer { @@ -17,13 +18,13 @@ namespace Bit.Core.IdentityServer private readonly IUserService _userService; private readonly IOrganizationUserRepository _organizationUserRepository; private readonly ILicensingService _licensingService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; public ProfileService( IUserService userService, IOrganizationUserRepository organizationUserRepository, ILicensingService licensingService, - CurrentContext currentContext) + ICurrentContext currentContext) { _userService = userService; _organizationUserRepository = organizationUserRepository; diff --git a/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs b/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs index 7c4c653258..72c47e747c 100644 --- a/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs +++ b/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs @@ -8,6 +8,7 @@ using System.Security.Claims; using System.Threading.Tasks; using Bit.Core.Services; using Bit.Core.Identity; +using Bit.Core.Context; using Microsoft.Extensions.Logging; namespace Bit.Core.IdentityServer @@ -30,7 +31,7 @@ namespace Bit.Core.IdentityServer IApplicationCacheService applicationCacheService, IMailService mailService, ILogger logger, - CurrentContext currentContext, + ICurrentContext currentContext, GlobalSettings globalSettings, IPolicyRepository policyRepository) : base(userManager, deviceRepository, deviceService, userService, eventService, diff --git a/src/Core/Models/Data/EventMessage.cs b/src/Core/Models/Data/EventMessage.cs index 8df07d49a7..8ba5ec2037 100644 --- a/src/Core/Models/Data/EventMessage.cs +++ b/src/Core/Models/Data/EventMessage.cs @@ -1,5 +1,6 @@ using System; using Bit.Core.Enums; +using Bit.Core.Context; namespace Bit.Core.Models.Data { @@ -7,7 +8,7 @@ namespace Bit.Core.Models.Data { public EventMessage() { } - public EventMessage(CurrentContext currentContext) + public EventMessage(ICurrentContext currentContext) : base() { IpAddress = currentContext.IpAddress; diff --git a/src/Core/Resources/SharedResources.en.resx b/src/Core/Resources/SharedResources.en.resx index 561266824a..7d89d89a3c 100644 --- a/src/Core/Resources/SharedResources.en.resx +++ b/src/Core/Resources/SharedResources.en.resx @@ -585,6 +585,17 @@ Require users to save vault items to an organization by removing the personal ownership option. + Organization users that can manage the organization's policies are exempt from this policy's enforcement. + + + Disable Send + 'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated. + + + Do not allow users to create or edit a Bitwarden Send. Deleting an existing Send is still allowed. + 'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated. + + Organization Owners and Administrators are exempt from this policy's enforcement. diff --git a/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs b/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs index 723bc0c7b3..390d91cade 100644 --- a/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs +++ b/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Bit.Core.Context; using Bit.Core.Models.Table; using Bit.Core.Enums; using Newtonsoft.Json; @@ -181,7 +182,7 @@ namespace Bit.Core.Services } var currentContext = _httpContextAccessor?.HttpContext?. - RequestServices.GetService(typeof(CurrentContext)) as CurrentContext; + RequestServices.GetService(typeof(ICurrentContext)) as ICurrentContext; return currentContext?.DeviceIdentifier; } diff --git a/src/Core/Services/Implementations/EventService.cs b/src/Core/Services/Implementations/EventService.cs index ab62f87e39..a1ea1cd29e 100644 --- a/src/Core/Services/Implementations/EventService.cs +++ b/src/Core/Services/Implementations/EventService.cs @@ -6,6 +6,7 @@ using Bit.Core.Models.Data; using System.Linq; using System.Collections.Generic; using Bit.Core.Models.Table; +using Bit.Core.Context; namespace Bit.Core.Services { @@ -14,14 +15,14 @@ namespace Bit.Core.Services private readonly IEventWriteService _eventWriteService; private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IApplicationCacheService _applicationCacheService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; private readonly GlobalSettings _globalSettings; public EventService( IEventWriteService eventWriteService, IOrganizationUserRepository organizationUserRepository, IApplicationCacheService applicationCacheService, - CurrentContext currentContext, + ICurrentContext currentContext, GlobalSettings globalSettings) { _eventWriteService = eventWriteService; diff --git a/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs b/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs index d7cf981127..4228aa76c0 100644 --- a/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs +++ b/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Bit.Core.Models.Table; using Microsoft.Azure.NotificationHubs; +using Bit.Core.Context; using Bit.Core.Enums; using Newtonsoft.Json; using System.Collections.Generic; @@ -206,7 +207,7 @@ namespace Bit.Core.Services } var currentContext = _httpContextAccessor?.HttpContext?. - RequestServices.GetService(typeof(CurrentContext)) as CurrentContext; + RequestServices.GetService(typeof(ICurrentContext)) as ICurrentContext; return currentContext?.DeviceIdentifier; } diff --git a/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs b/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs index c4d8eed121..ef27aee231 100644 --- a/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs +++ b/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Bit.Core.Context; using Bit.Core.Models.Table; using Bit.Core.Enums; using Newtonsoft.Json; @@ -187,7 +188,7 @@ namespace Bit.Core.Services } var currentContext = _httpContextAccessor?.HttpContext?. - RequestServices.GetService(typeof(CurrentContext)) as CurrentContext; + RequestServices.GetService(typeof(ICurrentContext)) as ICurrentContext; return currentContext?.DeviceIdentifier; } diff --git a/src/Core/Services/Implementations/RelayPushNotificationService.cs b/src/Core/Services/Implementations/RelayPushNotificationService.cs index 3611d5276c..966962147f 100644 --- a/src/Core/Services/Implementations/RelayPushNotificationService.cs +++ b/src/Core/Services/Implementations/RelayPushNotificationService.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Bit.Core.Context; using Bit.Core.Models.Table; using Bit.Core.Enums; using Microsoft.AspNetCore.Http; @@ -198,7 +199,7 @@ namespace Bit.Core.Services private async Task AddCurrentContextAsync(PushSendRequestModel request, bool addIdentifier) { var currentContext = _httpContextAccessor?.HttpContext?. - RequestServices.GetService(typeof(CurrentContext)) as CurrentContext; + RequestServices.GetService(typeof(ICurrentContext)) as ICurrentContext; if (!string.IsNullOrWhiteSpace(currentContext?.DeviceIdentifier)) { var device = await _deviceRepository.GetByIdentifierAsync(currentContext.DeviceIdentifier); diff --git a/src/Core/Services/Implementations/SendService.cs b/src/Core/Services/Implementations/SendService.cs index 73f3eb0872..59105e3fc9 100644 --- a/src/Core/Services/Implementations/SendService.cs +++ b/src/Core/Services/Implementations/SendService.cs @@ -1,6 +1,9 @@ using System; using System.IO; +using System.Linq; using System.Threading.Tasks; +using Bit.Core.Context; +using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Data; using Bit.Core.Models.Table; @@ -14,12 +17,14 @@ namespace Bit.Core.Services { private readonly ISendRepository _sendRepository; private readonly IUserRepository _userRepository; + private readonly IPolicyRepository _policyRepository; private readonly IUserService _userService; private readonly IOrganizationRepository _organizationRepository; private readonly ISendFileStorageService _sendFileStorageService; private readonly IPasswordHasher _passwordHasher; private readonly IPushNotificationService _pushService; private readonly GlobalSettings _globalSettings; + private readonly ICurrentContext _currentContext; public SendService( ISendRepository sendRepository, @@ -29,20 +34,27 @@ namespace Bit.Core.Services ISendFileStorageService sendFileStorageService, IPasswordHasher passwordHasher, IPushNotificationService pushService, - GlobalSettings globalSettings) + GlobalSettings globalSettings, + IPolicyRepository policyRepository, + ICurrentContext currentContext) { _sendRepository = sendRepository; _userRepository = userRepository; _userService = userService; + _policyRepository = policyRepository; _organizationRepository = organizationRepository; _sendFileStorageService = sendFileStorageService; _passwordHasher = passwordHasher; _pushService = pushService; _globalSettings = globalSettings; + _currentContext = currentContext; } public async Task SaveSendAsync(Send send) { + // Make sure user can save Sends + await ValidateUserCanSaveAsync(send.UserId); + if (send.Id == default(Guid)) { await _sendRepository.CreateAsync(send); @@ -58,7 +70,7 @@ namespace Bit.Core.Services public async Task CreateSendAsync(Send send, SendFileData data, Stream stream, long requestLength) { - if (send.Type != Enums.SendType.File) + if (send.Type != SendType.File) { throw new BadRequestException("Send is not of type \"file\"."); } @@ -174,5 +186,28 @@ namespace Bit.Core.Services { return _passwordHasher.HashPassword(new User(), password); } + + private async Task ValidateUserCanSaveAsync(Guid? userId) + { + if (!userId.HasValue || (!_currentContext.Organizations?.Any() ?? true)) + { + return; + } + + var policies = await _policyRepository.GetManyByUserIdAsync(userId.Value); + + if (policies == null) + { + return; + } + + foreach (var policy in policies.Where(p => p.Enabled && p.Type == PolicyType.DisableSend)) + { + if (!_currentContext.ManagePolicies(policy.OrganizationId)) + { + throw new BadRequestException("Due to an Enterprise Policy, you are only able to delete an existing Send."); + } + } + } } } diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs index bbe602c16b..2c05196466 100644 --- a/src/Core/Services/Implementations/UserService.cs +++ b/src/Core/Services/Implementations/UserService.cs @@ -14,6 +14,7 @@ using Bit.Core.Models.Business; using U2fLib = U2F.Core.Crypto.U2F; using U2F.Core.Models; using U2F.Core.Utils; +using Bit.Core.Context; using Bit.Core.Exceptions; using Bit.Core.Utilities; using System.IO; @@ -46,7 +47,7 @@ namespace Bit.Core.Services private readonly IPolicyRepository _policyRepository; private readonly IDataProtector _organizationServiceDataProtector; private readonly IReferenceEventService _referenceEventService; - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; private readonly GlobalSettings _globalSettings; private readonly IOrganizationService _organizationService; @@ -74,7 +75,7 @@ namespace Bit.Core.Services IPaymentService paymentService, IPolicyRepository policyRepository, IReferenceEventService referenceEventService, - CurrentContext currentContext, + ICurrentContext currentContext, GlobalSettings globalSettings, IOrganizationService organizationService) : base( diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs index 2539ccebd3..e2ab4c0b4c 100644 --- a/src/Core/Utilities/CoreHelpers.cs +++ b/src/Core/Utilities/CoreHelpers.cs @@ -15,6 +15,7 @@ using System.Globalization; using System.Web; using Microsoft.AspNetCore.DataProtection; using Bit.Core.Enums; +using Bit.Core.Context; using System.Threading.Tasks; using Microsoft.Azure.Storage; using Microsoft.Azure.Storage.Blob; @@ -685,7 +686,7 @@ namespace Bit.Core.Utilities return configDict; } - public static List> BuildIdentityClaims(User user, ICollection orgs, bool isPremium) + public static List> BuildIdentityClaims(User user, ICollection orgs, bool isPremium) { var claims = new List>() { diff --git a/src/Core/Utilities/CurrentContextMiddleware.cs b/src/Core/Utilities/CurrentContextMiddleware.cs index cb24de0c37..6149ef2577 100644 --- a/src/Core/Utilities/CurrentContextMiddleware.cs +++ b/src/Core/Utilities/CurrentContextMiddleware.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Http; using System.Threading.Tasks; +using Bit.Core.Context; namespace Bit.Core.Utilities { @@ -12,7 +13,7 @@ namespace Bit.Core.Utilities _next = next; } - public async Task Invoke(HttpContext httpContext, CurrentContext currentContext, GlobalSettings globalSettings) + public async Task Invoke(HttpContext httpContext, ICurrentContext currentContext, GlobalSettings globalSettings) { await currentContext.BuildAsync(httpContext, globalSettings); await _next.Invoke(httpContext); diff --git a/src/Events/Controllers/CollectController.cs b/src/Events/Controllers/CollectController.cs index f97c85a90e..7fede39b1c 100644 --- a/src/Events/Controllers/CollectController.cs +++ b/src/Events/Controllers/CollectController.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Bit.Core; +using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Models.Table; using Bit.Core.Repositories; @@ -18,12 +18,12 @@ namespace Bit.Events.Controllers [Authorize("Application")] public class CollectController : Controller { - private readonly CurrentContext _currentContext; + private readonly ICurrentContext _currentContext; private readonly IEventService _eventService; private readonly ICipherRepository _cipherRepository; public CollectController( - CurrentContext currentContext, + ICurrentContext currentContext, IEventService eventService, ICipherRepository cipherRepository) { diff --git a/src/Events/Startup.cs b/src/Events/Startup.cs index 7ddc299596..86cc26d945 100644 --- a/src/Events/Startup.cs +++ b/src/Events/Startup.cs @@ -1,5 +1,6 @@ using System.Globalization; using Bit.Core; +using Bit.Core.Context; using Bit.Core.Services; using Bit.Core.Utilities; using IdentityModel; @@ -35,7 +36,7 @@ namespace Bit.Events services.AddSqlServerRepositories(globalSettings); // Context - services.AddScoped(); + services.AddScoped(); // Identity services.AddIdentityAuthenticationServices(globalSettings, Environment, config => diff --git a/src/Identity/Startup.cs b/src/Identity/Startup.cs index f211c770a9..e12a21e187 100644 --- a/src/Identity/Startup.cs +++ b/src/Identity/Startup.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Configuration; using Bit.Core; +using Bit.Core.Context; using Bit.Core.Utilities; using AspNetCoreRateLimit; using System.Globalization; @@ -49,7 +50,7 @@ namespace Bit.Identity services.AddSqlServerRepositories(globalSettings); // Context - services.AddScoped(); + services.AddScoped(); // Caching services.AddMemoryCache(); diff --git a/src/Notifications/NotificationsHub.cs b/src/Notifications/NotificationsHub.cs index adde6ed8d0..d40e885323 100644 --- a/src/Notifications/NotificationsHub.cs +++ b/src/Notifications/NotificationsHub.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Bit.Core; +using Bit.Core.Context; using Microsoft.AspNetCore.Authorization; namespace Bit.Notifications diff --git a/test/Core.Test/AutoFixture/CurrentContextFixtures.cs b/test/Core.Test/AutoFixture/CurrentContextFixtures.cs new file mode 100644 index 0000000000..21b82102bd --- /dev/null +++ b/test/Core.Test/AutoFixture/CurrentContextFixtures.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using AutoFixture; +using AutoFixture.Kernel; +using Bit.Core.Context; + +namespace Bit.Core.Test.AutoFixture.CurrentContextFixtures +{ + internal class CurrentContext : ICustomization + { + public void Customize(IFixture fixture) + { + fixture.Customizations.Add(new CurrentContextBuilder()); + } + } + + internal class CurrentContextBuilder : ISpecimenBuilder + { + public object Create(object request, ISpecimenContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + if (!(request is Type typeRequest)) + { + return new NoSpecimen(); + } + if (typeof(ICurrentContext) != typeRequest) + { + return new NoSpecimen(); + } + + var obj = new Fixture().WithAutoNSubstitutions().Create(); + obj.Organizations = context.Create>(); + return obj; + } + } +} diff --git a/test/Core.Test/AutoFixture/SendFixtures.cs b/test/Core.Test/AutoFixture/SendFixtures.cs new file mode 100644 index 0000000000..5847684013 --- /dev/null +++ b/test/Core.Test/AutoFixture/SendFixtures.cs @@ -0,0 +1,65 @@ +using System; +using AutoFixture; +using Bit.Core.Models.Table; +using Bit.Core.Test.AutoFixture.Attributes; + +namespace Bit.Core.Test.AutoFixture.SendFixtures +{ + internal class OrganizationSend : ICustomization + { + public Guid? OrganizationId { get; set; } + public void Customize(IFixture fixture) + { + fixture.Customize(composer => composer + .With(s => s.OrganizationId, OrganizationId ?? Guid.NewGuid()) + .Without(s => s.UserId)); + } + } + + internal class UserSend : ICustomization + { + public Guid? UserId { get; set; } + public void Customize(IFixture fixture) + { + fixture.Customize(composer => composer + .With(s => s.UserId, UserId ?? Guid.NewGuid()) + .Without(s => s.OrganizationId)); + } + } + + internal class UserSendAutoDataAttribute : CustomAutoDataAttribute + { + public UserSendAutoDataAttribute(string userId = null) : base(new SutProviderCustomization(), + new UserSend { UserId = userId == null ? (Guid?)null : new Guid(userId) }) + { } + } + internal class InlineUserSendAutoDataAttribute : InlineCustomAutoDataAttribute + { + public InlineUserSendAutoDataAttribute(params object[] values) : base(new[] { typeof(CurrentContextFixtures.CurrentContext), + typeof(SutProviderCustomization), typeof(UserSend) }, values) + { } + } + + internal class InlineKnownUserSendAutoDataAttribute : InlineCustomAutoDataAttribute + { + public InlineKnownUserSendAutoDataAttribute(string userId, params object[] values) : base(new ICustomization[] + { new CurrentContextFixtures.CurrentContext(), new SutProviderCustomization(), + new UserSend { UserId = new Guid(userId) } }, values) + { } + } + + internal class OrganizationSendAutoDataAttribute : CustomAutoDataAttribute + { + public OrganizationSendAutoDataAttribute(string organizationId = null) : base(new CurrentContextFixtures.CurrentContext(), + new SutProviderCustomization(), + new OrganizationSend { OrganizationId = organizationId == null ? (Guid?)null : new Guid(organizationId) }) + { } + } + + internal class InlineOrganizationSendAutoDataAttribute : InlineCustomAutoDataAttribute + { + public InlineOrganizationSendAutoDataAttribute(params object[] values) : base(new[] { typeof(CurrentContextFixtures.CurrentContext), + typeof(SutProviderCustomization), typeof(OrganizationSend) }, values) + { } + } +} diff --git a/test/Core.Test/AutoFixture/SutProvider.cs b/test/Core.Test/AutoFixture/SutProvider.cs index 220777c44d..fdc4fa7965 100644 --- a/test/Core.Test/AutoFixture/SutProvider.cs +++ b/test/Core.Test/AutoFixture/SutProvider.cs @@ -16,10 +16,12 @@ namespace Bit.Core.Test.AutoFixture public TSut Sut { get; private set; } public Type SutType => typeof(TSut); - public SutProvider() + public SutProvider() : this(new Fixture()) { } + + public SutProvider(IFixture fixture) { _dependencies = new Dictionary>(); - _fixture = new Fixture().WithAutoNSubstitutions(); + _fixture = (fixture ?? new Fixture()).WithAutoNSubstitutions(); _constructorParameterRelay = new ConstructorParameterRelay(this, _fixture); _fixture.Customizations.Add(_constructorParameterRelay); } diff --git a/test/Core.Test/AutoFixture/SutProviderCustomization.cs b/test/Core.Test/AutoFixture/SutProviderCustomization.cs index ecafba5b3d..549af17e78 100644 --- a/test/Core.Test/AutoFixture/SutProviderCustomization.cs +++ b/test/Core.Test/AutoFixture/SutProviderCustomization.cs @@ -6,6 +6,8 @@ namespace Bit.Core.Test.AutoFixture { public class SutProviderCustomization : ICustomization, ISpecimenBuilder { + private IFixture _fixture = null; + public object Create(object request, ISpecimenContext context) { if (context == null) @@ -21,11 +23,12 @@ namespace Bit.Core.Test.AutoFixture return new NoSpecimen(); } - return ((ISutProvider)Activator.CreateInstance(typeRequest)).Create(); + return ((ISutProvider)Activator.CreateInstance(typeRequest, _fixture)).Create(); } public void Customize(IFixture fixture) { + _fixture = fixture; fixture.Customizations.Add(this); } } diff --git a/test/Core.Test/Services/EventServiceTests.cs b/test/Core.Test/Services/EventServiceTests.cs index 60bcd5cd96..14fbf2c3b5 100644 --- a/test/Core.Test/Services/EventServiceTests.cs +++ b/test/Core.Test/Services/EventServiceTests.cs @@ -1,4 +1,5 @@ using System; +using Bit.Core.Context; using Bit.Core.Repositories; using Bit.Core.Services; using NSubstitute; diff --git a/test/Core.Test/Services/SendServiceTests.cs b/test/Core.Test/Services/SendServiceTests.cs new file mode 100644 index 0000000000..2d7a331675 --- /dev/null +++ b/test/Core.Test/Services/SendServiceTests.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Bit.Core.Context; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.Models.Data; +using Bit.Core.Models.Table; +using Bit.Core.Repositories; +using Bit.Core.Services; +using Bit.Core.Test.AutoFixture; +using Bit.Core.Test.AutoFixture.SendFixtures; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.Services +{ + public class SendServiceTests + { + private void SaveSendAsync_Setup(SendType sendType, bool canManagePolicies, + SutProvider sutProvider, Send send, List policies) + { + send.Id = default; + send.Type = sendType; + + policies.First().Type = PolicyType.DisableSend; + policies.First().Enabled = true; + + sutProvider.GetDependency().GetManyByUserIdAsync(send.UserId.Value).Returns(policies); + sutProvider.GetDependency().ManagePolicies(Arg.Any()).Returns(canManagePolicies); + } + + [Theory] + [InlineUserSendAutoData(SendType.File)] + [InlineUserSendAutoData(SendType.Text)] + public async void SaveSendAsync_DisableSend_CantManagePolicies_throws(SendType sendType, + SutProvider sutProvider, Send send, List policies) + { + SaveSendAsync_Setup(sendType, canManagePolicies: false, sutProvider, send, policies); + + await Assert.ThrowsAsync(() => sutProvider.Sut.SaveSendAsync(send)); + } + + [Theory] + [InlineUserSendAutoData(SendType.File)] + [InlineUserSendAutoData(SendType.Text)] + public async void SaveSendAsync_DisableSend_DisabledPolicy_CantManagePolicies_success(SendType sendType, + SutProvider sutProvider, Send send, List policies) + { + SaveSendAsync_Setup(sendType, canManagePolicies: false, sutProvider, send, policies); + foreach (var policy in policies.Where(p => p.Type == PolicyType.DisableSend)) + { + policy.Enabled = false; + } + + await sutProvider.Sut.SaveSendAsync(send); + + await sutProvider.GetDependency().Received(1).CreateAsync(send); + } + + [Theory] + [InlineUserSendAutoData(SendType.File)] + [InlineUserSendAutoData(SendType.Text)] + public async void SaveSendAsync_DisableSend_CanManagePolicies_success(SendType sendType, + SutProvider sutProvider, Send send, List policies) + { + SaveSendAsync_Setup(sendType, canManagePolicies: true, sutProvider, send, policies); + + await sutProvider.Sut.SaveSendAsync(send); + + await sutProvider.GetDependency().Received(1).CreateAsync(send); + } + } +} diff --git a/test/Core.Test/Services/UserServiceTests.cs b/test/Core.Test/Services/UserServiceTests.cs index ecb62a5d05..aab31e50a5 100644 --- a/test/Core.Test/Services/UserServiceTests.cs +++ b/test/Core.Test/Services/UserServiceTests.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Options; using Microsoft.Extensions.Logging; using NSubstitute; using Xunit; +using Bit.Core.Context; namespace Bit.Core.Test.Services {