diff --git a/src/Core/Vault/Entities/Cipher.cs b/src/Core/Vault/Entities/Cipher.cs index f6afc090bb..9366a7a0eb 100644 --- a/src/Core/Vault/Entities/Cipher.cs +++ b/src/Core/Vault/Entities/Cipher.cs @@ -25,7 +25,10 @@ public class Cipher : ITableObject, ICloneable public DateTime? DeletedDate { get; set; } public Enums.CipherRepromptType? Reprompt { get; set; } public string Key { get; set; } + // Deprecated. Left for backwards compatibility with old archive handling. + // Remove after all clients migrate to the Archives JSON column. public DateTime? ArchivedDate { get; set; } + public string Archives { get; set; } public void SetNewId() { diff --git a/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs b/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs index 880ee77854..465404c760 100644 --- a/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs +++ b/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs @@ -34,7 +34,9 @@ public class CipherDetailsQuery : IQuery FolderId = (_ignoreFolders || !_userId.HasValue || c.Folders == null || !c.Folders.ToLowerInvariant().Contains(_userId.Value.ToString())) ? null : CoreHelpers.LoadClassFromJsonData>(c.Folders)[_userId.Value], - ArchivedDate = c.ArchivedDate, + ArchivedDate = !_userId.HasValue || c.Archives == null || !c.Archives.ToLowerInvariant().Contains(_userId.Value.ToString()) ? + null : + CoreHelpers.LoadClassFromJsonData>(c.Archives)[_userId.Value], }; return query; } diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Archive.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Archive.sql index 68f11c0d4f..d2c41f29ba 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Archive.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Archive.sql @@ -26,7 +26,11 @@ BEGIN UPDATE [dbo].[Cipher] SET - [ArchivedDate] = @UtcNow, + [ArchivedDate] = JSON_MODIFY( + COALESCE([Archives], N'{}'), + '$."' + CONVERT(NVARCHAR(36), @UserId) + '"', + @UtcNow + ), [RevisionDate] = @UtcNow WHERE [Id] IN (SELECT [Id] FROM #Temp) diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Unarchive.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Unarchive.sql index c2b7b10619..d3ef3df96e 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Unarchive.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Unarchive.sql @@ -26,7 +26,11 @@ BEGIN UPDATE [dbo].[Cipher] SET - [ArchivedDate] = NULL, + [Archives] = JSON_MODIFY( + COALESCE([Archives], N'{}'), + '$."' + CONVERT(NVARCHAR(36), @UserId) + '"', + NULL + ), [RevisionDate] = @UtcNow WHERE [Id] IN (SELECT [Id] FROM #Temp) diff --git a/util/Migrator/DbScripts/2025-12-02_AddCipherArchives.sql b/util/Migrator/DbScripts/2025-12-02_AddCipherArchives.sql index 421fcc4487..bbfe5d91e3 100644 --- a/util/Migrator/DbScripts/2025-12-02_AddCipherArchives.sql +++ b/util/Migrator/DbScripts/2025-12-02_AddCipherArchives.sql @@ -160,3 +160,105 @@ FROM WHERE C.[UserId] = @UserId; GO + +IF OBJECT_ID('[dbo].[Cipher_Archive]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Cipher_Archive]; +END +GO + +CREATE 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 + [Id], + [UserId] + FROM + [dbo].[UserCipherDetails](@UserId) + WHERE + [Edit] = 1 + AND [ArchivedDate] IS NULL + AND [Id] IN (SELECT * FROM @Ids) + + DECLARE @UtcNow DATETIME2(7) = SYSUTCDATETIME(); + UPDATE + [dbo].[Cipher] + SET + [ArchivedDate] = JSON_MODIFY( + COALESCE([Archives], N'{}'), + '$."' + CONVERT(NVARCHAR(36), @UserId) + '"', + @UtcNow + ), + [RevisionDate] = @UtcNow + WHERE + [Id] IN (SELECT [Id] FROM #Temp) + + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId + + DROP TABLE #Temp + + SELECT @UtcNow +END +GO + +IF OBJECT_ID('[dbo].[Cipher_Unarchive]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Cipher_Unarchive]; +END +GO + +CREATE 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 + [Id], + [UserId] + FROM + [dbo].[UserCipherDetails](@UserId) + WHERE + [Edit] = 1 + AND [ArchivedDate] IS NOT NULL + AND [Id] IN (SELECT * FROM @Ids) + + DECLARE @UtcNow DATETIME2(7) = SYSUTCDATETIME(); + UPDATE + [dbo].[Cipher] + SET + [Archives] = JSON_MODIFY( + COALESCE([Archives], N'{}'), + '$."' + CONVERT(NVARCHAR(36), @UserId) + '"', + NULL + ), + [RevisionDate] = @UtcNow + WHERE + [Id] IN (SELECT [Id] FROM #Temp) + + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId + + DROP TABLE #Temp + + SELECT @UtcNow +END +GO