From 70f5fd50303c82d2ff0cd2d7170beaa0a34f32f7 Mon Sep 17 00:00:00 2001 From: Vincent Salucci <26154748+vincentsalucci@users.noreply.github.com> Date: Fri, 11 Dec 2020 10:45:26 -0600 Subject: [PATCH] [Policy] Personal Ownership (#1013) * Initial commit of disable personal vault policy * Added new sproc // updated policy check (was missing conditionals) * Updated DeMorgan's law logic --- .../src/Portal/Models/PolicyEditModel.cs | 1 + .../src/Portal/Models/PolicyModel.cs | 5 ++++ .../src/Portal/Views/Policies/Edit.cshtml | 11 +++++++++ src/Core/Enums/PolicyType.cs | 1 + .../IOrganizationUserRepository.cs | 2 ++ .../SqlServer/OrganizationUserRepository.cs | 14 +++++++++++ src/Core/Resources/SharedResources.en.resx | 9 +++++++ .../Services/Implementations/CipherService.cs | 18 ++++++++++++++ src/Sql/Sql.sqlproj | 1 + ...tails_ReadByUserIdStatusOrganizationId.sql | 17 +++++++++++++ ...> 2020-12-04_00_OrgUserReadByOrgEmail.sql} | 0 ...gUserOrgDetailsReadByUserIdStatusOrgId.sql | 24 +++++++++++++++++++ 12 files changed, 103 insertions(+) create mode 100644 src/Sql/dbo/Stored Procedures/OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId.sql rename util/Migrator/DbScripts/{2020_12_04_00_OrgUserReadByOrgEmail.sql => 2020-12-04_00_OrgUserReadByOrgEmail.sql} (100%) create mode 100644 util/Migrator/DbScripts/2020-12-06_00_OrgUserOrgDetailsReadByUserIdStatusOrgId.sql diff --git a/bitwarden_license/src/Portal/Models/PolicyEditModel.cs b/bitwarden_license/src/Portal/Models/PolicyEditModel.cs index 0ed75eab9e..4d2cf14a5d 100644 --- a/bitwarden_license/src/Portal/Models/PolicyEditModel.cs +++ b/bitwarden_license/src/Portal/Models/PolicyEditModel.cs @@ -84,6 +84,7 @@ namespace Bit.Portal.Models case PolicyType.SingleOrg: case PolicyType.TwoFactorAuthentication: case PolicyType.RequireSso: + case PolicyType.PersonalOwnership: break; default: throw new ArgumentOutOfRangeException(); diff --git a/bitwarden_license/src/Portal/Models/PolicyModel.cs b/bitwarden_license/src/Portal/Models/PolicyModel.cs index 057ec18c04..1c365148ab 100644 --- a/bitwarden_license/src/Portal/Models/PolicyModel.cs +++ b/bitwarden_license/src/Portal/Models/PolicyModel.cs @@ -41,6 +41,11 @@ namespace Bit.Portal.Models NameKey = "RequireSso"; DescriptionKey = "RequireSsoDescription"; break; + + case PolicyType.PersonalOwnership: + NameKey = "PersonalOwnership"; + DescriptionKey = "PersonalOwnershipDescription"; + break; default: throw new ArgumentOutOfRangeException(); diff --git a/bitwarden_license/src/Portal/Views/Policies/Edit.cshtml b/bitwarden_license/src/Portal/Views/Policies/Edit.cshtml index ac1c1488ba..203e22c7a0 100644 --- a/bitwarden_license/src/Portal/Views/Policies/Edit.cshtml +++ b/bitwarden_license/src/Portal/Views/Policies/Edit.cshtml @@ -51,6 +51,17 @@ } + @if (Model.PolicyType == PolicyType.PersonalOwnership) + { + + } +
diff --git a/src/Core/Enums/PolicyType.cs b/src/Core/Enums/PolicyType.cs index bfd9514f97..e939c385f2 100644 --- a/src/Core/Enums/PolicyType.cs +++ b/src/Core/Enums/PolicyType.cs @@ -7,5 +7,6 @@ PasswordGenerator = 2, SingleOrg = 3, RequireSso = 4, + PersonalOwnership = 5, } } diff --git a/src/Core/Repositories/IOrganizationUserRepository.cs b/src/Core/Repositories/IOrganizationUserRepository.cs index 92dcf595dc..04a87fffb4 100644 --- a/src/Core/Repositories/IOrganizationUserRepository.cs +++ b/src/Core/Repositories/IOrganizationUserRepository.cs @@ -23,6 +23,8 @@ namespace Bit.Core.Repositories Task> GetManyDetailsByOrganizationAsync(Guid organizationId); Task> GetManyDetailsByUserAsync(Guid userId, OrganizationUserStatusType? status = null); + Task GetDetailsByUserAsync(Guid userId, Guid organizationId, + OrganizationUserStatusType? status = null); Task UpdateGroupsAsync(Guid orgUserId, IEnumerable groupIds); Task CreateAsync(OrganizationUser obj, IEnumerable collections); Task ReplaceAsync(OrganizationUser obj, IEnumerable collections); diff --git a/src/Core/Repositories/SqlServer/OrganizationUserRepository.cs b/src/Core/Repositories/SqlServer/OrganizationUserRepository.cs index 76407cc7d5..1fa94027af 100644 --- a/src/Core/Repositories/SqlServer/OrganizationUserRepository.cs +++ b/src/Core/Repositories/SqlServer/OrganizationUserRepository.cs @@ -184,6 +184,20 @@ namespace Bit.Core.Repositories.SqlServer return results.ToList(); } } + + public async Task GetDetailsByUserAsync(Guid userId, + Guid organizationId, OrganizationUserStatusType? status = null) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + "[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId]", + new { UserId = userId, Status = status, OrganizationId = organizationId }, + commandType: CommandType.StoredProcedure); + + return results.SingleOrDefault(); + } + } public async Task UpdateGroupsAsync(Guid orgUserId, IEnumerable groupIds) { diff --git a/src/Core/Resources/SharedResources.en.resx b/src/Core/Resources/SharedResources.en.resx index 56ff3d83bb..d020ab9851 100644 --- a/src/Core/Resources/SharedResources.en.resx +++ b/src/Core/Resources/SharedResources.en.resx @@ -566,4 +566,13 @@ Organization Owners and Administrators are exempt from this policy's enforcement. + + Personal Ownership + + + Require users to save vault items to an organization by removing the personal ownership option. + + + Organization Owners and Administrators are exempt from this policy's enforcement. + diff --git a/src/Core/Services/Implementations/CipherService.cs b/src/Core/Services/Implementations/CipherService.cs index ce8319364a..4307fa13cc 100644 --- a/src/Core/Services/Implementations/CipherService.cs +++ b/src/Core/Services/Implementations/CipherService.cs @@ -27,6 +27,7 @@ namespace Bit.Core.Services private readonly IAttachmentStorageService _attachmentStorageService; private readonly IEventService _eventService; private readonly IUserService _userService; + private readonly IPolicyRepository _policyRepository; private readonly GlobalSettings _globalSettings; public CipherService( @@ -41,6 +42,7 @@ namespace Bit.Core.Services IAttachmentStorageService attachmentStorageService, IEventService eventService, IUserService userService, + IPolicyRepository policyRepository, GlobalSettings globalSettings) { _cipherRepository = cipherRepository; @@ -54,6 +56,7 @@ namespace Bit.Core.Services _attachmentStorageService = attachmentStorageService; _eventService = eventService; _userService = userService; + _policyRepository = policyRepository; _globalSettings = globalSettings; } @@ -118,6 +121,21 @@ namespace Bit.Core.Services } else { + // Make sure the user can save new ciphers to their personal vault + var userPolicies = await _policyRepository.GetManyByUserIdAsync(savingUserId); + if (userPolicies != null) + { + foreach (var policy in userPolicies.Where(p => p.Enabled && p.Type == PolicyType.PersonalOwnership)) + { + var org = await _organizationUserRepository.GetDetailsByUserAsync(savingUserId, policy.OrganizationId, + OrganizationUserStatusType.Confirmed); + if(org != null && org.Enabled && org.UsePolicies + && org.Type != OrganizationUserType.Admin && org.Type != OrganizationUserType.Owner) + { + throw new BadRequestException("Due to an Enterprise Policy, you are restricted from saving items to your personal vault."); + } + } + } await _cipherRepository.CreateAsync(cipher); } await _eventService.LogCipherEventAsync(cipher, Enums.EventType.Cipher_Created); diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index 9d500ae401..70b9b3edf9 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -295,6 +295,7 @@ + diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId.sql b/src/Sql/dbo/Stored Procedures/OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId.sql new file mode 100644 index 0000000000..a3e15d78e0 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId.sql @@ -0,0 +1,17 @@ +CREATE PROCEDURE [dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId] + @UserId UNIQUEIDENTIFIER, + @Status TINYINT, + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[OrganizationUserOrganizationDetailsView] + WHERE + [UserId] = @UserId + AND [OrganizationId] = @OrganizationId + AND (@Status IS NULL OR [Status] = @Status) +END \ No newline at end of file diff --git a/util/Migrator/DbScripts/2020_12_04_00_OrgUserReadByOrgEmail.sql b/util/Migrator/DbScripts/2020-12-04_00_OrgUserReadByOrgEmail.sql similarity index 100% rename from util/Migrator/DbScripts/2020_12_04_00_OrgUserReadByOrgEmail.sql rename to util/Migrator/DbScripts/2020-12-04_00_OrgUserReadByOrgEmail.sql diff --git a/util/Migrator/DbScripts/2020-12-06_00_OrgUserOrgDetailsReadByUserIdStatusOrgId.sql b/util/Migrator/DbScripts/2020-12-06_00_OrgUserOrgDetailsReadByUserIdStatusOrgId.sql new file mode 100644 index 0000000000..b17334e810 --- /dev/null +++ b/util/Migrator/DbScripts/2020-12-06_00_OrgUserOrgDetailsReadByUserIdStatusOrgId.sql @@ -0,0 +1,24 @@ +IF OBJECT_ID('[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId] +END +GO + +CREATE PROCEDURE [dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId] + @UserId UNIQUEIDENTIFIER, + @Status TINYINT, + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[OrganizationUserOrganizationDetailsView] + WHERE + [UserId] = @UserId + AND [OrganizationId] = @OrganizationId + AND (@Status IS NULL OR [Status] = @Status) +END +GO