diff --git a/src/Api/Controllers/CollectionsController.cs b/src/Api/Controllers/CollectionsController.cs index e30e5ba39f..f60925d3f7 100644 --- a/src/Api/Controllers/CollectionsController.cs +++ b/src/Api/Controllers/CollectionsController.cs @@ -96,12 +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.GetManyUsersByIdAsync(collection.Id); var responses = collectionUsers.Select(cu => new SelectionReadOnlyResponseModel(cu)); - return new ListResponseModel(responses); + return responses; } [HttpPost("")] diff --git a/src/Api/Controllers/GroupsController.cs b/src/Api/Controllers/GroupsController.cs index 9a9b259223..a78209cef1 100644 --- a/src/Api/Controllers/GroupsController.cs +++ b/src/Api/Controllers/GroupsController.cs @@ -8,6 +8,7 @@ using Bit.Core.Models.Api; using Bit.Core.Exceptions; using Bit.Core.Services; using Bit.Core; +using System.Collections.Generic; namespace Bit.Api.Controllers { @@ -68,7 +69,7 @@ namespace Bit.Api.Controllers } [HttpGet("{id}/users")] - public async Task> GetUsers(string orgId, string id) + public async Task> GetUsers(string orgId, string id) { var idGuid = new Guid(id); var group = await _groupRepository.GetByIdAsync(idGuid); @@ -77,9 +78,8 @@ namespace Bit.Api.Controllers throw new NotFoundException(); } - var groups = await _groupRepository.GetManyUserDetailsByIdAsync(idGuid); - var responses = groups.Select(g => new GroupUserResponseModel(g)); - return new ListResponseModel(responses); + var groupIds = await _groupRepository.GetManyUserIdsByIdAsync(idGuid); + return groupIds; } [HttpPost("")] @@ -110,6 +110,17 @@ namespace Bit.Api.Controllers return new GroupResponseModel(group); } + [HttpPut("{id}/users")] + public async Task PutUsers(string orgId, string id, [FromBody]IEnumerable model) + { + var group = await _groupRepository.GetByIdAsync(new Guid(id)); + if(group == null || !_currentContext.OrganizationAdmin(group.OrganizationId)) + { + throw new NotFoundException(); + } + await _groupRepository.UpdateUsersAsync(group.Id, model); + } + [HttpDelete("{id}")] [HttpPost("{id}/delete")] public async Task Delete(string orgId, string id) diff --git a/src/Core/Models/Api/Response/GroupUserResponseModel.cs b/src/Core/Models/Api/Response/GroupUserResponseModel.cs deleted file mode 100644 index 1f118cd4cd..0000000000 --- a/src/Core/Models/Api/Response/GroupUserResponseModel.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Bit.Core.Models.Data; -using Bit.Core.Enums; - -namespace Bit.Core.Models.Api -{ - public class GroupUserResponseModel : ResponseModel - { - public GroupUserResponseModel(GroupUserDetails groupUser) - : base("groupUser") - { - if(groupUser == null) - { - throw new ArgumentNullException(nameof(groupUser)); - } - - OrganizationUserId = groupUser.OrganizationUserId.ToString(); - AccessAll = groupUser.AccessAll; - Name = groupUser.Name; - Email = groupUser.Email; - Type = groupUser.Type; - Status = groupUser.Status; - } - - 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; } - } -} diff --git a/src/Core/Models/Api/Response/SelectionReadOnlyResponseModel.cs b/src/Core/Models/Api/Response/SelectionReadOnlyResponseModel.cs index 218e167ae4..dce5ffd456 100644 --- a/src/Core/Models/Api/Response/SelectionReadOnlyResponseModel.cs +++ b/src/Core/Models/Api/Response/SelectionReadOnlyResponseModel.cs @@ -3,10 +3,9 @@ using Bit.Core.Models.Data; namespace Bit.Core.Models.Api { - public class SelectionReadOnlyResponseModel : ResponseModel + public class SelectionReadOnlyResponseModel { public SelectionReadOnlyResponseModel(SelectionReadOnly selection) - : base("selection") { if(selection == null) { diff --git a/src/Core/Models/Data/GroupUserDetails.cs b/src/Core/Models/Data/GroupUserDetails.cs deleted file mode 100644 index a719e352c3..0000000000 --- a/src/Core/Models/Data/GroupUserDetails.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Bit.Core.Models.Data -{ - public class GroupUserDetails - { - 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; } - } -} diff --git a/src/Core/Repositories/IGroupRepository.cs b/src/Core/Repositories/IGroupRepository.cs index 0f4199e960..4d3302f266 100644 --- a/src/Core/Repositories/IGroupRepository.cs +++ b/src/Core/Repositories/IGroupRepository.cs @@ -10,8 +10,8 @@ namespace Bit.Core.Repositories { Task>> GetByIdWithCollectionsAsync(Guid id); Task> GetManyByOrganizationIdAsync(Guid organizationId); - Task> GetManyUserDetailsByIdAsync(Guid id); Task> GetManyIdsByUserIdAsync(Guid organizationUserId); + Task> GetManyUserIdsByIdAsync(Guid id); Task> GetManyGroupUsersByOrganizationIdAsync(Guid organizationId); Task CreateAsync(Group obj, IEnumerable collections); Task ReplaceAsync(Group obj, IEnumerable collections); diff --git a/src/Core/Repositories/SqlServer/CollectionRepository.cs b/src/Core/Repositories/SqlServer/CollectionRepository.cs index 52997a0488..3773c2abb7 100644 --- a/src/Core/Repositories/SqlServer/CollectionRepository.cs +++ b/src/Core/Repositories/SqlServer/CollectionRepository.cs @@ -168,7 +168,7 @@ namespace Bit.Core.Repositories.SqlServer { var results = await connection.ExecuteAsync( $"[{Schema}].[CollectionUser_UpdateUsers]", - new { Id = id, Users = users.ToArrayTVP() }, + new { CollectionId = id, Users = users.ToArrayTVP() }, commandType: CommandType.StoredProcedure); } } diff --git a/src/Core/Repositories/SqlServer/GroupRepository.cs b/src/Core/Repositories/SqlServer/GroupRepository.cs index 5c9ab0a20b..4639be33c1 100644 --- a/src/Core/Repositories/SqlServer/GroupRepository.cs +++ b/src/Core/Repositories/SqlServer/GroupRepository.cs @@ -51,19 +51,6 @@ namespace Bit.Core.Repositories.SqlServer } } - public async Task> GetManyUserDetailsByIdAsync(Guid id) - { - using(var connection = new SqlConnection(ConnectionString)) - { - var results = await connection.QueryAsync( - $"[{Schema}].[GroupUserDetails_ReadByGroupId]", - new { GroupId = id }, - commandType: CommandType.StoredProcedure); - - return results.ToList(); - } - } - public async Task> GetManyIdsByUserIdAsync(Guid organizationUserId) { using(var connection = new SqlConnection(ConnectionString)) @@ -77,6 +64,19 @@ namespace Bit.Core.Repositories.SqlServer } } + public async Task> GetManyUserIdsByIdAsync(Guid id) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{Schema}].[GroupUser_ReadOrganizationUserIdsByGroupId]", + new { GroupId = id }, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } + public async Task> GetManyGroupUsersByOrganizationIdAsync(Guid organizationId) { using(var connection = new SqlConnection(ConnectionString)) diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index 00a195354e..fe9d6dedf1 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -174,7 +174,6 @@ - @@ -237,5 +236,6 @@ + \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/CollectionUser_UpdateUsers.sql b/src/Sql/dbo/Stored Procedures/CollectionUser_UpdateUsers.sql index 4761bc3f7e..f27e4110ef 100644 --- a/src/Sql/dbo/Stored Procedures/CollectionUser_UpdateUsers.sql +++ b/src/Sql/dbo/Stored Procedures/CollectionUser_UpdateUsers.sql @@ -1,11 +1,18 @@ CREATE PROCEDURE [dbo].[CollectionUser_UpdateUsers] - @Id UNIQUEIDENTIFIER, + @CollectionId UNIQUEIDENTIFIER, @Users AS [dbo].[SelectionReadOnlyArray] READONLY AS BEGIN SET NOCOUNT ON - DECLARE @OrganizationId UNIQUEIDENTIFIER = (SELECT [OrganizationId] FROM [dbo].[Collection] WHERE [Id] = @Id) + DECLARE @OrgId UNIQUEIDENTIFIER = ( + SELECT TOP 1 + [OrganizationId] + FROM + [dbo].[Collection] + WHERE + [Id] = @CollectionId + ) ;WITH [AvailableUsersCTE] AS( SELECT @@ -13,29 +20,29 @@ BEGIN FROM [dbo].[OrganizationUser] WHERE - OrganizationId = @OrganizationId + OrganizationId = @OrgId ) MERGE [dbo].[CollectionUser] AS [Target] USING @Users AS [Source] ON - [Target].[CollectionId] = @Id + [Target].[CollectionId] = @CollectionId AND [Target].[OrganizationUserId] = [Source].[Id] WHEN NOT MATCHED BY TARGET AND [Source].[Id] IN (SELECT [Id] FROM [AvailableUsersCTE]) THEN INSERT VALUES ( - @Id, + @CollectionId, [Source].[Id], [Source].[ReadOnly] ) WHEN MATCHED AND [Target].[ReadOnly] != [Source].[ReadOnly] THEN UPDATE SET [Target].[ReadOnly] = [Source].[ReadOnly] WHEN NOT MATCHED BY SOURCE - AND [Target].[CollectionId] = @Id THEN + AND [Target].[CollectionId] = @CollectionId THEN DELETE ; - EXEC [dbo].[User_BumpAccountRevisionDateByCollectionId] @Id, @OrganizationId + EXEC [dbo].[User_BumpAccountRevisionDateByCollectionId] @CollectionId, @OrgId END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/GroupUserDetails_ReadByGroupId.sql b/src/Sql/dbo/Stored Procedures/GroupUserDetails_ReadByGroupId.sql deleted file mode 100644 index 89d3cdfc5e..0000000000 --- a/src/Sql/dbo/Stored Procedures/GroupUserDetails_ReadByGroupId.sql +++ /dev/null @@ -1,22 +0,0 @@ -CREATE PROCEDURE [dbo].[GroupUserDetails_ReadByGroupId] - @GroupId UNIQUEIDENTIFIER -AS -BEGIN - SET NOCOUNT ON - - SELECT - OU.[Id] AS [OrganizationUserId], - OU.[AccessAll], - U.[Name], - ISNULL(U.[Email], OU.[Email]) Email, - OU.[Status], - OU.[Type] - FROM - [dbo].[OrganizationUser] OU - INNER JOIN - [dbo].[GroupUser] GU ON GU.[OrganizationUserId] = OU.[Id] - LEFT JOIN - [dbo].[User] U ON U.[Id] = OU.[UserId] - WHERE - GU.[GroupId] = @GroupId -END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/GroupUser_ReadOrganizationUserIdsByGroupId.sql b/src/Sql/dbo/Stored Procedures/GroupUser_ReadOrganizationUserIdsByGroupId.sql new file mode 100644 index 0000000000..c37b48de7d --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/GroupUser_ReadOrganizationUserIdsByGroupId.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[GroupUser_ReadOrganizationUserIdsByGroupId] + @GroupId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + [OrganizationUserId] + FROM + [dbo].[GroupUser] + WHERE + [GroupId] = @GroupId +END \ No newline at end of file diff --git a/util/Setup/DbScripts/2018-10-17_00_ManagerRole.sql b/util/Setup/DbScripts/2018-10-17_00_ManagerRole.sql index 7e8dd496db..875ba72b02 100644 --- a/util/Setup/DbScripts/2018-10-17_00_ManagerRole.sql +++ b/util/Setup/DbScripts/2018-10-17_00_ManagerRole.sql @@ -124,13 +124,20 @@ END GO CREATE PROCEDURE [dbo].[CollectionUser_UpdateUsers] - @Id UNIQUEIDENTIFIER, + @CollectionId UNIQUEIDENTIFIER, @Users AS [dbo].[SelectionReadOnlyArray] READONLY AS BEGIN SET NOCOUNT ON - DECLARE @OrganizationId UNIQUEIDENTIFIER = (SELECT [OrganizationId] FROM [dbo].[Collection] WHERE [Id] = @Id) + DECLARE @OrgId UNIQUEIDENTIFIER = ( + SELECT TOP 1 + [OrganizationId] + FROM + [dbo].[Collection] + WHERE + [Id] = @CollectionId + ) ;WITH [AvailableUsersCTE] AS( SELECT @@ -138,31 +145,31 @@ BEGIN FROM [dbo].[OrganizationUser] WHERE - OrganizationId = @OrganizationId + OrganizationId = @OrgId ) MERGE [dbo].[CollectionUser] AS [Target] USING @Users AS [Source] ON - [Target].[CollectionId] = @Id + [Target].[CollectionId] = @CollectionId AND [Target].[OrganizationUserId] = [Source].[Id] WHEN NOT MATCHED BY TARGET AND [Source].[Id] IN (SELECT [Id] FROM [AvailableUsersCTE]) THEN INSERT VALUES ( - @Id, + @CollectionId, [Source].[Id], [Source].[ReadOnly] ) WHEN MATCHED AND [Target].[ReadOnly] != [Source].[ReadOnly] THEN UPDATE SET [Target].[ReadOnly] = [Source].[ReadOnly] WHEN NOT MATCHED BY SOURCE - AND [Target].[CollectionId] = @Id THEN + AND [Target].[CollectionId] = @CollectionId THEN DELETE ; - EXEC [dbo].[User_BumpAccountRevisionDateByCollectionId] @Id, @OrganizationId + EXEC [dbo].[User_BumpAccountRevisionDateByCollectionId] @CollectionId, @OrgId END GO @@ -193,3 +200,30 @@ BEGIN [CollectionId] = @CollectionId END GO + +IF OBJECT_ID('[dbo].[GroupUserDetails_ReadByGroupId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[GroupUserDetails_ReadByGroupId] +END +GO + +IF OBJECT_ID('[dbo].[GroupUser_ReadOrganizationUserIdsByGroupId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[GroupUser_ReadOrganizationUserIdsByGroupId] +END +GO + +CREATE PROCEDURE [dbo].[GroupUser_ReadOrganizationUserIdsByGroupId] + @GroupId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + [OrganizationUserId] + FROM + [dbo].[GroupUser] + WHERE + [GroupId] = @GroupId +END +GO