1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 08:02:49 -05:00

Add cipher response to restore (#1072)

* Return revised ciphers on restore api call

* Return restored date from restore sproc

* Test Restore updates passed in ciphers

This is necessary for CipherController to appropriately return the
up-to-date ciphers without an extra db call to read them.

* Add missing SELECT
This commit is contained in:
Matt Gibson
2021-01-08 08:52:42 -06:00
committed by GitHub
parent 6143ad2b95
commit 5aba9f7549
9 changed files with 146 additions and 18 deletions

View File

@ -437,7 +437,7 @@ namespace Bit.Api.Controllers
}
[HttpPut("{id}/restore")]
public async Task PutRestore(string id)
public async Task<CipherResponseModel> PutRestore(string id)
{
var userId = _userService.GetProperUserId(User).Value;
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
@ -447,13 +447,14 @@ namespace Bit.Api.Controllers
}
await _cipherService.RestoreAsync(cipher, userId);
return new CipherResponseModel(cipher, _globalSettings);
}
[HttpPut("{id}/restore-admin")]
public async Task PutRestoreAdmin(string id)
public async Task<CipherMiniResponseModel> PutRestoreAdmin(string id)
{
var userId = _userService.GetProperUserId(User).Value;
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(new Guid(id));
if (cipher == null || !cipher.OrganizationId.HasValue ||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
{
@ -461,10 +462,11 @@ namespace Bit.Api.Controllers
}
await _cipherService.RestoreAsync(cipher, userId, true);
return new CipherMiniResponseModel(cipher, _globalSettings, cipher.OrganizationUseTotp);
}
[HttpPut("restore")]
public async Task PutRestoreMany([FromBody]CipherBulkRestoreRequestModel model)
public async Task<ListResponseModel<CipherResponseModel>> PutRestoreMany([FromBody] CipherBulkRestoreRequestModel model)
{
if (!_globalSettings.SelfHosted && model.Ids.Count() > 500)
{
@ -472,7 +474,14 @@ namespace Bit.Api.Controllers
}
var userId = _userService.GetProperUserId(User).Value;
await _cipherService.RestoreManyAsync(model.Ids.Select(i => new Guid(i)), userId);
var cipherIdsToRestore = new HashSet<Guid>(model.Ids.Select(i => new Guid(i)));
var ciphers = await _cipherRepository.GetManyByUserIdAsync(userId);
var restoringCiphers = ciphers.Where(c => cipherIdsToRestore.Contains(c.Id) && c.Edit);
await _cipherService.RestoreManyAsync(restoringCiphers, userId);
var responses = restoringCiphers.Select(c => new CipherResponseModel(c, _globalSettings));
return new ListResponseModel<CipherResponseModel>(responses);
}
[HttpPut("move")]

3
src/Core/AssemblyInfo.cs Normal file
View File

@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Core.Test")]

View File

@ -35,6 +35,6 @@ namespace Bit.Core.Repositories
IEnumerable<CollectionCipher> collectionCiphers);
Task SoftDeleteAsync(IEnumerable<Guid> ids, Guid userId);
Task SoftDeleteByIdsOrganizationIdAsync(IEnumerable<Guid> ids, Guid organizationId);
Task RestoreAsync(IEnumerable<Guid> ids, Guid userId);
Task<DateTime> RestoreAsync(IEnumerable<Guid> ids, Guid userId);
}
}

View File

@ -610,14 +610,16 @@ namespace Bit.Core.Repositories.SqlServer
}
}
public async Task RestoreAsync(IEnumerable<Guid> ids, Guid userId)
public async Task<DateTime> RestoreAsync(IEnumerable<Guid> ids, Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
var results = await connection.ExecuteScalarAsync<DateTime>(
$"[{Schema}].[Cipher_Restore]",
new { Ids = ids.ToGuidIdArrayTVP(), UserId = userId },
commandType: CommandType.StoredProcedure);
return results;
}
}

View File

@ -36,6 +36,6 @@ namespace Bit.Core.Services
Task SoftDeleteAsync(Cipher cipher, Guid deletingUserId, bool orgAdmin = false);
Task SoftDeleteManyAsync(IEnumerable<Guid> cipherIds, Guid deletingUserId, Guid? organizationId = null, bool orgAdmin = false);
Task RestoreAsync(Cipher cipher, Guid restoringUserId, bool orgAdmin = false);
Task RestoreManyAsync(IEnumerable<Guid> cipherIds, Guid restoringUserId);
Task RestoreManyAsync(IEnumerable<CipherDetails> ciphers, Guid restoringUserId);
}
}

View File

@ -786,16 +786,16 @@ namespace Bit.Core.Services
await _pushService.PushSyncCipherUpdateAsync(cipher, null);
}
public async Task RestoreManyAsync(IEnumerable<Guid> cipherIds, Guid restoringUserId)
public async Task RestoreManyAsync(IEnumerable<CipherDetails> ciphers, Guid restoringUserId)
{
var cipherIdsSet = new HashSet<Guid>(cipherIds);
var ciphers = await _cipherRepository.GetManyByUserIdAsync(restoringUserId);
var restoringCiphers = ciphers.Where(c => cipherIdsSet.Contains(c.Id) && c.Edit);
var revisionDate = await _cipherRepository.RestoreAsync(ciphers.Select(c => c.Id), restoringUserId);
await _cipherRepository.RestoreAsync(cipherIds, restoringUserId);
var events = restoringCiphers.Select(c =>
new Tuple<Cipher, EventType, DateTime?>(c, EventType.Cipher_Restored, null));
var events = ciphers.Select(c =>
{
c.RevisionDate = revisionDate;
c.DeletedDate = null;
return new Tuple<Cipher, EventType, DateTime?>(c, EventType.Cipher_Restored, null);
});
foreach (var eventsBatch in events.Batch(100))
{
await _eventService.LogCipherEventsAsync(eventsBatch);

View File

@ -57,4 +57,6 @@ BEGIN
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
DROP TABLE #Temp
END
SELECT @UtcNow
END