diff --git a/src/Api/Controllers/CollectionsController.cs b/src/Api/Controllers/CollectionsController.cs index dad5cf0ba..e30e5ba39 100644 --- a/src/Api/Controllers/CollectionsController.cs +++ b/src/Api/Controllers/CollectionsController.cs @@ -9,6 +9,7 @@ using Bit.Core.Exceptions; using Bit.Core.Services; using Bit.Core; using Bit.Core.Models.Table; +using System.Collections.Generic; namespace Bit.Api.Controllers { @@ -95,13 +96,12 @@ namespace Bit.Api.Controllers } [HttpGet("{id}/users")] - public async Task> GetUsers(string orgId, string id) + public async Task> GetUsers(string orgId, string id) { var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId)); - var collectionUsers = await _collectionRepository.GetManyUserDetailsByIdAsync(collection.OrganizationId, - collection.Id); - var responses = collectionUsers.Select(c => new CollectionUserResponseModel(c)); - return new ListResponseModel(responses); + var collectionUsers = await _collectionRepository.GetManyUsersByIdAsync(collection.Id); + var responses = collectionUsers.Select(cu => new SelectionReadOnlyResponseModel(cu)); + return new ListResponseModel(responses); } [HttpPost("")] @@ -129,6 +129,13 @@ namespace Bit.Api.Controllers return new CollectionResponseModel(collection); } + [HttpPut("{id}/users")] + public async Task PutUsers(string orgId, string id, [FromBody]IEnumerable model) + { + var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId)); + await _collectionRepository.UpdateUsersAsync(collection.Id, model?.Select(g => g.ToSelectionReadOnly())); + } + [HttpDelete("{id}")] [HttpPost("{id}/delete")] public async Task Delete(string orgId, string id) diff --git a/src/Core/Models/Api/Response/CollectionUserResponseModel.cs b/src/Core/Models/Api/Response/CollectionUserResponseModel.cs deleted file mode 100644 index dc96c5dcf..000000000 --- a/src/Core/Models/Api/Response/CollectionUserResponseModel.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Bit.Core.Models.Data; -using Bit.Core.Enums; - -namespace Bit.Core.Models.Api -{ - public class CollectionUserResponseModel : ResponseModel - { - public CollectionUserResponseModel(CollectionUserDetails collectionUser) - : base("collectionUser") - { - if(collectionUser == null) - { - throw new ArgumentNullException(nameof(collectionUser)); - } - - OrganizationUserId = collectionUser.OrganizationUserId.ToString(); - AccessAll = collectionUser.AccessAll; - Name = collectionUser.Name; - Email = collectionUser.Email; - Type = collectionUser.Type; - Status = collectionUser.Status; - ReadOnly = collectionUser.ReadOnly; - } - - public string OrganizationUserId { get; set; } - public bool AccessAll { get; set; } - public string Name { get; set; } - public string Email { get; set; } - public OrganizationUserType Type { get; set; } - public OrganizationUserStatusType Status { get; set; } - public bool ReadOnly { get; set; } - } -} diff --git a/src/Core/Models/Api/Response/SelectionReadOnlyResponseModel.cs b/src/Core/Models/Api/Response/SelectionReadOnlyResponseModel.cs index dce5ffd45..218e167ae 100644 --- a/src/Core/Models/Api/Response/SelectionReadOnlyResponseModel.cs +++ b/src/Core/Models/Api/Response/SelectionReadOnlyResponseModel.cs @@ -3,9 +3,10 @@ using Bit.Core.Models.Data; namespace Bit.Core.Models.Api { - public class SelectionReadOnlyResponseModel + public class SelectionReadOnlyResponseModel : ResponseModel { public SelectionReadOnlyResponseModel(SelectionReadOnly selection) + : base("selection") { if(selection == null) { diff --git a/src/Core/Models/Data/CollectionUserDetails.cs b/src/Core/Models/Data/CollectionUserDetails.cs deleted file mode 100644 index 5f81b6eea..000000000 --- a/src/Core/Models/Data/CollectionUserDetails.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Bit.Core.Models.Data -{ - public class CollectionUserDetails - { - public Guid OrganizationUserId { get; set; } - public bool AccessAll { get; set; } - public string Name { get; set; } - public string Email { get; set; } - public Enums.OrganizationUserStatusType Status { get; set; } - public Enums.OrganizationUserType Type { get; set; } - public bool ReadOnly { get; set; } - } -} diff --git a/src/Core/Repositories/ICollectionRepository.cs b/src/Core/Repositories/ICollectionRepository.cs index 69f9f2152..7ebf4d2dc 100644 --- a/src/Core/Repositories/ICollectionRepository.cs +++ b/src/Core/Repositories/ICollectionRepository.cs @@ -14,10 +14,10 @@ namespace Bit.Core.Repositories Task> GetManyByOrganizationIdAsync(Guid organizationId); Task GetByIdAsync(Guid id, Guid userId); Task> GetManyByUserIdAsync(Guid userId); - Task> GetManyUserDetailsByIdAsync(Guid organizationId, Guid collectionId); Task CreateAsync(Collection obj, IEnumerable groups); Task ReplaceAsync(Collection obj, IEnumerable groups); Task DeleteUserAsync(Guid collectionId, Guid organizationUserId); Task UpdateUsersAsync(Guid id, IEnumerable users); + Task> GetManyUsersByIdAsync(Guid id); } } diff --git a/src/Core/Repositories/SqlServer/CollectionRepository.cs b/src/Core/Repositories/SqlServer/CollectionRepository.cs index 473bee215..52997a048 100644 --- a/src/Core/Repositories/SqlServer/CollectionRepository.cs +++ b/src/Core/Repositories/SqlServer/CollectionRepository.cs @@ -111,24 +111,6 @@ namespace Bit.Core.Repositories.SqlServer } } - public async Task> GetManyUserDetailsByIdAsync(Guid organizationId, - Guid collectionId) - { - using(var connection = new SqlConnection(ConnectionString)) - { - var results = await connection.QueryAsync( - $"[{Schema}].[CollectionUserDetails_ReadByCollectionId]", - new { OrganizationId = organizationId, CollectionId = collectionId }, - commandType: CommandType.StoredProcedure); - - // Return distinct Id results. If at least one of the grouped results is not ReadOnly, that we return it. - return results - .GroupBy(c => c.OrganizationUserId) - .Select(g => g.OrderBy(og => og.ReadOnly).First()) - .ToList(); - } - } - public async Task CreateAsync(Collection obj, IEnumerable groups) { obj.SetNewId(); @@ -185,12 +167,25 @@ namespace Bit.Core.Repositories.SqlServer using(var connection = new SqlConnection(ConnectionString)) { var results = await connection.ExecuteAsync( - $"[{Schema}].[Collection_UpdateUsers]", + $"[{Schema}].[CollectionUser_UpdateUsers]", new { Id = id, Users = users.ToArrayTVP() }, commandType: CommandType.StoredProcedure); } } + public async Task> GetManyUsersByIdAsync(Guid id) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{Schema}].[CollectionUser_ReadByCollectionId]", + new { CollectionId = id }, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } + public class CollectionWithGroups : Collection { public DataTable Groups { get; set; } diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index ace7180b4..00a195354 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -160,7 +160,7 @@ - + @@ -236,6 +236,6 @@ - + \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/CollectionUserDetails_ReadByCollectionId.sql b/src/Sql/dbo/Stored Procedures/CollectionUserDetails_ReadByCollectionId.sql deleted file mode 100644 index 90bec4585..000000000 --- a/src/Sql/dbo/Stored Procedures/CollectionUserDetails_ReadByCollectionId.sql +++ /dev/null @@ -1,42 +0,0 @@ -CREATE PROCEDURE [dbo].[CollectionUserDetails_ReadByCollectionId] - @CollectionId UNIQUEIDENTIFIER, - @OrganizationId UNIQUEIDENTIFIER -AS -BEGIN - SET NOCOUNT ON - - SELECT - OU.[Id] AS [OrganizationUserId], - CASE - WHEN OU.[AccessAll] = 1 OR G.[AccessAll] = 1 THEN 1 - ELSE 0 - END [AccessAll], - U.[Name], - ISNULL(U.[Email], OU.[Email]) Email, - OU.[Status], - OU.[Type], - CASE - WHEN OU.[AccessAll] = 1 OR CU.[ReadOnly] = 0 OR G.[AccessAll] = 1 OR CG.[ReadOnly] = 0 THEN 0 - ELSE 1 - END [ReadOnly] - FROM - [dbo].[OrganizationUser] OU - LEFT JOIN - [dbo].[CollectionUser] CU ON OU.[AccessAll] = 0 AND CU.[OrganizationUserId] = OU.[Id] AND CU.[CollectionId] = @CollectionId - 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.[GroupId] = GU.[GroupId] AND CG.[CollectionId] = @CollectionId - LEFT JOIN - [dbo].[User] U ON U.[Id] = OU.[UserId] - WHERE - OU.[OrganizationId] = @OrganizationId - AND ( - CU.[CollectionId] IS NOT NULL - OR CG.[CollectionId] IS NOT NULL - OR OU.[AccessAll] = 1 - OR G.[AccessAll] = 1 - ) -END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/CollectionUser_ReadByCollectionId.sql b/src/Sql/dbo/Stored Procedures/CollectionUser_ReadByCollectionId.sql new file mode 100644 index 000000000..472ff820b --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/CollectionUser_ReadByCollectionId.sql @@ -0,0 +1,14 @@ +CREATE PROCEDURE [dbo].[CollectionUser_ReadByCollectionId] + @CollectionId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + [OrganizationUserId] [Id], + [ReadOnly] + FROM + [dbo].[CollectionUser] + WHERE + [CollectionId] = @CollectionId +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Collection_UpdateUsers.sql b/src/Sql/dbo/Stored Procedures/CollectionUser_UpdateUsers.sql similarity index 95% rename from src/Sql/dbo/Stored Procedures/Collection_UpdateUsers.sql rename to src/Sql/dbo/Stored Procedures/CollectionUser_UpdateUsers.sql index d8b2fce79..4761bc3f7 100644 --- a/src/Sql/dbo/Stored Procedures/Collection_UpdateUsers.sql +++ b/src/Sql/dbo/Stored Procedures/CollectionUser_UpdateUsers.sql @@ -1,4 +1,4 @@ -CREATE PROCEDURE [dbo].[Collection_UpdateUsers] +CREATE PROCEDURE [dbo].[CollectionUser_UpdateUsers] @Id UNIQUEIDENTIFIER, @Users AS [dbo].[SelectionReadOnlyArray] READONLY AS diff --git a/util/Setup/DbScripts/2018-10-17_00_ManagerRole.sql b/util/Setup/DbScripts/2018-10-17_00_ManagerRole.sql index 660ad1921..7e8dd496d 100644 --- a/util/Setup/DbScripts/2018-10-17_00_ManagerRole.sql +++ b/util/Setup/DbScripts/2018-10-17_00_ManagerRole.sql @@ -117,7 +117,13 @@ BEGIN END GO -CREATE PROCEDURE [dbo].[Collection_UpdateUsers] +IF OBJECT_ID('[dbo].[CollectionUser_UpdateUsers]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[CollectionUser_UpdateUsers] +END +GO + +CREATE PROCEDURE [dbo].[CollectionUser_UpdateUsers] @Id UNIQUEIDENTIFIER, @Users AS [dbo].[SelectionReadOnlyArray] READONLY AS @@ -159,3 +165,31 @@ BEGIN EXEC [dbo].[User_BumpAccountRevisionDateByCollectionId] @Id, @OrganizationId END GO + +IF OBJECT_ID('[dbo].[CollectionUserDetails_ReadByCollectionId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[CollectionUserDetails_ReadByCollectionId] +END +GO + +IF OBJECT_ID('[dbo].[CollectionUser_ReadByCollectionId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[CollectionUser_ReadByCollectionId] +END +GO + +CREATE PROCEDURE [dbo].[CollectionUser_ReadByCollectionId] + @CollectionId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + [OrganizationUserId] [Id], + [ReadOnly] + FROM + [dbo].[CollectionUser] + WHERE + [CollectionId] = @CollectionId +END +GO