diff --git a/src/Api/Controllers/CiphersController.cs b/src/Api/Controllers/CiphersController.cs index 19f5d78957..5a2f1a380f 100644 --- a/src/Api/Controllers/CiphersController.cs +++ b/src/Api/Controllers/CiphersController.cs @@ -241,5 +241,21 @@ namespace Bit.Api.Controllers await _cipherService.DeleteAsync(cipher, userId, true); } + + [HttpDelete("")] + [HttpPost("delete")] + public async Task DeleteMany([FromBody]CipherBulkDeleteRequestModel model) + { + var userId = _userService.GetProperUserId(User).Value; + await _cipherService.DeleteManyAsync(model.Ids.Select(i => new Guid(i)), userId); + } + + [HttpPut("move")] + [HttpPost("move")] + public async Task MoveMany([FromBody]CipherBulkMoveRequestModel model) + { + var userId = _userService.GetProperUserId(User).Value; + await _cipherService.MoveManyAsync(model.Ids.Select(i => new Guid(i)), new Guid(model.FolderId), userId); + } } } diff --git a/src/Core/Models/Api/Request/CipherRequestModel.cs b/src/Core/Models/Api/Request/CipherRequestModel.cs index 5e7ee966d6..10433277d1 100644 --- a/src/Core/Models/Api/Request/CipherRequestModel.cs +++ b/src/Core/Models/Api/Request/CipherRequestModel.cs @@ -89,4 +89,18 @@ namespace Bit.Core.Models.Api [Required] public IEnumerable CollectionIds { get; set; } } + + public class CipherBulkDeleteRequestModel + { + [Required] + public IEnumerable Ids { get; set; } + } + + public class CipherBulkMoveRequestModel + { + [Required] + public IEnumerable Ids { get; set; } + [Required] + public string FolderId { get; set; } + } } diff --git a/src/Core/Repositories/ICipherRepository.cs b/src/Core/Repositories/ICipherRepository.cs index cf6f67cc6d..c7e93c3f8c 100644 --- a/src/Core/Repositories/ICipherRepository.cs +++ b/src/Core/Repositories/ICipherRepository.cs @@ -19,6 +19,8 @@ namespace Bit.Core.Repositories Task UpsertAsync(CipherDetails cipher); Task ReplaceAsync(Cipher obj, IEnumerable collectionIds); Task UpdatePartialAsync(Guid id, Guid userId, Guid? folderId, bool favorite); + Task DeleteAsync(IEnumerable ids, Guid userId); + Task MoveAsync(IEnumerable ids, Guid folderId, Guid userId); Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable ciphers, IEnumerable folders); Task CreateAsync(IEnumerable ciphers, IEnumerable folders); } diff --git a/src/Core/Repositories/SqlServer/CipherRepository.cs b/src/Core/Repositories/SqlServer/CipherRepository.cs index 6a08b1990d..84254eb514 100644 --- a/src/Core/Repositories/SqlServer/CipherRepository.cs +++ b/src/Core/Repositories/SqlServer/CipherRepository.cs @@ -176,6 +176,28 @@ namespace Bit.Core.Repositories.SqlServer } } + public async Task DeleteAsync(IEnumerable ids, Guid userId) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.ExecuteAsync( + $"[{Schema}].[Cipher_Delete]", + new { Ids = ids.ToGuidIdArrayTVP(), UserId = userId }, + commandType: CommandType.StoredProcedure); + } + } + + public async Task MoveAsync(IEnumerable ids, Guid folderId, Guid userId) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.ExecuteAsync( + $"[{Schema}].[Cipher_Move]", + new { Ids = ids.ToGuidIdArrayTVP(), FolderId = folderId, UserId = userId }, + commandType: CommandType.StoredProcedure); + } + } + public Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable ciphers, IEnumerable folders) { using(var connection = new SqlConnection(ConnectionString)) diff --git a/src/Core/Services/ICipherService.cs b/src/Core/Services/ICipherService.cs index bdfc896fb5..37cd1aa226 100644 --- a/src/Core/Services/ICipherService.cs +++ b/src/Core/Services/ICipherService.cs @@ -11,6 +11,8 @@ namespace Bit.Core.Services Task SaveAsync(Cipher cipher, Guid savingUserId, bool orgAdmin = false); Task SaveDetailsAsync(CipherDetails cipher, Guid savingUserId); Task DeleteAsync(Cipher cipher, Guid deletingUserId, bool orgAdmin = false); + Task DeleteManyAsync(IEnumerable cipherIds, Guid deletingUserId); + Task MoveManyAsync(IEnumerable cipherIds, Guid destinationFolderId, Guid movingUserId); Task SaveFolderAsync(Folder folder); Task DeleteFolderAsync(Folder folder); Task ShareAsync(Cipher cipher, Guid organizationId, IEnumerable collectionIds, Guid userId); diff --git a/src/Core/Services/Implementations/CipherService.cs b/src/Core/Services/Implementations/CipherService.cs index dfebf69e94..abe35d3717 100644 --- a/src/Core/Services/Implementations/CipherService.cs +++ b/src/Core/Services/Implementations/CipherService.cs @@ -99,6 +99,20 @@ namespace Bit.Core.Services await _pushService.PushSyncCipherDeleteAsync(cipher); } + public async Task DeleteManyAsync(IEnumerable cipherIds, Guid deletingUserId) + { + await _cipherRepository.DeleteAsync(cipherIds, deletingUserId); + // push + await _pushService.PushSyncCiphersAsync(deletingUserId); + } + + public async Task MoveManyAsync(IEnumerable cipherIds, Guid destinationFolderId, Guid movingUserId) + { + await _cipherRepository.MoveAsync(cipherIds, destinationFolderId, movingUserId); + // push + await _pushService.PushSyncCiphersAsync(movingUserId); + } + public async Task SaveFolderAsync(Folder folder) { if(folder.Id == default(Guid))