diff --git a/src/Api/Controllers/CiphersController.cs b/src/Api/Controllers/CiphersController.cs index 47ecb536d8..186cec5b54 100644 --- a/src/Api/Controllers/CiphersController.cs +++ b/src/Api/Controllers/CiphersController.cs @@ -272,11 +272,16 @@ public class CiphersController : Controller [HttpPut("{id}/partial")] [HttpPost("{id}/partial")] - public async Task PutPartial(string id, [FromBody] CipherPartialRequestModel model) + public async Task PutPartial(string id, [FromBody] CipherPartialRequestModel model) { var userId = _userService.GetProperUserId(User).Value; var folderId = string.IsNullOrWhiteSpace(model.FolderId) ? null : (Guid?)new Guid(model.FolderId); - await _cipherRepository.UpdatePartialAsync(new Guid(id), userId, folderId, model.Favorite); + var cipherId = new Guid(id); + await _cipherRepository.UpdatePartialAsync(cipherId, userId, folderId, model.Favorite); + + var cipher = await _cipherRepository.GetByIdAsync(cipherId, userId); + var response = new CipherResponseModel(cipher, _globalSettings); + return response; } [HttpPut("{id}/share")] diff --git a/src/Infrastructure.EntityFramework/Repositories/CipherRepository.cs b/src/Infrastructure.EntityFramework/Repositories/CipherRepository.cs index 17aaedfac9..1719a161b4 100644 --- a/src/Infrastructure.EntityFramework/Repositories/CipherRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/CipherRepository.cs @@ -345,7 +345,6 @@ public class CipherRepository : Repository, var idsToMove = from ucd in userCipherDetails join c in cipherEntities on ucd.Id equals c.Id - where ucd.Edit select c; await idsToMove.ForEachAsync(cipher => { diff --git a/src/Sql/dbo/Stored Procedures/Cipher_Move.sql b/src/Sql/dbo/Stored Procedures/Cipher_Move.sql index c6b9c8ddc2..d66a1c8f3a 100644 --- a/src/Sql/dbo/Stored Procedures/Cipher_Move.sql +++ b/src/Sql/dbo/Stored Procedures/Cipher_Move.sql @@ -15,8 +15,7 @@ BEGIN FROM [dbo].[UserCipherDetails](@UserId) WHERE - [Edit] = 1 - AND [Id] IN (SELECT * FROM @Ids) + [Id] IN (SELECT * FROM @Ids) ) UPDATE [dbo].[Cipher] diff --git a/test/Api.Test/Controllers/CiphersControllerTests.cs b/test/Api.Test/Controllers/CiphersControllerTests.cs new file mode 100644 index 0000000000..8a8861a738 --- /dev/null +++ b/test/Api.Test/Controllers/CiphersControllerTests.cs @@ -0,0 +1,45 @@ +using System.Security.Claims; +using Bit.Api.Controllers; +using Bit.Api.Models.Request; +using Bit.Core.Repositories; +using Bit.Core.Services; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using Core.Models.Data; +using NSubstitute; +using Xunit; + +namespace Bit.Api.Test.Controllers; + +[ControllerCustomize(typeof(CiphersController))] +[SutProviderCustomize] +public class CiphersControllerTests +{ + [Theory, BitAutoData] + public async Task PutPartialShouldReturnCipherWithGivenFolderAndFavoriteValues(Guid userId, Guid folderId, SutProvider sutProvider) + { + var isFavorite = true; + var cipherId = Guid.NewGuid(); + + sutProvider.GetDependency() + .GetProperUserId(Arg.Any()) + .Returns(userId); + + var cipherDetails = new CipherDetails + { + Favorite = isFavorite, + FolderId = folderId, + Type = Core.Enums.CipherType.SecureNote, + Data = "{}" + }; + + sutProvider.GetDependency() + .GetByIdAsync(cipherId, userId) + .Returns(Task.FromResult(cipherDetails)); + + var result = await sutProvider.Sut.PutPartial(cipherId.ToString(), new CipherPartialRequestModel { Favorite = isFavorite, FolderId = folderId.ToString() }); + + Assert.Equal(folderId.ToString(), result.FolderId); + Assert.Equal(isFavorite, result.Favorite); + } +} diff --git a/util/Migrator/DbScripts/2022-09-08_00_CipherMovePermissions.sql b/util/Migrator/DbScripts/2022-09-08_00_CipherMovePermissions.sql new file mode 100644 index 0000000000..ca2c171ad0 --- /dev/null +++ b/util/Migrator/DbScripts/2022-09-08_00_CipherMovePermissions.sql @@ -0,0 +1,39 @@ +-- Remove check for Edit permission. User should be able to move the cipher to a different folder even if they don't have Edit permissions + +ALTER PROCEDURE [dbo].[Cipher_Move] + @Ids AS [dbo].[GuidIdArray] READONLY, + @FolderId AS UNIQUEIDENTIFIER, + @UserId AS UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + DECLARE @UserIdKey VARCHAR(50) = CONCAT('"', @UserId, '"') + DECLARE @UserIdPath VARCHAR(50) = CONCAT('$.', @UserIdKey) + + ;WITH [IdsToMoveCTE] AS ( + SELECT + [Id] + FROM + [dbo].[UserCipherDetails](@UserId) + WHERE + [Id] IN (SELECT * FROM @Ids) + ) + UPDATE + [dbo].[Cipher] + SET + [Folders] = + CASE + WHEN @FolderId IS NOT NULL AND [Folders] IS NULL THEN + CONCAT('{', @UserIdKey, ':"', @FolderId, '"', '}') + WHEN @FolderId IS NOT NULL THEN + JSON_MODIFY([Folders], @UserIdPath, CAST(@FolderId AS VARCHAR(50))) + ELSE + JSON_MODIFY([Folders], @UserIdPath, NULL) + END + WHERE + [Id] IN (SELECT * FROM [IdsToMoveCTE]) + + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId +END +GO \ No newline at end of file