mirror of
https://github.com/bitwarden/server.git
synced 2026-01-31 14:13:18 +08:00
[PM-30448] - remove edit requirement for cipher archiving (#6830)
* remove edit requirement for cipher archiving * update cipher_archive/unarchive sql * update cipher_archive/unarchive sql * fix sql * update sql * update sql
This commit is contained in:
@@ -801,7 +801,7 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
|
|||||||
var query = from ucd in await userCipherDetailsQuery.Run(dbContext).ToListAsync()
|
var query = from ucd in await userCipherDetailsQuery.Run(dbContext).ToListAsync()
|
||||||
join c in cipherEntitiesToCheck
|
join c in cipherEntitiesToCheck
|
||||||
on ucd.Id equals c.Id
|
on ucd.Id equals c.Id
|
||||||
where ucd.Edit && FilterArchivedDate(action, ucd)
|
where FilterArchivedDate(action, ucd)
|
||||||
select c;
|
select c;
|
||||||
|
|
||||||
var utcNow = DateTime.UtcNow;
|
var utcNow = DateTime.UtcNow;
|
||||||
|
|||||||
@@ -13,14 +13,13 @@ BEGIN
|
|||||||
|
|
||||||
INSERT INTO #Temp
|
INSERT INTO #Temp
|
||||||
SELECT
|
SELECT
|
||||||
[Id],
|
ucd.[Id],
|
||||||
[UserId]
|
ucd.[UserId]
|
||||||
FROM
|
FROM
|
||||||
[dbo].[UserCipherDetails](@UserId)
|
[dbo].[UserCipherDetails](@UserId) ucd
|
||||||
|
INNER JOIN @Ids ids ON ids.Id = ucd.[Id]
|
||||||
WHERE
|
WHERE
|
||||||
[Edit] = 1
|
ucd.[ArchivedDate] IS NULL
|
||||||
AND [ArchivedDate] IS NULL
|
|
||||||
AND [Id] IN (SELECT * FROM @Ids)
|
|
||||||
|
|
||||||
DECLARE @UtcNow DATETIME2(7) = SYSUTCDATETIME();
|
DECLARE @UtcNow DATETIME2(7) = SYSUTCDATETIME();
|
||||||
UPDATE
|
UPDATE
|
||||||
@@ -32,8 +31,9 @@ BEGIN
|
|||||||
CONVERT(NVARCHAR(30), @UtcNow, 127)
|
CONVERT(NVARCHAR(30), @UtcNow, 127)
|
||||||
),
|
),
|
||||||
[RevisionDate] = @UtcNow
|
[RevisionDate] = @UtcNow
|
||||||
WHERE
|
FROM [dbo].[Cipher] AS c
|
||||||
[Id] IN (SELECT [Id] FROM #Temp)
|
INNER JOIN #Temp AS t
|
||||||
|
ON t.[Id] = c.[Id];
|
||||||
|
|
||||||
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,13 @@ BEGIN
|
|||||||
|
|
||||||
INSERT INTO #Temp
|
INSERT INTO #Temp
|
||||||
SELECT
|
SELECT
|
||||||
[Id],
|
ucd.[Id],
|
||||||
[UserId]
|
ucd.[UserId]
|
||||||
FROM
|
FROM
|
||||||
[dbo].[UserCipherDetails](@UserId)
|
[dbo].[UserCipherDetails](@UserId) ucd
|
||||||
|
INNER JOIN @Ids ids ON ids.Id = ucd.[Id]
|
||||||
WHERE
|
WHERE
|
||||||
[Edit] = 1
|
ucd.[ArchivedDate] IS NOT NULL
|
||||||
AND [ArchivedDate] IS NOT NULL
|
|
||||||
AND [Id] IN (SELECT * FROM @Ids)
|
|
||||||
|
|
||||||
DECLARE @UtcNow DATETIME2(7) = SYSUTCDATETIME();
|
DECLARE @UtcNow DATETIME2(7) = SYSUTCDATETIME();
|
||||||
UPDATE
|
UPDATE
|
||||||
@@ -32,8 +31,9 @@ BEGIN
|
|||||||
NULL
|
NULL
|
||||||
),
|
),
|
||||||
[RevisionDate] = @UtcNow
|
[RevisionDate] = @UtcNow
|
||||||
WHERE
|
FROM [dbo].[Cipher] AS c
|
||||||
[Id] IN (SELECT [Id] FROM #Temp)
|
INNER JOIN #Temp AS t
|
||||||
|
ON t.[Id] = c.[Id];
|
||||||
|
|
||||||
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Test.AutoFixture.Attributes;
|
using Bit.Core.Test.AutoFixture.Attributes;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Bit.Core.Vault.Entities;
|
using Bit.Core.Vault.Entities;
|
||||||
using Bit.Infrastructure.EFIntegration.Test.AutoFixture;
|
using Bit.Infrastructure.EFIntegration.Test.AutoFixture;
|
||||||
using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers;
|
using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers;
|
||||||
@@ -279,4 +280,92 @@ public class CipherRepositoryTests
|
|||||||
Assert.Equal(Core.Vault.Enums.CipherRepromptType.Password, savedCipher.Reprompt);
|
Assert.Equal(Core.Vault.Enums.CipherRepromptType.Password, savedCipher.Reprompt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CiSkippedTheory, EfUserCipherCustomize, BitAutoData]
|
||||||
|
public async Task ArchiveAsync_SetsArchivesJsonAndBumpsUserAccountRevisionDate(
|
||||||
|
Cipher cipher,
|
||||||
|
User user,
|
||||||
|
List<EfVaultRepo.CipherRepository> suts,
|
||||||
|
List<EfRepo.UserRepository> efUserRepos)
|
||||||
|
{
|
||||||
|
foreach (var sut in suts)
|
||||||
|
{
|
||||||
|
var i = suts.IndexOf(sut);
|
||||||
|
|
||||||
|
var efUser = await efUserRepos[i].CreateAsync(user);
|
||||||
|
efUserRepos[i].ClearChangeTracking();
|
||||||
|
|
||||||
|
cipher.UserId = efUser.Id;
|
||||||
|
cipher.OrganizationId = null;
|
||||||
|
|
||||||
|
var createdCipher = await sut.CreateAsync(cipher);
|
||||||
|
sut.ClearChangeTracking();
|
||||||
|
|
||||||
|
var archiveUtcNow = await sut.ArchiveAsync(new[] { createdCipher.Id }, efUser.Id);
|
||||||
|
sut.ClearChangeTracking();
|
||||||
|
|
||||||
|
var savedCipher = await sut.GetByIdAsync(createdCipher.Id);
|
||||||
|
Assert.NotNull(savedCipher);
|
||||||
|
|
||||||
|
Assert.Equal(archiveUtcNow, savedCipher.RevisionDate);
|
||||||
|
|
||||||
|
Assert.False(string.IsNullOrWhiteSpace(savedCipher.Archives));
|
||||||
|
var archives = CoreHelpers.LoadClassFromJsonData<Dictionary<Guid, DateTime>>(savedCipher.Archives);
|
||||||
|
Assert.NotNull(archives);
|
||||||
|
Assert.True(archives.ContainsKey(efUser.Id));
|
||||||
|
Assert.Equal(archiveUtcNow, archives[efUser.Id]);
|
||||||
|
|
||||||
|
var bumpedUser = await efUserRepos[i].GetByIdAsync(efUser.Id);
|
||||||
|
Assert.Equal(DateTime.UtcNow.ToShortDateString(), bumpedUser.AccountRevisionDate.ToShortDateString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[CiSkippedTheory, EfUserCipherCustomize, BitAutoData]
|
||||||
|
public async Task UnarchiveAsync_RemovesUserFromArchivesJsonAndBumpsUserAccountRevisionDate(
|
||||||
|
Cipher cipher,
|
||||||
|
User user,
|
||||||
|
List<EfVaultRepo.CipherRepository> suts,
|
||||||
|
List<EfRepo.UserRepository> efUserRepos)
|
||||||
|
{
|
||||||
|
foreach (var sut in suts)
|
||||||
|
{
|
||||||
|
var i = suts.IndexOf(sut);
|
||||||
|
|
||||||
|
var efUser = await efUserRepos[i].CreateAsync(user);
|
||||||
|
efUserRepos[i].ClearChangeTracking();
|
||||||
|
|
||||||
|
cipher.UserId = efUser.Id;
|
||||||
|
cipher.OrganizationId = null;
|
||||||
|
|
||||||
|
var createdCipher = await sut.CreateAsync(cipher);
|
||||||
|
sut.ClearChangeTracking();
|
||||||
|
|
||||||
|
// Precondition: archived
|
||||||
|
await sut.ArchiveAsync(new[] { createdCipher.Id }, efUser.Id);
|
||||||
|
sut.ClearChangeTracking();
|
||||||
|
|
||||||
|
var unarchiveUtcNow = await sut.UnarchiveAsync(new[] { createdCipher.Id }, efUser.Id);
|
||||||
|
sut.ClearChangeTracking();
|
||||||
|
|
||||||
|
var savedCipher = await sut.GetByIdAsync(createdCipher.Id);
|
||||||
|
Assert.NotNull(savedCipher);
|
||||||
|
|
||||||
|
Assert.Equal(unarchiveUtcNow, savedCipher.RevisionDate);
|
||||||
|
|
||||||
|
// Archives should be null or not contain this user (repo clears string when map empty)
|
||||||
|
if (!string.IsNullOrWhiteSpace(savedCipher.Archives))
|
||||||
|
{
|
||||||
|
var archives = CoreHelpers.LoadClassFromJsonData<Dictionary<Guid, DateTime>>(savedCipher.Archives)
|
||||||
|
?? new Dictionary<Guid, DateTime>();
|
||||||
|
Assert.False(archives.ContainsKey(efUser.Id));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Null(savedCipher.Archives);
|
||||||
|
}
|
||||||
|
|
||||||
|
var bumpedUser = await efUserRepos[i].GetByIdAsync(efUser.Id);
|
||||||
|
Assert.Equal(DateTime.UtcNow.ToShortDateString(), bumpedUser.AccountRevisionDate.ToShortDateString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
CREATE OR ALTER PROCEDURE [dbo].[Cipher_Unarchive]
|
||||||
|
@Ids AS [dbo].[GuidIdArray] READONLY,
|
||||||
|
@UserId AS UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
CREATE TABLE #Temp
|
||||||
|
(
|
||||||
|
[Id] UNIQUEIDENTIFIER NOT NULL,
|
||||||
|
[UserId] UNIQUEIDENTIFIER NULL
|
||||||
|
)
|
||||||
|
|
||||||
|
INSERT INTO #Temp
|
||||||
|
SELECT
|
||||||
|
ucd.[Id],
|
||||||
|
ucd.[UserId]
|
||||||
|
FROM
|
||||||
|
[dbo].[UserCipherDetails](@UserId) ucd
|
||||||
|
INNER JOIN @Ids ids ON ids.Id = ucd.[Id]
|
||||||
|
WHERE
|
||||||
|
ucd.[ArchivedDate] IS NOT NULL
|
||||||
|
|
||||||
|
DECLARE @UtcNow DATETIME2(7) = SYSUTCDATETIME();
|
||||||
|
UPDATE
|
||||||
|
[dbo].[Cipher]
|
||||||
|
SET
|
||||||
|
[Archives] = JSON_MODIFY(
|
||||||
|
COALESCE([Archives], N'{}'),
|
||||||
|
CONCAT('$."', @UserId, '"'),
|
||||||
|
NULL
|
||||||
|
),
|
||||||
|
[RevisionDate] = @UtcNow
|
||||||
|
FROM [dbo].[Cipher] AS c
|
||||||
|
INNER JOIN #Temp AS t
|
||||||
|
ON t.[Id] = c.[Id];
|
||||||
|
|
||||||
|
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
||||||
|
|
||||||
|
DROP TABLE #Temp
|
||||||
|
|
||||||
|
SELECT @UtcNow
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE [dbo].[Cipher_Archive]
|
||||||
|
@Ids AS [dbo].[GuidIdArray] READONLY,
|
||||||
|
@UserId AS UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
CREATE TABLE #Temp
|
||||||
|
(
|
||||||
|
[Id] UNIQUEIDENTIFIER NOT NULL,
|
||||||
|
[UserId] UNIQUEIDENTIFIER NULL
|
||||||
|
)
|
||||||
|
|
||||||
|
INSERT INTO #Temp
|
||||||
|
SELECT
|
||||||
|
ucd.[Id],
|
||||||
|
ucd.[UserId]
|
||||||
|
FROM
|
||||||
|
[dbo].[UserCipherDetails](@UserId) ucd
|
||||||
|
INNER JOIN @Ids ids ON ids.Id = ucd.[Id]
|
||||||
|
WHERE
|
||||||
|
ucd.[ArchivedDate] IS NULL
|
||||||
|
|
||||||
|
DECLARE @UtcNow DATETIME2(7) = SYSUTCDATETIME();
|
||||||
|
UPDATE
|
||||||
|
[dbo].[Cipher]
|
||||||
|
SET
|
||||||
|
[Archives] = JSON_MODIFY(
|
||||||
|
COALESCE([Archives], N'{}'),
|
||||||
|
CONCAT('$."', @UserId, '"'),
|
||||||
|
CONVERT(NVARCHAR(30), @UtcNow, 127)
|
||||||
|
),
|
||||||
|
[RevisionDate] = @UtcNow
|
||||||
|
FROM [dbo].[Cipher] AS c
|
||||||
|
INNER JOIN #Temp AS t
|
||||||
|
ON t.[Id] = c.[Id];
|
||||||
|
|
||||||
|
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
||||||
|
|
||||||
|
DROP TABLE #Temp
|
||||||
|
|
||||||
|
SELECT @UtcNow
|
||||||
|
END
|
||||||
|
GO
|
||||||
Reference in New Issue
Block a user