diff --git a/src/Api/Controllers/SharesController.cs b/src/Api/Controllers/SharesController.cs new file mode 100644 index 0000000000..3ea2021ca8 --- /dev/null +++ b/src/Api/Controllers/SharesController.cs @@ -0,0 +1,66 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Bit.Core.Repositories; +using Microsoft.AspNetCore.Authorization; +using Bit.Api.Models; +using Bit.Core.Exceptions; +using Bit.Core.Services; + +namespace Bit.Api.Controllers +{ + [Route("shares")] + [Authorize("Application")] + public class SharesController : Controller + { + private readonly IShareRepository _shareRepository; + private readonly IUserService _userService; + + public SharesController( + IShareRepository shareRepository, + IUserService userService) + { + _shareRepository = shareRepository; + _userService = userService; + } + + [HttpGet("{id}")] + public async Task Get(string id) + { + var userId = _userService.GetProperUserId(User).Value; + var share = await _shareRepository.GetByIdAsync(new Guid(id), userId); + if(share == null) + { + throw new NotFoundException(); + } + + return new ShareResponseModel(share); + } + + [HttpPost("")] + public async Task Post([FromBody]ShareRequestModel model) + { + var share = model.ToShare(_userService.GetProperUserId(User).Value); + await _shareRepository.CreateAsync(share); + + var response = new ShareResponseModel(share); + return response; + } + + [HttpDelete("{id}")] + [HttpPost("{id}/delete")] + public async Task Delete(string id) + { + var share = await _shareRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value); + if(share == null) + { + throw new NotFoundException(); + } + + // TODO: permission checks + + await _shareRepository.DeleteAsync(share); + } + } +} diff --git a/src/Api/Models/Request/ShareRequestModel.cs b/src/Api/Models/Request/ShareRequestModel.cs new file mode 100644 index 0000000000..6655b12c58 --- /dev/null +++ b/src/Api/Models/Request/ShareRequestModel.cs @@ -0,0 +1,35 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Bit.Core.Domains; +using Newtonsoft.Json; + +namespace Bit.Api.Models +{ + public class ShareRequestModel + { + [Required] + [StringLength(36)] + public string UserId { get; set; } + [Required] + [StringLength(36)] + public string CipherId { get; set; } + public string Key { get; set; } + + public Share ToShare(Guid sharerUserId) + { + return ToShare(new Share + { + SharerUserId = sharerUserId + }); + } + + public Share ToShare(Share existingShare) + { + existingShare.UserId = new Guid(UserId); + existingShare.CipherId = new Guid(CipherId); + existingShare.Key = Key; + + return existingShare; + } + } +} diff --git a/src/Api/Models/Response/ShareResponseModel.cs b/src/Api/Models/Response/ShareResponseModel.cs new file mode 100644 index 0000000000..452d8f5989 --- /dev/null +++ b/src/Api/Models/Response/ShareResponseModel.cs @@ -0,0 +1,36 @@ +using System; +using Bit.Core.Domains; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Bit.Api.Models +{ + public class ShareResponseModel : ResponseModel + { + public ShareResponseModel(Share share) + : base("share") + { + if(share == null) + { + throw new ArgumentNullException(nameof(share)); + } + + Id = share.Id.ToString(); + UserId = share.UserId.ToString(); + SharerUserId = share.SharerUserId.ToString(); + CipherId = share.CipherId.ToString(); + Key = Key; + Permissions = share.Permissions == null ? null : + JsonConvert.DeserializeObject>(share.Permissions); + Status = share.Status; + } + + public string Id { get; set; } + public string UserId { get; set; } + public string SharerUserId { get; set; } + public string CipherId { get; set; } + public string Key { get; set; } + public IEnumerable Permissions { get; set; } + public Core.Enums.ShareStatusType? Status { get; set; } + } +} diff --git a/src/Core/Domains/Share.cs b/src/Core/Domains/Share.cs index 040b2822eb..2f5cae7355 100644 --- a/src/Core/Domains/Share.cs +++ b/src/Core/Domains/Share.cs @@ -7,6 +7,7 @@ namespace Bit.Core.Domains { public Guid Id { get; set; } public Guid UserId { get; set; } + public Guid SharerUserId { get; set; } public Guid CipherId { get; set; } public string Key { get; set; } public string Permissions { get; set; } diff --git a/src/Core/Repositories/IShareRepository.cs b/src/Core/Repositories/IShareRepository.cs index 44b1441a81..beadfe5fa3 100644 --- a/src/Core/Repositories/IShareRepository.cs +++ b/src/Core/Repositories/IShareRepository.cs @@ -1,9 +1,11 @@ using System; using Bit.Core.Domains; +using System.Threading.Tasks; namespace Bit.Core.Repositories { public interface IShareRepository : IRepository { + Task GetByIdAsync(Guid id, Guid userId); } } diff --git a/src/Core/Repositories/SqlServer/ShareRepository.cs b/src/Core/Repositories/SqlServer/ShareRepository.cs index 79417a74e8..32c640a5b7 100644 --- a/src/Core/Repositories/SqlServer/ShareRepository.cs +++ b/src/Core/Repositories/SqlServer/ShareRepository.cs @@ -1,5 +1,6 @@ using System; using Bit.Core.Domains; +using System.Threading.Tasks; namespace Bit.Core.Repositories.SqlServer { @@ -12,5 +13,16 @@ namespace Bit.Core.Repositories.SqlServer public ShareRepository(string connectionString) : base(connectionString) { } + + public async Task GetByIdAsync(Guid id, Guid userId) + { + var share = await GetByIdAsync(id); + if(share == null || (share.UserId != userId && share.SharerUserId != userId)) + { + return null; + } + + return share; + } } } diff --git a/src/Sql/dbo/Stored Procedures/Share_Create.sql b/src/Sql/dbo/Stored Procedures/Share_Create.sql index 720e87b9ee..35981eb2b6 100644 --- a/src/Sql/dbo/Stored Procedures/Share_Create.sql +++ b/src/Sql/dbo/Stored Procedures/Share_Create.sql @@ -1,6 +1,7 @@ CREATE PROCEDURE [dbo].[Share_Create] @Id UNIQUEIDENTIFIER, @UserId UNIQUEIDENTIFIER, + @SharerUserId UNIQUEIDENTIFIER, @CipherId UNIQUEIDENTIFIER, @Key NVARCHAR(MAX), @Permissions NVARCHAR(MAX), @@ -15,6 +16,7 @@ BEGIN ( [Id], [UserId], + [SharerUserId], [CipherId], [Key], [Permissions], @@ -26,6 +28,7 @@ BEGIN ( @Id, @UserId, + @SharerUserId, @CipherId, @Key, @Permissions, diff --git a/src/Sql/dbo/Stored Procedures/Share_Update.sql b/src/Sql/dbo/Stored Procedures/Share_Update.sql index 6dd1e5c220..1369d80957 100644 --- a/src/Sql/dbo/Stored Procedures/Share_Update.sql +++ b/src/Sql/dbo/Stored Procedures/Share_Update.sql @@ -1,6 +1,7 @@ CREATE PROCEDURE [dbo].[Share_Update] @Id UNIQUEIDENTIFIER, @UserId UNIQUEIDENTIFIER, + @SharerUserId UNIQUEIDENTIFIER, @CipherId UNIQUEIDENTIFIER, @Key NVARCHAR(MAX), @Permissions NVARCHAR(MAX), @@ -15,6 +16,7 @@ BEGIN [dbo].[Share] SET [UserId] = @UserId, + [SharerUserId] = @SharerUserId, [CipherId] = @CipherId, [Key] = @Key, [Permissions] = @Permissions, diff --git a/src/Sql/dbo/Tables/Share.sql b/src/Sql/dbo/Tables/Share.sql index fb57b8a9de..d5d02a563c 100644 --- a/src/Sql/dbo/Tables/Share.sql +++ b/src/Sql/dbo/Tables/Share.sql @@ -1,6 +1,7 @@ CREATE TABLE [dbo].[Share] ( [Id] UNIQUEIDENTIFIER NOT NULL, [UserId] UNIQUEIDENTIFIER NOT NULL, + [SharerUserId] UNIQUEIDENTIFIER NOT NULL, [CipherId] UNIQUEIDENTIFIER NOT NULL, [Key] VARCHAR (MAX) NULL, [Permissions] VARCHAR (MAX) NULL, @@ -9,7 +10,8 @@ [RevisionDate] DATETIME2 (7) NOT NULL, CONSTRAINT [PK_Share] PRIMARY KEY CLUSTERED ([Id] ASC), CONSTRAINT [FK_Share_Cipher] FOREIGN KEY ([CipherId]) REFERENCES [dbo].[Cipher] ([Id]), - CONSTRAINT [FK_Share_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) + CONSTRAINT [FK_Share_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]), + CONSTRAINT [FK_Share_SharerUser] FOREIGN KEY ([SharerUserId]) REFERENCES [dbo].[User] ([Id]) );