Browse Source

Individual Vault Item Encryption Feature (#3256)

* [SG-966] [SG-967] Add new cipher properties, update DB objects and create migrations (#2681)

* Updated cipher entity with two new columns

* Added sqlserver mifgration and updated applicable stored procedures and table

* Added EF Migrations

* Made changes to response model to include new column properties

* Fixed formatting

* Modified scripts to reflect suggestions made on PR

* Added column to cipher table using default

* Include constraint in create cipher table script

* Added key and forcerotatekey property to request model (#2716)

* Added key update on the Cipher_UpdateWithCollection stored procedure, ef (#2855)

* Added key and forceKeyRotation to BuildCiphersTable method (#2893)

* [PM-2211] Remove forceKeyRotation column (#2921)

* Removed forceKeyRotation column

* Adjusted date for migrtaion file

* Passed key column to update cipher script to update cipher key when it is rotated (#2967)

* [PM-2448] Update CipherDetails_Update SP to update attachment column (#2992)

* Updated the cipherdetails_update stored procedure to update the attachement column when encrypted with the cipher key

* Moved migration and renamed old migration file

* Fixed lint issues

* Fixed lint issues

* renamed sqlserver migration to have a more recent date

* [PM-2548] Added validation to edit and add attachments methods (#3130)

* PM-2548 Added validation to edit and add attachments methods

* PM-2548 Moved the validation to a private method

* PM-2548 Minor refactor

* Bumped up minimum version

* Bumped up minimum version

* Changed version for tests purposes

* Bumped up minimum version

* Updated encryption minimum version to match clients for QA.

* PM-3976 Passed Key column to update cipher on bulk edit (#3299)

* Updated minimum client version in preparation for release.

* Renamed migration with current date. (#3303)

---------

Co-authored-by: SmithThe4th <gsmith@bitwarden.com>
Co-authored-by: gbubemismith <gsmithwalter@gmail.com>
Co-authored-by: Carlos Gonçalves <cgoncalves@bitwarden.com>
Co-authored-by: Carlos Gonçalves <carlosmaccam@gmail.com>
pull/3309/head
Todd Martin 2 years ago committed by GitHub
parent
commit
7ede956c32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      src/Api/Vault/Controllers/CiphersController.cs
  2. 2
      src/Api/Vault/Models/Request/CipherRequestModel.cs
  3. 2
      src/Api/Vault/Models/Response/CipherResponseModel.cs
  4. 2
      src/Core/Constants.cs
  5. 1
      src/Core/Vault/Entities/Cipher.cs
  6. 9
      src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs
  7. 1
      src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs
  8. 2
      src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs
  9. 1
      src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs
  10. 3
      src/Sql/Vault/dbo/Functions/CipherDetails.sql
  11. 9
      src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_Create.sql
  12. 3
      src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql
  13. 9
      src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_Update.sql
  14. 9
      src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_Create.sql
  15. 3
      src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql
  16. 6
      src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_Update.sql
  17. 4
      src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql
  18. 1
      src/Sql/Vault/dbo/Tables/Cipher.sql
  19. 428
      util/Migrator/DbScripts/2023-09-27_00_CipherKeyUpdate.sql
  20. 2135
      util/MySqlMigrations/Migrations/20230208212115_CipherKeyUpdate.Designer.cs
  21. 25
      util/MySqlMigrations/Migrations/20230208212115_CipherKeyUpdate.cs
  22. 3
      util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs
  23. 2146
      util/PostgresMigrations/Migrations/20230208210621_CipherKeyUpdate.Designer.cs
  24. 24
      util/PostgresMigrations/Migrations/20230208210621_CipherKeyUpdate.cs
  25. 3
      util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs
  26. 2133
      util/SqliteMigrations/Migrations/20230208210629_CipherKeyUpdate.Designer.cs
  27. 24
      util/SqliteMigrations/Migrations/20230208210629_CipherKeyUpdate.cs
  28. 3
      util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs

16
src/Api/Vault/Controllers/CiphersController.cs

@ -36,6 +36,7 @@ public class CiphersController : Controller @@ -36,6 +36,7 @@ public class CiphersController : Controller
private readonly ICurrentContext _currentContext;
private readonly ILogger<CiphersController> _logger;
private readonly GlobalSettings _globalSettings;
private readonly Version _cipherKeyEncryptionMinimumVersion = new Version(Constants.CipherKeyEncryptionMinimumVersion);
public CiphersController(
ICipherRepository cipherRepository,
@ -177,6 +178,8 @@ public class CiphersController : Controller @@ -177,6 +178,8 @@ public class CiphersController : Controller
throw new NotFoundException();
}
ValidateItemLevelEncryptionIsAvailable(cipher);
var collectionIds = (await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(userId, id)).Select(c => c.CollectionId).ToList();
var modelOrgId = string.IsNullOrWhiteSpace(model.OrganizationId) ?
(Guid?)null : new Guid(model.OrganizationId);
@ -198,6 +201,9 @@ public class CiphersController : Controller @@ -198,6 +201,9 @@ public class CiphersController : Controller
{
var userId = _userService.GetProperUserId(User).Value;
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(id);
ValidateItemLevelEncryptionIsAvailable(cipher);
if (cipher == null || !cipher.OrganizationId.HasValue ||
!await _currentContext.EditAnyCollection(cipher.OrganizationId.Value))
{
@ -576,6 +582,8 @@ public class CiphersController : Controller @@ -576,6 +582,8 @@ public class CiphersController : Controller
throw new NotFoundException();
}
ValidateItemLevelEncryptionIsAvailable(cipher);
if (request.FileSize > CipherService.MAX_FILE_SIZE)
{
throw new BadRequestException($"Max file size is {CipherService.MAX_FILE_SIZE_READABLE}.");
@ -795,4 +803,12 @@ public class CiphersController : Controller @@ -795,4 +803,12 @@ public class CiphersController : Controller
throw new BadRequestException("Invalid content.");
}
}
private void ValidateItemLevelEncryptionIsAvailable(Cipher cipher)
{
if (cipher.Key != null && _currentContext.ClientVersion < _cipherKeyEncryptionMinimumVersion)
{
throw new BadRequestException("Cannot edit item. Update to the latest version of Bitwarden and try again.");
}
}
}

2
src/Api/Vault/Models/Request/CipherRequestModel.cs

@ -18,6 +18,7 @@ public class CipherRequestModel @@ -18,6 +18,7 @@ public class CipherRequestModel
public string FolderId { get; set; }
public bool Favorite { get; set; }
public CipherRepromptType Reprompt { get; set; }
public string Key { get; set; }
[Required]
[EncryptedString]
[EncryptedStringLength(1000)]
@ -86,6 +87,7 @@ public class CipherRequestModel @@ -86,6 +87,7 @@ public class CipherRequestModel
}
existingCipher.Reprompt = Reprompt;
existingCipher.Key = Key;
var hasAttachments2 = (Attachments2?.Count ?? 0) > 0;
var hasAttachments = (Attachments?.Count ?? 0) > 0;

2
src/Api/Vault/Models/Response/CipherResponseModel.cs

@ -63,6 +63,7 @@ public class CipherMiniResponseModel : ResponseModel @@ -63,6 +63,7 @@ public class CipherMiniResponseModel : ResponseModel
CreationDate = cipher.CreationDate;
DeletedDate = cipher.DeletedDate;
Reprompt = cipher.Reprompt.GetValueOrDefault(CipherRepromptType.None);
Key = cipher.Key;
}
public Guid Id { get; set; }
@ -83,6 +84,7 @@ public class CipherMiniResponseModel : ResponseModel @@ -83,6 +84,7 @@ public class CipherMiniResponseModel : ResponseModel
public DateTime CreationDate { get; set; }
public DateTime? DeletedDate { get; set; }
public CipherRepromptType Reprompt { get; set; }
public string Key { get; set; }
}
public class CipherResponseModel : CipherMiniResponseModel

2
src/Core/Constants.cs

@ -19,6 +19,8 @@ public static class Constants @@ -19,6 +19,8 @@ public static class Constants
/// their subscription has expired.
/// </summary>
public const int OrganizationSelfHostSubscriptionGracePeriodDays = 60;
public const string CipherKeyEncryptionMinimumVersion = "2023.9.1";
}
public static class TokenPurposes

1
src/Core/Vault/Entities/Cipher.cs

@ -21,6 +21,7 @@ public class Cipher : ITableObject<Guid>, ICloneable @@ -21,6 +21,7 @@ public class Cipher : ITableObject<Guid>, ICloneable
public DateTime RevisionDate { get; set; } = DateTime.UtcNow;
public DateTime? DeletedDate { get; set; }
public Enums.CipherRepromptType? Reprompt { get; set; }
public string Key { get; set; }
public void SetNewId()
{

9
src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs

@ -392,7 +392,8 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository @@ -392,7 +392,8 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
SET
[Data] = TC.[Data],
[Attachments] = TC.[Attachments],
[RevisionDate] = TC.[RevisionDate]
[RevisionDate] = TC.[RevisionDate],
[Key] = TC.[Key]
FROM
[dbo].[Cipher] C
INNER JOIN
@ -506,7 +507,8 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository @@ -506,7 +507,8 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
[Data] = TC.[Data],
[Attachments] = TC.[Attachments],
[RevisionDate] = TC.[RevisionDate],
[DeletedDate] = TC.[DeletedDate]
[DeletedDate] = TC.[DeletedDate],
[Key] = TC.[Key]
FROM
[dbo].[Cipher] C
INNER JOIN
@ -728,6 +730,8 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository @@ -728,6 +730,8 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
ciphersTable.Columns.Add(deletedDateColumn);
var repromptColumn = new DataColumn(nameof(c.Reprompt), typeof(short));
ciphersTable.Columns.Add(repromptColumn);
var keyColummn = new DataColumn(nameof(c.Key), typeof(string));
ciphersTable.Columns.Add(keyColummn);
foreach (DataColumn col in ciphersTable.Columns)
{
@ -754,6 +758,7 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository @@ -754,6 +758,7 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
row[revisionDateColumn] = cipher.RevisionDate;
row[deletedDateColumn] = cipher.DeletedDate.HasValue ? (object)cipher.DeletedDate : DBNull.Value;
row[repromptColumn] = cipher.Reprompt;
row[keyColummn] = cipher.Key;
ciphersTable.Rows.Add(row);
}

1
src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs

@ -73,6 +73,7 @@ public class UserCipherDetailsQuery : IQuery<CipherDetails> @@ -73,6 +73,7 @@ public class UserCipherDetailsQuery : IQuery<CipherDetails>
Reprompt = c.Reprompt,
ViewPassword = true,
OrganizationUseTotp = false,
Key = c.Key
});
return union;
}

2
src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs

@ -366,6 +366,7 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G @@ -366,6 +366,7 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
Reprompt = c.Reprompt,
ViewPassword = true,
OrganizationUseTotp = false,
Key = c.Key
};
}
var ciphers = await cipherDetailsView.ToListAsync();
@ -591,6 +592,7 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G @@ -591,6 +592,7 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
trackedCipher.Attachments = cipher.Attachments;
trackedCipher.RevisionDate = cipher.RevisionDate;
trackedCipher.DeletedDate = cipher.DeletedDate;
trackedCipher.Key = cipher.Key;
await transaction.CommitAsync();

1
src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs

@ -29,6 +29,7 @@ public class CipherDetailsQuery : IQuery<CipherDetails> @@ -29,6 +29,7 @@ public class CipherDetailsQuery : IQuery<CipherDetails>
RevisionDate = c.RevisionDate,
DeletedDate = c.DeletedDate,
Reprompt = c.Reprompt,
Key = c.Key,
Favorite = _userId.HasValue && c.Favorites != null && c.Favorites.ToLowerInvariant().Contains($"\"{_userId}\":true"),
FolderId = (_ignoreFolders || !_userId.HasValue || c.Folders == null || !c.Folders.ToLowerInvariant().Contains(_userId.Value.ToString())) ?
null :

3
src/Sql/Vault/dbo/Functions/CipherDetails.sql

@ -26,6 +26,7 @@ SELECT @@ -26,6 +26,7 @@ SELECT
ELSE TRY_CONVERT(UNIQUEIDENTIFIER, JSON_VALUE(C.[Folders], CONCAT('$."', @UserId, '"')))
END [FolderId],
C.[DeletedDate],
C.[Reprompt]
C.[Reprompt],
C.[Key]
FROM
[dbo].[Cipher] C

9
src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_Create.sql

@ -15,7 +15,8 @@ @@ -15,7 +15,8 @@
@ViewPassword BIT, -- not used
@OrganizationUseTotp BIT, -- not used
@DeletedDate DATETIME2(7),
@Reprompt TINYINT
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL
AS
BEGIN
SET NOCOUNT ON
@ -35,7 +36,8 @@ BEGIN @@ -35,7 +36,8 @@ BEGIN
[CreationDate],
[RevisionDate],
[DeletedDate],
[Reprompt]
[Reprompt],
[Key]
)
VALUES
(
@ -49,7 +51,8 @@ BEGIN @@ -49,7 +51,8 @@ BEGIN
@CreationDate,
@RevisionDate,
@DeletedDate,
@Reprompt
@Reprompt,
@Key
)
IF @OrganizationId IS NOT NULL

3
src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
@OrganizationUseTotp BIT, -- not used
@DeletedDate DATETIME2(7),
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL,
@CollectionIds AS [dbo].[GuidIdArray] READONLY
AS
BEGIN
@ -23,7 +24,7 @@ BEGIN @@ -23,7 +24,7 @@ BEGIN
EXEC [dbo].[CipherDetails_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders,
@Attachments, @CreationDate, @RevisionDate, @FolderId, @Favorite, @Edit, @ViewPassword,
@OrganizationUseTotp, @DeletedDate, @Reprompt
@OrganizationUseTotp, @DeletedDate, @Reprompt, @Key
DECLARE @UpdateCollectionsSuccess INT
EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds

9
src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_Update.sql

@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
@Data NVARCHAR(MAX),
@Favorites NVARCHAR(MAX), -- not used
@Folders NVARCHAR(MAX), -- not used
@Attachments NVARCHAR(MAX), -- not used
@Attachments NVARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@FolderId UNIQUEIDENTIFIER,
@ -15,7 +15,8 @@ @@ -15,7 +15,8 @@
@ViewPassword BIT, -- not used
@OrganizationUseTotp BIT, -- not used
@DeletedDate DATETIME2(2),
@Reprompt TINYINT
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL
AS
BEGIN
SET NOCOUNT ON
@ -48,10 +49,12 @@ BEGIN @@ -48,10 +49,12 @@ BEGIN
ELSE
JSON_MODIFY([Favorites], @UserIdPath, NULL)
END,
[Attachments] = @Attachments,
[Reprompt] = @Reprompt,
[CreationDate] = @CreationDate,
[RevisionDate] = @RevisionDate,
[DeletedDate] = @DeletedDate
[DeletedDate] = @DeletedDate,
[Key] = @Key
WHERE
[Id] = @Id

9
src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_Create.sql

@ -10,7 +10,8 @@ @@ -10,7 +10,8 @@
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@DeletedDate DATETIME2(7),
@Reprompt TINYINT
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL
AS
BEGIN
SET NOCOUNT ON
@ -28,7 +29,8 @@ BEGIN @@ -28,7 +29,8 @@ BEGIN
[CreationDate],
[RevisionDate],
[DeletedDate],
[Reprompt]
[Reprompt],
[Key]
)
VALUES
(
@ -43,7 +45,8 @@ BEGIN @@ -43,7 +45,8 @@ BEGIN
@CreationDate,
@RevisionDate,
@DeletedDate,
@Reprompt
@Reprompt,
@Key
)
IF @OrganizationId IS NOT NULL

3
src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql

@ -11,13 +11,14 @@ @@ -11,13 +11,14 @@
@RevisionDate DATETIME2(7),
@DeletedDate DATETIME2(7),
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL,
@CollectionIds AS [dbo].[GuidIdArray] READONLY
AS
BEGIN
SET NOCOUNT ON
EXEC [dbo].[Cipher_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders,
@Attachments, @CreationDate, @RevisionDate, @DeletedDate, @Reprompt
@Attachments, @CreationDate, @RevisionDate, @DeletedDate, @Reprompt, @Key
DECLARE @UpdateCollectionsSuccess INT
EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds

6
src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_Update.sql

@ -10,7 +10,8 @@ @@ -10,7 +10,8 @@
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@DeletedDate DATETIME2(7),
@Reprompt TINYINT
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL
AS
BEGIN
SET NOCOUNT ON
@ -28,7 +29,8 @@ BEGIN @@ -28,7 +29,8 @@ BEGIN
[CreationDate] = @CreationDate,
[RevisionDate] = @RevisionDate,
[DeletedDate] = @DeletedDate,
[Reprompt] = @Reprompt
[Reprompt] = @Reprompt,
[Key] = @Key
WHERE
[Id] = @Id

4
src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql

@ -11,6 +11,7 @@ @@ -11,6 +11,7 @@
@RevisionDate DATETIME2(7),
@DeletedDate DATETIME2(7),
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL,
@CollectionIds AS [dbo].[GuidIdArray] READONLY
AS
BEGIN
@ -36,7 +37,8 @@ BEGIN @@ -36,7 +37,8 @@ BEGIN
[Data] = @Data,
[Attachments] = @Attachments,
[RevisionDate] = @RevisionDate,
[DeletedDate] = @DeletedDate
[DeletedDate] = @DeletedDate,
[Key] = @Key
-- No need to update CreationDate, Favorites, Folders, or Type since that data will not change
WHERE
[Id] = @Id

1
src/Sql/Vault/dbo/Tables/Cipher.sql

@ -12,6 +12,7 @@ CREATE TABLE [dbo].[Cipher] ( @@ -12,6 +12,7 @@ CREATE TABLE [dbo].[Cipher] (
[RevisionDate] DATETIME2 (7) NOT NULL,
[DeletedDate] DATETIME2 (7) NULL,
[Reprompt] TINYINT NULL,
[Key] VARCHAR(MAX) NULL,
CONSTRAINT [PK_Cipher] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_Cipher_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]),
CONSTRAINT [FK_Cipher_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])

428
util/Migrator/DbScripts/2023-09-27_00_CipherKeyUpdate.sql

@ -0,0 +1,428 @@ @@ -0,0 +1,428 @@
-- Add Key Column
IF COL_LENGTH('[dbo].[Cipher]', 'Key') IS NULL
BEGIN
ALTER TABLE [dbo].[Cipher] ADD [Key] VARCHAR (MAX) NULL;
END
GO
-- Remove ForceKeyRotation Column from the Cipher table
-- (this is to help with the dev work as the column was no longer needed)
IF COL_LENGTH('[dbo].[Cipher]', 'ForceKeyRotation') IS NOT NULL
BEGIN
ALTER TABLE [dbo].[Cipher] DROP CONSTRAINT [D_Cipher_ForceKeyRotation];
ALTER TABLE [dbo].[Cipher] DROP COLUMN [ForceKeyRotation];
END
GO
CREATE OR ALTER FUNCTION [dbo].[CipherDetails](@UserId UNIQUEIDENTIFIER)
RETURNS TABLE
AS RETURN
SELECT
C.[Id],
C.[UserId],
C.[OrganizationId],
C.[Type],
C.[Data],
C.[Attachments],
C.[CreationDate],
C.[RevisionDate],
CASE
WHEN
@UserId IS NULL
OR C.[Favorites] IS NULL
OR JSON_VALUE(C.[Favorites], CONCAT('$."', @UserId, '"')) IS NULL
THEN 0
ELSE 1
END [Favorite],
CASE
WHEN
@UserId IS NULL
OR C.[Folders] IS NULL
THEN NULL
ELSE TRY_CONVERT(UNIQUEIDENTIFIER, JSON_VALUE(C.[Folders], CONCAT('$."', @UserId, '"')))
END [FolderId],
C.[DeletedDate],
C.[Reprompt],
C.[Key]
FROM
[dbo].[Cipher] C
GO
IF OBJECT_ID('[dbo].[UserCipherDetails]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[UserCipherDetails]';
END
GO
IF OBJECT_ID('[dbo].[CipherView]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[CipherView]';
END
GO
CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_Create]
@Id UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Type TINYINT,
@Data NVARCHAR(MAX),
@Favorites NVARCHAR(MAX), -- not used
@Folders NVARCHAR(MAX), -- not used
@Attachments NVARCHAR(MAX), -- not used
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@FolderId UNIQUEIDENTIFIER,
@Favorite BIT,
@Edit BIT, -- not used
@ViewPassword BIT, -- not used
@OrganizationUseTotp BIT, -- not used
@DeletedDate DATETIME2(7),
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL
AS
BEGIN
SET NOCOUNT ON
DECLARE @UserIdKey VARCHAR(50) = CONCAT('"', @UserId, '"')
DECLARE @UserIdPath VARCHAR(50) = CONCAT('$.', @UserIdKey)
INSERT INTO [dbo].[Cipher]
(
[Id],
[UserId],
[OrganizationId],
[Type],
[Data],
[Favorites],
[Folders],
[CreationDate],
[RevisionDate],
[DeletedDate],
[Reprompt],
[Key]
)
VALUES
(
@Id,
CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END,
@OrganizationId,
@Type,
@Data,
CASE WHEN @Favorite = 1 THEN CONCAT('{', @UserIdKey, ':true}') ELSE NULL END,
CASE WHEN @FolderId IS NOT NULL THEN CONCAT('{', @UserIdKey, ':"', @FolderId, '"', '}') ELSE NULL END,
@CreationDate,
@RevisionDate,
@DeletedDate,
@Reprompt,
@Key
)
IF @OrganizationId IS NOT NULL
BEGIN
EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId
END
ELSE IF @UserId IS NOT NULL
BEGIN
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
END
END
GO
CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_Update]
@Id UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Type TINYINT,
@Data NVARCHAR(MAX),
@Favorites NVARCHAR(MAX), -- not used
@Folders NVARCHAR(MAX), -- not used
@Attachments NVARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@FolderId UNIQUEIDENTIFIER,
@Favorite BIT,
@Edit BIT, -- not used
@ViewPassword BIT, -- not used
@OrganizationUseTotp BIT, -- not used
@DeletedDate DATETIME2(2),
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL
AS
BEGIN
SET NOCOUNT ON
DECLARE @UserIdKey VARCHAR(50) = CONCAT('"', @UserId, '"')
DECLARE @UserIdPath VARCHAR(50) = CONCAT('$.', @UserIdKey)
UPDATE
[dbo].[Cipher]
SET
[UserId] = CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END,
[OrganizationId] = @OrganizationId,
[Type] = @Type,
[Data] = @Data,
[Folders] =
CASE
WHEN @FolderId IS NOT NULL AND [Folders] IS NULL THEN
CONCAT('{', @UserIdKey, ':"', @FolderId, '"', '}')
WHEN @FolderId IS NOT NULL THEN
JSON_MODIFY([Folders], @UserIdPath, CAST(@FolderId AS VARCHAR(50)))
ELSE
JSON_MODIFY([Folders], @UserIdPath, NULL)
END,
[Favorites] =
CASE
WHEN @Favorite = 1 AND [Favorites] IS NULL THEN
CONCAT('{', @UserIdKey, ':true}')
WHEN @Favorite = 1 THEN
JSON_MODIFY([Favorites], @UserIdPath, CAST(1 AS BIT))
ELSE
JSON_MODIFY([Favorites], @UserIdPath, NULL)
END,
[Attachments] = @Attachments,
[Reprompt] = @Reprompt,
[CreationDate] = @CreationDate,
[RevisionDate] = @RevisionDate,
[DeletedDate] = @DeletedDate,
[Key] = @Key
WHERE
[Id] = @Id
IF @OrganizationId IS NOT NULL
BEGIN
EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId
END
ELSE IF @UserId IS NOT NULL
BEGIN
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
END
END
GO
CREATE OR ALTER PROCEDURE [dbo].[Cipher_Update]
@Id UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Type TINYINT,
@Data NVARCHAR(MAX),
@Favorites NVARCHAR(MAX),
@Folders NVARCHAR(MAX),
@Attachments NVARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@DeletedDate DATETIME2(7),
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL
AS
BEGIN
SET NOCOUNT ON
UPDATE
[dbo].[Cipher]
SET
[UserId] = CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END,
[OrganizationId] = @OrganizationId,
[Type] = @Type,
[Data] = @Data,
[Favorites] = @Favorites,
[Folders] = @Folders,
[Attachments] = @Attachments,
[CreationDate] = @CreationDate,
[RevisionDate] = @RevisionDate,
[DeletedDate] = @DeletedDate,
[Reprompt] = @Reprompt,
[Key] = @Key
WHERE
[Id] = @Id
IF @OrganizationId IS NOT NULL
BEGIN
EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId
END
ELSE IF @UserId IS NOT NULL
BEGIN
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
END
END
GO
CREATE OR ALTER PROCEDURE [dbo].[Cipher_Create]
@Id UNIQUEIDENTIFIER OUTPUT,
@UserId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Type TINYINT,
@Data NVARCHAR(MAX),
@Favorites NVARCHAR(MAX),
@Folders NVARCHAR(MAX),
@Attachments NVARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@DeletedDate DATETIME2(7),
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[Cipher]
(
[Id],
[UserId],
[OrganizationId],
[Type],
[Data],
[Favorites],
[Folders],
[Attachments],
[CreationDate],
[RevisionDate],
[DeletedDate],
[Reprompt],
[Key]
)
VALUES
(
@Id,
CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END,
@OrganizationId,
@Type,
@Data,
@Favorites,
@Folders,
@Attachments,
@CreationDate,
@RevisionDate,
@DeletedDate,
@Reprompt,
@Key
)
IF @OrganizationId IS NOT NULL
BEGIN
EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId
END
ELSE IF @UserId IS NOT NULL
BEGIN
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
END
END
GO
CREATE OR ALTER PROCEDURE [dbo].[Cipher_CreateWithCollections]
@Id UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Type TINYINT,
@Data NVARCHAR(MAX),
@Favorites NVARCHAR(MAX),
@Folders NVARCHAR(MAX),
@Attachments NVARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@DeletedDate DATETIME2(7),
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL,
@CollectionIds AS [dbo].[GuidIdArray] READONLY
AS
BEGIN
SET NOCOUNT ON
EXEC [dbo].[Cipher_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders,
@Attachments, @CreationDate, @RevisionDate, @DeletedDate, @Reprompt, @Key
DECLARE @UpdateCollectionsSuccess INT
EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds
END
GO
CREATE OR ALTER PROCEDURE [dbo].[Cipher_UpdateWithCollections]
@Id UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Type TINYINT,
@Data NVARCHAR(MAX),
@Favorites NVARCHAR(MAX),
@Folders NVARCHAR(MAX),
@Attachments NVARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@DeletedDate DATETIME2(7),
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL,
@CollectionIds AS [dbo].[GuidIdArray] READONLY
AS
BEGIN
SET NOCOUNT ON
BEGIN TRANSACTION Cipher_UpdateWithCollections
DECLARE @UpdateCollectionsSuccess INT
EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds
IF @UpdateCollectionsSuccess < 0
BEGIN
COMMIT TRANSACTION Cipher_UpdateWithCollections
SELECT -1 -- -1 = Failure
RETURN
END
UPDATE
[dbo].[Cipher]
SET
[UserId] = NULL,
[OrganizationId] = @OrganizationId,
[Data] = @Data,
[Attachments] = @Attachments,
[RevisionDate] = @RevisionDate,
[DeletedDate] = @DeletedDate,
[Key] = @Key
-- No need to update CreationDate, Favorites, Folders, or Type since that data will not change
WHERE
[Id] = @Id
COMMIT TRANSACTION Cipher_UpdateWithCollections
IF @Attachments IS NOT NULL
BEGIN
EXEC [dbo].[Organization_UpdateStorage] @OrganizationId
EXEC [dbo].[User_UpdateStorage] @UserId
END
EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId
SELECT 0 -- 0 = Success
END
GO
CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_CreateWithCollections]
@Id UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Type TINYINT,
@Data NVARCHAR(MAX),
@Favorites NVARCHAR(MAX), -- not used
@Folders NVARCHAR(MAX), -- not used
@Attachments NVARCHAR(MAX), -- not used
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@FolderId UNIQUEIDENTIFIER,
@Favorite BIT,
@Edit BIT, -- not used
@ViewPassword BIT, -- not used
@OrganizationUseTotp BIT, -- not used
@DeletedDate DATETIME2(7),
@Reprompt TINYINT,
@Key VARCHAR(MAX) = NULL,
@CollectionIds AS [dbo].[GuidIdArray] READONLY
AS
BEGIN
SET NOCOUNT ON
EXEC [dbo].[CipherDetails_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders,
@Attachments, @CreationDate, @RevisionDate, @FolderId, @Favorite, @Edit, @ViewPassword,
@OrganizationUseTotp, @DeletedDate, @Reprompt, @Key
DECLARE @UpdateCollectionsSuccess INT
EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds
END
GO

2135
util/MySqlMigrations/Migrations/20230208212115_CipherKeyUpdate.Designer.cs generated

File diff suppressed because it is too large Load Diff

25
util/MySqlMigrations/Migrations/20230208212115_CipherKeyUpdate.cs

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.MySqlMigrations.Migrations;
public partial class CipherKeyUpdate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Key",
table: "Cipher",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Key",
table: "Cipher");
}
}

3
util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs

@ -1498,6 +1498,9 @@ namespace Bit.MySqlMigrations.Migrations @@ -1498,6 +1498,9 @@ namespace Bit.MySqlMigrations.Migrations
b.Property<string>("Folders")
.HasColumnType("longtext");
b.Property<string>("Key")
.HasColumnType("longtext");
b.Property<Guid?>("OrganizationId")
.HasColumnType("char(36)");

2146
util/PostgresMigrations/Migrations/20230208210621_CipherKeyUpdate.Designer.cs generated

File diff suppressed because it is too large Load Diff

24
util/PostgresMigrations/Migrations/20230208210621_CipherKeyUpdate.cs

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.PostgresMigrations.Migrations;
public partial class CipherKeyUpdate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Key",
table: "Cipher",
type: "text",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Key",
table: "Cipher");
}
}

3
util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs

@ -1509,6 +1509,9 @@ namespace Bit.PostgresMigrations.Migrations @@ -1509,6 +1509,9 @@ namespace Bit.PostgresMigrations.Migrations
b.Property<string>("Folders")
.HasColumnType("text");
b.Property<string>("Key")
.HasColumnType("text");
b.Property<Guid?>("OrganizationId")
.HasColumnType("uuid");

2133
util/SqliteMigrations/Migrations/20230208210629_CipherKeyUpdate.Designer.cs generated

File diff suppressed because it is too large Load Diff

24
util/SqliteMigrations/Migrations/20230208210629_CipherKeyUpdate.cs

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.SqliteMigrations.Migrations;
public partial class CipherKeyUpdate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Key",
table: "Cipher",
type: "TEXT",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Key",
table: "Cipher");
}
}

3
util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs

@ -1496,6 +1496,9 @@ namespace Bit.SqliteMigrations.Migrations @@ -1496,6 +1496,9 @@ namespace Bit.SqliteMigrations.Migrations
b.Property<string>("Folders")
.HasColumnType("TEXT");
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<Guid?>("OrganizationId")
.HasColumnType("TEXT");

Loading…
Cancel
Save