From fdf7546f3340cd4df6c4350b3dfaa5cfca1e943b Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Mon, 8 May 2017 14:08:44 -0400 Subject: [PATCH] added groups apis --- src/Api/Controllers/GroupsController.cs | 99 +++++++++++++++++++ .../Models/Api/Request/GroupRequestModel.cs | 28 ++++++ .../Models/Api/Response/GroupResponseModel.cs | 25 +++++ src/Core/Models/Table/Group.cs | 19 ++++ src/Core/Repositories/IGroupRepository.cs | 12 +++ .../Repositories/SqlServer/GroupRepository.cs | 35 +++++++ .../Utilities/ServiceCollectionExtensions.cs | 1 + src/Sql/Sql.sqlproj | 6 ++ .../Collection_ReadByOrganizationId.sql | 6 +- .../Collection_ReadByUserId.sql | 8 +- .../dbo/Stored Procedures/Group_Create.sql | 27 +++++ .../Stored Procedures/Group_DeleteById.sql | 12 +++ .../dbo/Stored Procedures/Group_ReadById.sql | 13 +++ .../Group_ReadByOrganizationId.sql | 13 +++ .../dbo/Stored Procedures/Group_Update.sql | 20 ++++ src/Sql/dbo/Views/GroupView.sql | 6 ++ 16 files changed, 323 insertions(+), 7 deletions(-) create mode 100644 src/Api/Controllers/GroupsController.cs create mode 100644 src/Core/Models/Api/Request/GroupRequestModel.cs create mode 100644 src/Core/Models/Api/Response/GroupResponseModel.cs create mode 100644 src/Core/Models/Table/Group.cs create mode 100644 src/Core/Repositories/IGroupRepository.cs create mode 100644 src/Core/Repositories/SqlServer/GroupRepository.cs create mode 100644 src/Sql/dbo/Stored Procedures/Group_Create.sql create mode 100644 src/Sql/dbo/Stored Procedures/Group_DeleteById.sql create mode 100644 src/Sql/dbo/Stored Procedures/Group_ReadById.sql create mode 100644 src/Sql/dbo/Stored Procedures/Group_ReadByOrganizationId.sql create mode 100644 src/Sql/dbo/Stored Procedures/Group_Update.sql create mode 100644 src/Sql/dbo/Views/GroupView.sql diff --git a/src/Api/Controllers/GroupsController.cs b/src/Api/Controllers/GroupsController.cs new file mode 100644 index 0000000000..424ce4e80e --- /dev/null +++ b/src/Api/Controllers/GroupsController.cs @@ -0,0 +1,99 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Bit.Core.Repositories; +using Microsoft.AspNetCore.Authorization; +using Bit.Core.Models.Api; +using Bit.Core.Exceptions; +using Bit.Core.Services; +using Bit.Core; + +namespace Bit.Api.Controllers +{ + [Route("organizations/{orgId}/groups")] + [Authorize("Application")] + public class GroupsController : Controller + { + private readonly IGroupRepository _groupRepository; + private readonly IUserService _userService; + private readonly CurrentContext _currentContext; + + public GroupsController( + IGroupRepository groupRepository, + IUserService userService, + CurrentContext currentContext) + { + _groupRepository = groupRepository; + _userService = userService; + _currentContext = currentContext; + } + + [HttpGet("{id}")] + public async Task Get(string orgId, string id) + { + var group = await _groupRepository.GetByIdAsync(new Guid(id)); + if(group == null || !_currentContext.OrganizationAdmin(group.OrganizationId)) + { + throw new NotFoundException(); + } + + return new GroupResponseModel(group); + } + + [HttpGet("")] + public async Task> Get(string orgId) + { + var orgIdGuid = new Guid(orgId); + if(!_currentContext.OrganizationAdmin(orgIdGuid)) + { + throw new NotFoundException(); + } + + var groups = await _groupRepository.GetManyByOrganizationIdAsync(orgIdGuid); + var responses = groups.Select(g => new GroupResponseModel(g)); + return new ListResponseModel(responses); + } + + [HttpPost("")] + public async Task Post(string orgId, [FromBody]GroupRequestModel model) + { + var orgIdGuid = new Guid(orgId); + if(!_currentContext.OrganizationAdmin(orgIdGuid)) + { + throw new NotFoundException(); + } + + var group = model.ToGroup(orgIdGuid); + await _groupRepository.CreateAsync(group); + return new GroupResponseModel(group); + } + + [HttpPut("{id}")] + [HttpPost("{id}")] + public async Task Put(string orgId, string id, [FromBody]GroupRequestModel model) + { + var group = await _groupRepository.GetByIdAsync(new Guid(id)); + if(group == null || !_currentContext.OrganizationAdmin(group.OrganizationId)) + { + throw new NotFoundException(); + } + + await _groupRepository.ReplaceAsync(model.ToGroup(group)); + return new GroupResponseModel(group); + } + + [HttpDelete("{id}")] + [HttpPost("{id}/delete")] + public async Task Delete(string orgId, string id) + { + var group = await _groupRepository.GetByIdAsync(new Guid(id)); + if(group == null || !_currentContext.OrganizationAdmin(group.OrganizationId)) + { + throw new NotFoundException(); + } + + await _groupRepository.DeleteAsync(group); + } + } +} diff --git a/src/Core/Models/Api/Request/GroupRequestModel.cs b/src/Core/Models/Api/Request/GroupRequestModel.cs new file mode 100644 index 0000000000..c5ecfbb672 --- /dev/null +++ b/src/Core/Models/Api/Request/GroupRequestModel.cs @@ -0,0 +1,28 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Bit.Core.Models.Table; +using Newtonsoft.Json; + +namespace Bit.Core.Models.Api +{ + public class GroupRequestModel + { + [Required] + [StringLength(300)] + public string Name { get; set; } + + public Group ToGroup(Guid orgId) + { + return ToGroup(new Group + { + OrganizationId = orgId + }); + } + + public Group ToGroup(Group existingGroup) + { + existingGroup.Name = Name; + return existingGroup; + } + } +} diff --git a/src/Core/Models/Api/Response/GroupResponseModel.cs b/src/Core/Models/Api/Response/GroupResponseModel.cs new file mode 100644 index 0000000000..1b2e94ae78 --- /dev/null +++ b/src/Core/Models/Api/Response/GroupResponseModel.cs @@ -0,0 +1,25 @@ +using System; +using Bit.Core.Models.Table; + +namespace Bit.Core.Models.Api +{ + public class GroupResponseModel : ResponseModel + { + public GroupResponseModel(Group group, string obj = "group") + : base(obj) + { + if(group == null) + { + throw new ArgumentNullException(nameof(group)); + } + + Id = group.Id.ToString(); + OrganizationId = group.OrganizationId.ToString(); + Name = group.Name; + } + + public string Id { get; set; } + public string OrganizationId { get; set; } + public string Name { get; set; } + } +} diff --git a/src/Core/Models/Table/Group.cs b/src/Core/Models/Table/Group.cs new file mode 100644 index 0000000000..22d755ce05 --- /dev/null +++ b/src/Core/Models/Table/Group.cs @@ -0,0 +1,19 @@ +using System; +using Bit.Core.Utilities; + +namespace Bit.Core.Models.Table +{ + public class Group : IDataObject + { + public Guid Id { get; set; } + public Guid OrganizationId { get; set; } + public string Name { get; set; } + public DateTime CreationDate { get; internal set; } = DateTime.UtcNow; + public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow; + + public void SetNewId() + { + Id = CoreHelpers.GenerateComb(); + } + } +} diff --git a/src/Core/Repositories/IGroupRepository.cs b/src/Core/Repositories/IGroupRepository.cs new file mode 100644 index 0000000000..d23950e46d --- /dev/null +++ b/src/Core/Repositories/IGroupRepository.cs @@ -0,0 +1,12 @@ +using System; +using Bit.Core.Models.Table; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Bit.Core.Repositories +{ + public interface IGroupRepository : IRepository + { + Task> GetManyByOrganizationIdAsync(Guid organizationId); + } +} diff --git a/src/Core/Repositories/SqlServer/GroupRepository.cs b/src/Core/Repositories/SqlServer/GroupRepository.cs new file mode 100644 index 0000000000..0dbe97e0cc --- /dev/null +++ b/src/Core/Repositories/SqlServer/GroupRepository.cs @@ -0,0 +1,35 @@ +using System; +using Bit.Core.Models.Table; +using System.Collections.Generic; +using System.Threading.Tasks; +using Dapper; +using System.Data; +using System.Data.SqlClient; +using System.Linq; + +namespace Bit.Core.Repositories.SqlServer +{ + public class GroupRepository : Repository, IGroupRepository + { + public GroupRepository(GlobalSettings globalSettings) + : this(globalSettings.SqlServer.ConnectionString) + { } + + public GroupRepository(string connectionString) + : base(connectionString) + { } + + public async Task> GetManyByOrganizationIdAsync(Guid organizationId) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{Schema}].[Group_ReadByOrganizationId]", + new { OrganizationId = organizationId }, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } + } +} diff --git a/src/Core/Utilities/ServiceCollectionExtensions.cs b/src/Core/Utilities/ServiceCollectionExtensions.cs index ee2958feea..ee304a811a 100644 --- a/src/Core/Utilities/ServiceCollectionExtensions.cs +++ b/src/Core/Utilities/ServiceCollectionExtensions.cs @@ -35,6 +35,7 @@ namespace Bit.Core.Utilities services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); } public static void AddBaseServices(this IServiceCollection services) diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index f8bba0f8aa..899abb918f 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -178,5 +178,11 @@ + + + + + + \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Collection_ReadByOrganizationId.sql b/src/Sql/dbo/Stored Procedures/Collection_ReadByOrganizationId.sql index 52c0cddaa5..0d317ebded 100644 --- a/src/Sql/dbo/Stored Procedures/Collection_ReadByOrganizationId.sql +++ b/src/Sql/dbo/Stored Procedures/Collection_ReadByOrganizationId.sql @@ -5,9 +5,9 @@ BEGIN SET NOCOUNT ON SELECT - S.* + * FROM - [dbo].[CollectionView] S + [dbo].[CollectionView] WHERE - S.[OrganizationId] = @OrganizationId + [OrganizationId] = @OrganizationId END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Collection_ReadByUserId.sql b/src/Sql/dbo/Stored Procedures/Collection_ReadByUserId.sql index c0ddcb4cad..a87aced687 100644 --- a/src/Sql/dbo/Stored Procedures/Collection_ReadByUserId.sql +++ b/src/Sql/dbo/Stored Procedures/Collection_ReadByUserId.sql @@ -5,15 +5,15 @@ BEGIN SET NOCOUNT ON SELECT - S.* + C.* FROM - [dbo].[CollectionView] S + [dbo].[CollectionView] C INNER JOIN - [Organization] O ON O.[Id] = S.[OrganizationId] + [Organization] O ON O.[Id] = C.[OrganizationId] INNER JOIN [dbo].[OrganizationUser] OU ON OU.[OrganizationId] = O.[Id] AND OU.[UserId] = @UserId LEFT JOIN - [dbo].[CollectionUser] CU ON OU.[AccessAll] = 0 AND CU.[CollectionId] = S.[Id] AND CU.[OrganizationUserId] = OU.[Id] + [dbo].[CollectionUser] CU ON OU.[AccessAll] = 0 AND CU.[CollectionId] = C.[Id] AND CU.[OrganizationUserId] = OU.[Id] WHERE OU.[Status] = 2 -- Confirmed AND O.[Enabled] = 1 diff --git a/src/Sql/dbo/Stored Procedures/Group_Create.sql b/src/Sql/dbo/Stored Procedures/Group_Create.sql new file mode 100644 index 0000000000..4e4926383a --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Group_Create.sql @@ -0,0 +1,27 @@ +CREATE PROCEDURE [dbo].[Group_Create] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Name VARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[Group] + ( + [Id], + [OrganizationId], + [Name], + [CreationDate], + [RevisionDate] + ) + VALUES + ( + @Id, + @OrganizationId, + @Name, + @CreationDate, + @RevisionDate + ) +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Group_DeleteById.sql b/src/Sql/dbo/Stored Procedures/Group_DeleteById.sql new file mode 100644 index 0000000000..4ad4dcd3bb --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Group_DeleteById.sql @@ -0,0 +1,12 @@ +CREATE PROCEDURE [dbo].[Group_DeleteById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + DELETE + FROM + [dbo].[Group] + WHERE + [Id] = @Id +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Group_ReadById.sql b/src/Sql/dbo/Stored Procedures/Group_ReadById.sql new file mode 100644 index 0000000000..512b910a70 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Group_ReadById.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[Group_ReadById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[GroupView] + WHERE + [Id] = @Id +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Group_ReadByOrganizationId.sql b/src/Sql/dbo/Stored Procedures/Group_ReadByOrganizationId.sql new file mode 100644 index 0000000000..114291ae65 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Group_ReadByOrganizationId.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[Group_ReadByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[GroupView] + WHERE + [OrganizationId] = @OrganizationId +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Group_Update.sql b/src/Sql/dbo/Stored Procedures/Group_Update.sql new file mode 100644 index 0000000000..743955dd45 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Group_Update.sql @@ -0,0 +1,20 @@ +CREATE PROCEDURE [dbo].[Group_Update] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Name VARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[Group] + SET + [OrganizationId] = @OrganizationId, + [Name] = @Name, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate + WHERE + [Id] = @Id +END \ No newline at end of file diff --git a/src/Sql/dbo/Views/GroupView.sql b/src/Sql/dbo/Views/GroupView.sql new file mode 100644 index 0000000000..dbf27e3d7d --- /dev/null +++ b/src/Sql/dbo/Views/GroupView.sql @@ -0,0 +1,6 @@ +CREATE VIEW [dbo].[GroupView] +AS +SELECT + * +FROM + [dbo].[Group] \ No newline at end of file