1
0
mirror of https://github.com/bitwarden/server synced 2025-12-06 00:03:34 +00:00

[PM-19151] [PM-19161] Innovation/archive/server (#5672)

* Added the ArchivedDate to cipher entity and response model
* Created migration scripts for sqlserver and ef core migration to add the ArchivedDate column

---------

Co-authored-by: gbubemismith <gsmithwalter@gmail.com>
Co-authored-by: SmithThe4th <gsmith@bitwarden.com>
Co-authored-by: Shane <smelton@bitwarden.com>
Co-authored-by: cd-bitwarden <106776772+cd-bitwarden@users.noreply.github.com>
Co-authored-by: jng <jng@bitwarden.com>
This commit is contained in:
Patrick-Pimentel-Bitwarden
2025-09-12 13:24:30 -04:00
committed by GitHub
parent 18aed0bd79
commit 4e64d35f89
43 changed files with 10342 additions and 42 deletions

View File

@@ -12,9 +12,11 @@ internal class OrganizationCipher : ICustomization
{
fixture.Customize<Cipher>(composer => composer
.With(c => c.OrganizationId, OrganizationId ?? Guid.NewGuid())
.Without(c => c.ArchivedDate)
.Without(c => c.UserId));
fixture.Customize<CipherDetails>(composer => composer
.With(c => c.OrganizationId, Guid.NewGuid())
.Without(c => c.ArchivedDate)
.Without(c => c.UserId));
}
}
@@ -26,9 +28,11 @@ internal class UserCipher : ICustomization
{
fixture.Customize<Cipher>(composer => composer
.With(c => c.UserId, UserId ?? Guid.NewGuid())
.Without(c => c.ArchivedDate)
.Without(c => c.OrganizationId));
fixture.Customize<CipherDetails>(composer => composer
.With(c => c.UserId, Guid.NewGuid())
.Without(c => c.ArchivedDate)
.Without(c => c.OrganizationId));
}
}

View File

@@ -0,0 +1,49 @@
using Bit.Core.Entities;
using Bit.Core.Platform.Push;
using Bit.Core.Test.AutoFixture.CipherFixtures;
using Bit.Core.Vault.Commands;
using Bit.Core.Vault.Models.Data;
using Bit.Core.Vault.Repositories;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Vault.Commands;
[UserCipherCustomize]
[SutProviderCustomize]
public class ArchiveCiphersCommandTest
{
[Theory]
[BitAutoData(true, false, 1, 1, 1)]
[BitAutoData(false, false, 1, 0, 1)]
[BitAutoData(false, true, 1, 0, 1)]
[BitAutoData(true, true, 1, 0, 1)]
public async Task ArchiveAsync_Works(
bool isEditable, bool hasOrganizationId,
int cipherRepoCalls, int resultCountFromQuery, int pushNotificationsCalls,
SutProvider<ArchiveCiphersCommand> sutProvider, CipherDetails cipher, User user)
{
cipher.Edit = isEditable;
cipher.OrganizationId = hasOrganizationId ? Guid.NewGuid() : null;
var cipherList = new List<CipherDetails> { cipher };
sutProvider.GetDependency<ICipherRepository>()
.GetManyByUserIdAsync(user.Id).Returns(cipherList);
// Act
await sutProvider.Sut.ArchiveManyAsync([cipher.Id], user.Id);
// Assert
await sutProvider.GetDependency<ICipherRepository>().Received(cipherRepoCalls).ArchiveAsync(
Arg.Is<IEnumerable<Guid>>(ids => ids.Count() == resultCountFromQuery
&& ids.Count() >= 1
? true
: ids.All(id => cipherList.Contains(cipher))),
user.Id);
await sutProvider.GetDependency<IPushNotificationService>().Received(pushNotificationsCalls)
.PushSyncCiphersAsync(user.Id);
}
}

View File

@@ -0,0 +1,49 @@
using Bit.Core.Entities;
using Bit.Core.Platform.Push;
using Bit.Core.Test.AutoFixture.CipherFixtures;
using Bit.Core.Vault.Commands;
using Bit.Core.Vault.Models.Data;
using Bit.Core.Vault.Repositories;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Vault.Commands;
[UserCipherCustomize]
[SutProviderCustomize]
public class UnarchiveCiphersCommandTest
{
[Theory]
[BitAutoData(true, false, 1, 1, 1)]
[BitAutoData(false, false, 1, 0, 1)]
[BitAutoData(false, true, 1, 0, 1)]
[BitAutoData(true, true, 1, 1, 1)]
public async Task UnarchiveAsync_Works(
bool isEditable, bool hasOrganizationId,
int cipherRepoCalls, int resultCountFromQuery, int pushNotificationsCalls,
SutProvider<UnarchiveCiphersCommand> sutProvider, CipherDetails cipher, User user)
{
cipher.Edit = isEditable;
cipher.OrganizationId = hasOrganizationId ? Guid.NewGuid() : null;
var cipherList = new List<CipherDetails> { cipher };
sutProvider.GetDependency<ICipherRepository>()
.GetManyByUserIdAsync(user.Id).Returns(cipherList);
// Act
await sutProvider.Sut.UnarchiveManyAsync([cipher.Id], user.Id);
// Assert
await sutProvider.GetDependency<ICipherRepository>().Received(cipherRepoCalls).UnarchiveAsync(
Arg.Is<IEnumerable<Guid>>(ids => ids.Count() == resultCountFromQuery
&& ids.Count() >= 1
? true
: ids.All(id => cipherList.Contains(cipher))),
user.Id);
await sutProvider.GetDependency<IPushNotificationService>().Received(pushNotificationsCalls)
.PushSyncCiphersAsync(user.Id);
}
}

View File

@@ -1211,4 +1211,34 @@ public class CipherRepositoryTests
Assert.Null(deletedCipher2);
}
[DatabaseTheory, DatabaseData]
public async Task ArchiveAsync_Works(
ICipherRepository sutRepository,
IUserRepository userRepository)
{
var user = await userRepository.CreateAsync(new User
{
Name = "Test User",
Email = $"test+{Guid.NewGuid()}@email.com",
ApiKey = "TEST",
SecurityStamp = "stamp",
});
// Ciphers
var cipher = await sutRepository.CreateAsync(new Cipher
{
Type = CipherType.Login,
Data = "",
UserId = user.Id
});
// Act
await sutRepository.ArchiveAsync(new List<Guid> { cipher.Id }, user.Id);
// Assert
var archivedCipher = await sutRepository.GetByIdAsync(cipher.Id, user.Id);
Assert.NotNull(archivedCipher);
Assert.NotNull(archivedCipher.ArchivedDate);
}
}