From 74874a1c38683d9a5ed152ed5ce0521f68702c09 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Mon, 11 Jun 2018 14:25:53 -0400 Subject: [PATCH] return collection readonly details --- src/Api/Controllers/CollectionsController.cs | 19 ++++--- src/Api/Controllers/SyncController.cs | 5 +- .../Api/Response/CollectionResponseModel.cs | 15 +++++- .../Models/Api/Response/SyncResponseModel.cs | 7 +-- src/Core/Models/Data/CollectionDetails.cs | 9 ++++ .../Repositories/ICollectionRepository.cs | 2 +- .../SqlServer/CollectionRepository.cs | 6 +-- .../Collection_ReadByUserId.sql | 21 ++++---- .../2018-06-11_00_WebVaultUpdates.sql | 49 +++++++++++++++++++ util/Setup/Setup.csproj | 2 + 10 files changed, 108 insertions(+), 27 deletions(-) create mode 100644 src/Core/Models/Data/CollectionDetails.cs create mode 100644 util/Setup/DbScripts/2018-06-11_00_WebVaultUpdates.sql diff --git a/src/Api/Controllers/CollectionsController.cs b/src/Api/Controllers/CollectionsController.cs index 72e5683216..68d1457eb7 100644 --- a/src/Api/Controllers/CollectionsController.cs +++ b/src/Api/Controllers/CollectionsController.cs @@ -46,7 +46,7 @@ namespace Bit.Api.Controllers } [HttpGet("{id}/details")] - public async Task GetDetails(string orgId, string id) + public async Task GetDetails(string orgId, string id) { var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(new Guid(id)); if(collectionDetails?.Item1 == null || !_currentContext.OrganizationAdmin(collectionDetails.Item1.OrganizationId)) @@ -54,7 +54,7 @@ namespace Bit.Api.Controllers throw new NotFoundException(); } - return new CollectionDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2); + return new CollectionGroupDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2); } [HttpGet("")] @@ -72,12 +72,19 @@ namespace Bit.Api.Controllers } [HttpGet("~/collections")] - public async Task> GetUser([FromQuery]bool writeOnly = false) + public async Task> GetUser([FromQuery]bool writeOnly = false) { var collections = await _collectionRepository.GetManyByUserIdAsync( - _userService.GetProperUserId(User).Value, writeOnly); - var responses = collections.Select(c => new CollectionResponseModel(c)); - return new ListResponseModel(responses); + _userService.GetProperUserId(User).Value); + + // TODO: Deprecated. writeOnly flag can be removed after v1.21.0 + if(writeOnly) + { + collections = collections.Where(c => !c.ReadOnly).ToList(); + } + + var responses = collections.Select(c => new CollectionDetailsResponseModel(c)); + return new ListResponseModel(responses); } [HttpGet("{id}/users")] diff --git a/src/Api/Controllers/SyncController.cs b/src/Api/Controllers/SyncController.cs index b043d5e557..6153cae09e 100644 --- a/src/Api/Controllers/SyncController.cs +++ b/src/Api/Controllers/SyncController.cs @@ -11,6 +11,7 @@ using Bit.Core.Exceptions; using System.Linq; using Bit.Core.Models.Table; using System.Collections.Generic; +using Bit.Core.Models.Data; namespace Bit.Api.Controllers { @@ -59,11 +60,11 @@ namespace Bit.Api.Controllers var folders = await _folderRepository.GetManyByUserIdAsync(user.Id); var ciphers = await _cipherRepository.GetManyByUserIdAsync(user.Id, hasEnabledOrgs); - IEnumerable collections = null; + IEnumerable collections = null; IDictionary> collectionCiphersGroupDict = null; if(hasEnabledOrgs) { - collections = await _collectionRepository.GetManyByUserIdAsync(user.Id, false); + collections = await _collectionRepository.GetManyByUserIdAsync(user.Id); var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(user.Id); collectionCiphersGroupDict = collectionCiphers.GroupBy(c => c.CipherId).ToDictionary(s => s.Key); } diff --git a/src/Core/Models/Api/Response/CollectionResponseModel.cs b/src/Core/Models/Api/Response/CollectionResponseModel.cs index ac95234584..6458455361 100644 --- a/src/Core/Models/Api/Response/CollectionResponseModel.cs +++ b/src/Core/Models/Api/Response/CollectionResponseModel.cs @@ -28,8 +28,19 @@ namespace Bit.Core.Models.Api public class CollectionDetailsResponseModel : CollectionResponseModel { - public CollectionDetailsResponseModel(Collection collection, IEnumerable groups) - : base(collection, "collectionDetails") + public CollectionDetailsResponseModel(CollectionDetails collectionDetails) + : base(collectionDetails, "collectionDetails") + { + ReadOnly = collectionDetails.ReadOnly; + } + + public bool ReadOnly { get; set; } + } + + public class CollectionGroupDetailsResponseModel : CollectionResponseModel + { + public CollectionGroupDetailsResponseModel(Collection collection, IEnumerable groups) + : base(collection, "collectionGroupDetails") { Groups = groups.Select(g => new SelectionReadOnlyResponseModel(g)); } diff --git a/src/Core/Models/Api/Response/SyncResponseModel.cs b/src/Core/Models/Api/Response/SyncResponseModel.cs index 069a26a09b..6bd3103ed8 100644 --- a/src/Core/Models/Api/Response/SyncResponseModel.cs +++ b/src/Core/Models/Api/Response/SyncResponseModel.cs @@ -14,7 +14,7 @@ namespace Bit.Core.Models.Api User user, IEnumerable organizationUserDetails, IEnumerable folders, - IEnumerable collections, + IEnumerable collections, IEnumerable ciphers, IDictionary> collectionCiphersDict) : base("sync") @@ -22,13 +22,14 @@ namespace Bit.Core.Models.Api Profile = new ProfileResponseModel(user, organizationUserDetails); Folders = folders.Select(f => new FolderResponseModel(f)); Ciphers = ciphers.Select(c => new CipherDetailsResponseModel(c, globalSettings, collectionCiphersDict)); - Collections = collections?.Select(c => new CollectionResponseModel(c)) ?? new List(); + Collections = collections?.Select( + c => new CollectionDetailsResponseModel(c)) ?? new List(); Domains = new DomainsResponseModel(user, false); } public ProfileResponseModel Profile { get; set; } public IEnumerable Folders { get; set; } - public IEnumerable Collections { get; set; } + public IEnumerable Collections { get; set; } public IEnumerable Ciphers { get; set; } public DomainsResponseModel Domains { get; set; } } diff --git a/src/Core/Models/Data/CollectionDetails.cs b/src/Core/Models/Data/CollectionDetails.cs new file mode 100644 index 0000000000..54325135ae --- /dev/null +++ b/src/Core/Models/Data/CollectionDetails.cs @@ -0,0 +1,9 @@ +using Bit.Core.Models.Table; + +namespace Bit.Core.Models.Data +{ + public class CollectionDetails : Collection + { + public bool ReadOnly { get; set; } + } +} diff --git a/src/Core/Repositories/ICollectionRepository.cs b/src/Core/Repositories/ICollectionRepository.cs index c18161cf34..4e00d21803 100644 --- a/src/Core/Repositories/ICollectionRepository.cs +++ b/src/Core/Repositories/ICollectionRepository.cs @@ -11,7 +11,7 @@ namespace Bit.Core.Repositories Task GetCountByOrganizationIdAsync(Guid organizationId); Task>> GetByIdWithGroupsAsync(Guid id); Task> GetManyByOrganizationIdAsync(Guid organizationId); - Task> GetManyByUserIdAsync(Guid userId, bool writeOnly); + Task> GetManyByUserIdAsync(Guid userId); Task> GetManyUserDetailsByIdAsync(Guid organizationId, Guid collectionId); Task CreateAsync(Collection obj, IEnumerable groups); Task ReplaceAsync(Collection obj, IEnumerable groups); diff --git a/src/Core/Repositories/SqlServer/CollectionRepository.cs b/src/Core/Repositories/SqlServer/CollectionRepository.cs index e859bb7f55..d4b5eb4422 100644 --- a/src/Core/Repositories/SqlServer/CollectionRepository.cs +++ b/src/Core/Repositories/SqlServer/CollectionRepository.cs @@ -64,13 +64,13 @@ namespace Bit.Core.Repositories.SqlServer } } - public async Task> GetManyByUserIdAsync(Guid userId, bool writeOnly) + public async Task> GetManyByUserIdAsync(Guid userId) { using(var connection = new SqlConnection(ConnectionString)) { - var results = await connection.QueryAsync( + var results = await connection.QueryAsync( $"[{Schema}].[Collection_ReadByUserId]", - new { UserId = userId, WriteOnly = writeOnly }, + new { UserId = userId }, commandType: CommandType.StoredProcedure); // Return distinct Id results. diff --git a/src/Sql/dbo/Stored Procedures/Collection_ReadByUserId.sql b/src/Sql/dbo/Stored Procedures/Collection_ReadByUserId.sql index 2b89ee2907..2d082ab2d8 100644 --- a/src/Sql/dbo/Stored Procedures/Collection_ReadByUserId.sql +++ b/src/Sql/dbo/Stored Procedures/Collection_ReadByUserId.sql @@ -1,12 +1,20 @@ CREATE PROCEDURE [dbo].[Collection_ReadByUserId] - @UserId UNIQUEIDENTIFIER, - @WriteOnly BIT + @UserId UNIQUEIDENTIFIER AS BEGIN SET NOCOUNT ON SELECT - C.* + C.*, + CASE + WHEN + OU.[AccessAll] = 1 + OR G.[AccessAll] = 1 + OR CU.[ReadOnly] = 0 + OR CG.[ReadOnly] = 0 + THEN 1 + ELSE 0 + END [ReadOnly] FROM [dbo].[CollectionView] C INNER JOIN @@ -31,11 +39,4 @@ BEGIN OR G.[AccessAll] = 1 OR CG.[CollectionId] IS NOT NULL ) - AND ( - @WriteOnly = 0 - OR OU.[AccessAll] = 1 - OR G.[AccessAll] = 1 - OR CU.[ReadOnly] = 0 - OR CG.[ReadOnly] = 0 - ) END \ No newline at end of file diff --git a/util/Setup/DbScripts/2018-06-11_00_WebVaultUpdates.sql b/util/Setup/DbScripts/2018-06-11_00_WebVaultUpdates.sql new file mode 100644 index 0000000000..720d1cf70a --- /dev/null +++ b/util/Setup/DbScripts/2018-06-11_00_WebVaultUpdates.sql @@ -0,0 +1,49 @@ +IF OBJECT_ID('[dbo].[Collection_ReadByUserId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Collection_ReadByUserId] +END +GO + +CREATE PROCEDURE [dbo].[Collection_ReadByUserId] + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + C.*, + CASE + WHEN + OU.[AccessAll] = 1 + OR G.[AccessAll] = 1 + OR CU.[ReadOnly] = 0 + OR CG.[ReadOnly] = 0 + THEN 1 + ELSE 0 + END [ReadOnly] + FROM + [dbo].[CollectionView] C + INNER JOIN + [dbo].[OrganizationUser] OU ON C.[OrganizationId] = OU.[OrganizationId] + INNER JOIN + [dbo].[Organization] O ON O.[Id] = C.[OrganizationId] + LEFT JOIN + [dbo].[CollectionUser] CU ON OU.[AccessAll] = 0 AND CU.[CollectionId] = C.[Id] AND CU.[OrganizationUserId] = [OU].[Id] + LEFT JOIN + [dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND OU.[AccessAll] = 0 AND GU.[OrganizationUserId] = OU.[Id] + LEFT JOIN + [dbo].[Group] G ON G.[Id] = GU.[GroupId] + LEFT JOIN + [dbo].[CollectionGroup] CG ON G.[AccessAll] = 0 AND CG.[CollectionId] = C.[Id] AND CG.[GroupId] = GU.[GroupId] + WHERE + OU.[UserId] = @UserId + AND OU.[Status] = 2 -- 2 = Confirmed + AND O.[Enabled] = 1 + AND ( + OU.[AccessAll] = 1 + OR CU.[CollectionId] IS NOT NULL + OR G.[AccessAll] = 1 + OR CG.[CollectionId] IS NOT NULL + ) +END +GO diff --git a/util/Setup/Setup.csproj b/util/Setup/Setup.csproj index 03e3d3380b..e7fc950459 100644 --- a/util/Setup/Setup.csproj +++ b/util/Setup/Setup.csproj @@ -10,9 +10,11 @@ + +