diff --git a/src/Core/Models/Api/Request/SelectionReadOnlyRequestModel.cs b/src/Core/Models/Api/Request/SelectionReadOnlyRequestModel.cs index d903b3cce5..36fc5edc0b 100644 --- a/src/Core/Models/Api/Request/SelectionReadOnlyRequestModel.cs +++ b/src/Core/Models/Api/Request/SelectionReadOnlyRequestModel.cs @@ -11,13 +11,15 @@ namespace Bit.Core.Models.Api [Required] public string Id { get; set; } public bool ReadOnly { get; set; } + public bool HidePasswords { get; set; } public SelectionReadOnly ToSelectionReadOnly() { return new SelectionReadOnly { Id = new Guid(Id), - ReadOnly = ReadOnly + ReadOnly = ReadOnly, + HidePasswords = HidePasswords, }; } } diff --git a/src/Core/Models/Api/Response/CipherResponseModel.cs b/src/Core/Models/Api/Response/CipherResponseModel.cs index 348fa551c6..95457d5ed3 100644 --- a/src/Core/Models/Api/Response/CipherResponseModel.cs +++ b/src/Core/Models/Api/Response/CipherResponseModel.cs @@ -89,11 +89,13 @@ namespace Bit.Core.Models.Api FolderId = cipher.FolderId?.ToString(); Favorite = cipher.Favorite; Edit = cipher.Edit; + ViewPassword = cipher.ViewPassword; } public string FolderId { get; set; } public bool Favorite { get; set; } public bool Edit { get; set; } + public bool ViewPassword { get; set; } } public class CipherDetailsResponseModel : CipherResponseModel diff --git a/src/Core/Models/Api/Response/CollectionResponseModel.cs b/src/Core/Models/Api/Response/CollectionResponseModel.cs index 2161fbf9fb..901027a9ed 100644 --- a/src/Core/Models/Api/Response/CollectionResponseModel.cs +++ b/src/Core/Models/Api/Response/CollectionResponseModel.cs @@ -34,9 +34,11 @@ namespace Bit.Core.Models.Api : base(collectionDetails, "collectionDetails") { ReadOnly = collectionDetails.ReadOnly; + HidePasswords = collectionDetails.HidePasswords; } public bool ReadOnly { get; set; } + public bool HidePasswords { get; set; } } public class CollectionGroupDetailsResponseModel : CollectionResponseModel diff --git a/src/Core/Models/Api/Response/SelectionReadOnlyResponseModel.cs b/src/Core/Models/Api/Response/SelectionReadOnlyResponseModel.cs index 35400ebac6..0f3f238a4a 100644 --- a/src/Core/Models/Api/Response/SelectionReadOnlyResponseModel.cs +++ b/src/Core/Models/Api/Response/SelectionReadOnlyResponseModel.cs @@ -14,9 +14,11 @@ namespace Bit.Core.Models.Api Id = selection.Id.ToString(); ReadOnly = selection.ReadOnly; + HidePasswords = selection.HidePasswords; } public string Id { get; set; } public bool ReadOnly { get; set; } + public bool HidePasswords { get; set; } } } diff --git a/src/Core/Models/Data/CipherDetails.cs b/src/Core/Models/Data/CipherDetails.cs index d40567287b..7098cd8b8f 100644 --- a/src/Core/Models/Data/CipherDetails.cs +++ b/src/Core/Models/Data/CipherDetails.cs @@ -7,5 +7,6 @@ namespace Core.Models.Data public Guid? FolderId { get; set; } public bool Favorite { get; set; } public bool Edit { get; set; } + public bool ViewPassword { get; set; } } } diff --git a/src/Core/Models/Data/CollectionDetails.cs b/src/Core/Models/Data/CollectionDetails.cs index 54325135ae..62f3c46fd0 100644 --- a/src/Core/Models/Data/CollectionDetails.cs +++ b/src/Core/Models/Data/CollectionDetails.cs @@ -5,5 +5,6 @@ namespace Bit.Core.Models.Data public class CollectionDetails : Collection { public bool ReadOnly { get; set; } + public bool HidePasswords { get; set; } } } diff --git a/src/Core/Models/Data/SelectionReadOnly.cs b/src/Core/Models/Data/SelectionReadOnly.cs index a8193e5fca..fb72778128 100644 --- a/src/Core/Models/Data/SelectionReadOnly.cs +++ b/src/Core/Models/Data/SelectionReadOnly.cs @@ -6,5 +6,6 @@ namespace Bit.Core.Models.Data { public Guid Id { get; set; } public bool ReadOnly { get; set; } + public bool HidePasswords { get; set; } } } diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs index 38761168e0..bf71f7c562 100644 --- a/src/Core/Utilities/CoreHelpers.cs +++ b/src/Core/Utilities/CoreHelpers.cs @@ -125,6 +125,8 @@ namespace Bit.Core.Utilities table.Columns.Add(idColumn); var readOnlyColumn = new DataColumn("ReadOnly", typeof(bool)); table.Columns.Add(readOnlyColumn); + var hidePasswordsColumn = new DataColumn("HidePasswords", typeof(bool)); + table.Columns.Add(hidePasswordsColumn); if (values != null) { @@ -133,6 +135,7 @@ namespace Bit.Core.Utilities var row = table.NewRow(); row[idColumn] = value.Id; row[readOnlyColumn] = value.ReadOnly; + row[hidePasswordsColumn] = value.HidePasswords; table.Rows.Add(row); } } diff --git a/src/Sql/dbo/Functions/UserCipherDetails.sql b/src/Sql/dbo/Functions/UserCipherDetails.sql index 2c19246a2b..d42a08cef4 100644 --- a/src/Sql/dbo/Functions/UserCipherDetails.sql +++ b/src/Sql/dbo/Functions/UserCipherDetails.sql @@ -17,12 +17,19 @@ SELECT CASE WHEN OU.[AccessAll] = 1 - OR CU.[ReadOnly] = 0 OR G.[AccessAll] = 1 - OR CG.[ReadOnly] = 0 + OR COALESCE(CU.[ReadOnly], CG.[ReadOnly], 0) = 0 THEN 1 ELSE 0 END [Edit], + CASE + WHEN + OU.[AccessAll] = 1 + OR G.[AccessAll] = 1 + OR COALESCE(CU.[HidePasswords], CG.[HidePasswords], 0) = 0 + THEN 1 + ELSE 0 + END [ViewPassword], CASE WHEN O.[UseTotp] = 1 THEN 1 @@ -55,6 +62,7 @@ UNION ALL SELECT *, 1 [Edit], + 1 [ViewPassword], 0 [OrganizationUseTotp] FROM [dbo].[CipherDetails](@UserId) diff --git a/src/Sql/dbo/Functions/UserCollectionDetails.sql b/src/Sql/dbo/Functions/UserCollectionDetails.sql index f8eb2d9cce..e3c7210093 100644 --- a/src/Sql/dbo/Functions/UserCollectionDetails.sql +++ b/src/Sql/dbo/Functions/UserCollectionDetails.sql @@ -7,11 +7,18 @@ SELECT WHEN OU.[AccessAll] = 1 OR G.[AccessAll] = 1 - OR CU.[ReadOnly] = 0 - OR CG.[ReadOnly] = 0 + OR COALESCE(CU.[ReadOnly], CG.[ReadOnly], 0) = 0 THEN 0 ELSE 1 - END [ReadOnly] + END [ReadOnly], + CASE + WHEN + OU.[AccessAll] = 1 + OR G.[AccessAll] = 1 + OR COALESCE(CU.[HidePasswords], CG.[HidePasswords], 0) = 0 + THEN 0 + ELSE 1 + END [HidePasswords] FROM [dbo].[CollectionView] C INNER JOIN diff --git a/src/Sql/dbo/Stored Procedures/CipherDetails_Create.sql b/src/Sql/dbo/Stored Procedures/CipherDetails_Create.sql index 4c7f82d6d6..bd4c575534 100644 --- a/src/Sql/dbo/Stored Procedures/CipherDetails_Create.sql +++ b/src/Sql/dbo/Stored Procedures/CipherDetails_Create.sql @@ -12,6 +12,7 @@ @FolderId UNIQUEIDENTIFIER, @Favorite BIT, @Edit BIT, -- not used + @ViewPassword BIT, -- not used @OrganizationUseTotp BIT, -- not used @DeletedDate DATETIME2(7) AS diff --git a/src/Sql/dbo/Stored Procedures/CipherDetails_CreateWithCollections.sql b/src/Sql/dbo/Stored Procedures/CipherDetails_CreateWithCollections.sql index 3d8bdc6ca8..6c04fe3ed9 100644 --- a/src/Sql/dbo/Stored Procedures/CipherDetails_CreateWithCollections.sql +++ b/src/Sql/dbo/Stored Procedures/CipherDetails_CreateWithCollections.sql @@ -12,6 +12,7 @@ @FolderId UNIQUEIDENTIFIER, @Favorite BIT, @Edit BIT, -- not used + @ViewPassword BIT, -- not used @OrganizationUseTotp BIT, -- not used @DeletedDate DATETIME2(7), @CollectionIds AS [dbo].[GuidIdArray] READONLY @@ -20,7 +21,8 @@ BEGIN SET NOCOUNT ON EXEC [dbo].[CipherDetails_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders, - @Attachments, @CreationDate, @RevisionDate, @FolderId, @Favorite, @Edit, @OrganizationUseTotp, @DeletedDate + @Attachments, @CreationDate, @RevisionDate, @FolderId, @Favorite, @Edit, @ViewPassword, + @OrganizationUseTotp, @DeletedDate DECLARE @UpdateCollectionsSuccess INT EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds diff --git a/src/Sql/dbo/Stored Procedures/CipherDetails_Update.sql b/src/Sql/dbo/Stored Procedures/CipherDetails_Update.sql index 35830c363e..bb1f006f6e 100644 --- a/src/Sql/dbo/Stored Procedures/CipherDetails_Update.sql +++ b/src/Sql/dbo/Stored Procedures/CipherDetails_Update.sql @@ -12,6 +12,7 @@ @FolderId UNIQUEIDENTIFIER, @Favorite BIT, @Edit BIT, -- not used + @ViewPassword BIT, -- not used @OrganizationUseTotp BIT, -- not used @DeletedDate DATETIME2(2) AS diff --git a/src/Sql/dbo/Stored Procedures/CollectionUser_ReadByCollectionId.sql b/src/Sql/dbo/Stored Procedures/CollectionUser_ReadByCollectionId.sql index 472ff820b4..467ecb3e1b 100644 --- a/src/Sql/dbo/Stored Procedures/CollectionUser_ReadByCollectionId.sql +++ b/src/Sql/dbo/Stored Procedures/CollectionUser_ReadByCollectionId.sql @@ -6,7 +6,8 @@ BEGIN SELECT [OrganizationUserId] [Id], - [ReadOnly] + [ReadOnly], + [HidePasswords] FROM [dbo].[CollectionUser] WHERE diff --git a/src/Sql/dbo/Stored Procedures/CollectionUser_UpdateUsers.sql b/src/Sql/dbo/Stored Procedures/CollectionUser_UpdateUsers.sql index fed619aedf..28c767c868 100644 --- a/src/Sql/dbo/Stored Procedures/CollectionUser_UpdateUsers.sql +++ b/src/Sql/dbo/Stored Procedures/CollectionUser_UpdateUsers.sql @@ -18,14 +18,18 @@ BEGIN UPDATE [Target] SET - [Target].[ReadOnly] = [Source].[ReadOnly] + [Target].[ReadOnly] = [Source].[ReadOnly], + [Target].[HidePasswords] = [Source].[HidePasswords] FROM [dbo].[CollectionUser] [Target] INNER JOIN @Users [Source] ON [Source].[Id] = [Target].[OrganizationUserId] WHERE [Target].[CollectionId] = @CollectionId - AND [Target].[ReadOnly] != [Source].[ReadOnly] + AND ( + [Target].[ReadOnly] != [Source].[ReadOnly] + OR [Target].[HidePasswords] != [Source].[HidePasswords] + ) -- Insert INSERT INTO @@ -33,7 +37,8 @@ BEGIN SELECT @CollectionId, [Source].[Id], - [Source].[ReadOnly] + [Source].[ReadOnly], + [Source].[HidePasswords] FROM @Users [Source] INNER JOIN diff --git a/src/Sql/dbo/Stored Procedures/Collection_CreateWithGroups.sql b/src/Sql/dbo/Stored Procedures/Collection_CreateWithGroups.sql index b0475a4a73..c577e1f02a 100644 --- a/src/Sql/dbo/Stored Procedures/Collection_CreateWithGroups.sql +++ b/src/Sql/dbo/Stored Procedures/Collection_CreateWithGroups.sql @@ -24,12 +24,14 @@ BEGIN ( [CollectionId], [GroupId], - [ReadOnly] + [ReadOnly], + [HidePasswords] ) SELECT @Id, [Id], - [ReadOnly] + [ReadOnly], + [HidePasswords] FROM @Groups WHERE diff --git a/src/Sql/dbo/Stored Procedures/Collection_ReadWithGroupsById.sql b/src/Sql/dbo/Stored Procedures/Collection_ReadWithGroupsById.sql index d9caec30f9..4230dca985 100644 --- a/src/Sql/dbo/Stored Procedures/Collection_ReadWithGroupsById.sql +++ b/src/Sql/dbo/Stored Procedures/Collection_ReadWithGroupsById.sql @@ -8,7 +8,8 @@ BEGIN SELECT [GroupId] [Id], - [ReadOnly] + [ReadOnly], + [HidePasswords] FROM [dbo].[CollectionGroup] WHERE diff --git a/src/Sql/dbo/Stored Procedures/Collection_ReadWithGroupsByIdUserId.sql b/src/Sql/dbo/Stored Procedures/Collection_ReadWithGroupsByIdUserId.sql index 40ad021931..7d30e777bd 100644 --- a/src/Sql/dbo/Stored Procedures/Collection_ReadWithGroupsByIdUserId.sql +++ b/src/Sql/dbo/Stored Procedures/Collection_ReadWithGroupsByIdUserId.sql @@ -9,7 +9,8 @@ BEGIN SELECT [GroupId] [Id], - [ReadOnly] + [ReadOnly], + [HidePasswords] FROM [dbo].[CollectionGroup] WHERE diff --git a/src/Sql/dbo/Stored Procedures/Collection_UpdateWithGroups.sql b/src/Sql/dbo/Stored Procedures/Collection_UpdateWithGroups.sql index f11231dc71..b0bfba442b 100644 --- a/src/Sql/dbo/Stored Procedures/Collection_UpdateWithGroups.sql +++ b/src/Sql/dbo/Stored Procedures/Collection_UpdateWithGroups.sql @@ -33,10 +33,15 @@ BEGIN ( @Id, [Source].[Id], - [Source].[ReadOnly] + [Source].[ReadOnly], + [Source].[HidePasswords] ) - WHEN MATCHED AND [Target].[ReadOnly] != [Source].[ReadOnly] THEN - UPDATE SET [Target].[ReadOnly] = [Source].[ReadOnly] + WHEN MATCHED AND ( + [Target].[ReadOnly] != [Source].[ReadOnly] + OR [Target].[HidePasswords] != [Source].[HidePasswords] + ) THEN + UPDATE SET [Target].[ReadOnly] = [Source].[ReadOnly], + [Target].[HidePasswords] = [Source].[HidePasswords] WHEN NOT MATCHED BY SOURCE AND [Target].[CollectionId] = @Id THEN DELETE diff --git a/src/Sql/dbo/Stored Procedures/Group_CreateWithCollections.sql b/src/Sql/dbo/Stored Procedures/Group_CreateWithCollections.sql index 84a56fa49e..b41637522e 100644 --- a/src/Sql/dbo/Stored Procedures/Group_CreateWithCollections.sql +++ b/src/Sql/dbo/Stored Procedures/Group_CreateWithCollections.sql @@ -25,12 +25,14 @@ BEGIN ( [CollectionId], [GroupId], - [ReadOnly] + [ReadOnly], + [HidePasswords] ) SELECT [Id], @Id, - [ReadOnly] + [ReadOnly], + [HidePasswords] FROM @Collections WHERE diff --git a/src/Sql/dbo/Stored Procedures/Group_ReadWithCollectionsById.sql b/src/Sql/dbo/Stored Procedures/Group_ReadWithCollectionsById.sql index e06a8cb9aa..22bcbf8374 100644 --- a/src/Sql/dbo/Stored Procedures/Group_ReadWithCollectionsById.sql +++ b/src/Sql/dbo/Stored Procedures/Group_ReadWithCollectionsById.sql @@ -8,7 +8,8 @@ BEGIN SELECT [CollectionId] [Id], - [ReadOnly] + [ReadOnly], + [HidePasswords] FROM [dbo].[CollectionGroup] WHERE diff --git a/src/Sql/dbo/Stored Procedures/Group_UpdateWithCollections.sql b/src/Sql/dbo/Stored Procedures/Group_UpdateWithCollections.sql index 460c12b443..2b3f4a4d8f 100644 --- a/src/Sql/dbo/Stored Procedures/Group_UpdateWithCollections.sql +++ b/src/Sql/dbo/Stored Procedures/Group_UpdateWithCollections.sql @@ -34,10 +34,15 @@ BEGIN ( [Source].[Id], @Id, - [Source].[ReadOnly] + [Source].[ReadOnly], + [Source].[HidePasswords] ) - WHEN MATCHED AND [Target].[ReadOnly] != [Source].[ReadOnly] THEN - UPDATE SET [Target].[ReadOnly] = [Source].[ReadOnly] + WHEN MATCHED AND ( + [Target].[ReadOnly] != [Source].[ReadOnly] + OR [Target].[HidePasswords] != [Source].[HidePasswords] + ) THEN + UPDATE SET [Target].[ReadOnly] = [Source].[ReadOnly], + [Target].[HidePasswords] = [Source].[HidePasswords] WHEN NOT MATCHED BY SOURCE AND [Target].[GroupId] = @Id THEN DELETE diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUserUserDetails_ReadWithCollectionsById.sql b/src/Sql/dbo/Stored Procedures/OrganizationUserUserDetails_ReadWithCollectionsById.sql index b9c3245e8d..89173c7aee 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUserUserDetails_ReadWithCollectionsById.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUserUserDetails_ReadWithCollectionsById.sql @@ -8,7 +8,8 @@ BEGIN SELECT CU.[CollectionId] Id, - CU.[ReadOnly] + CU.[ReadOnly], + CU.[HidePasswords] FROM [dbo].[OrganizationUser] OU INNER JOIN diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateWithCollections.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateWithCollections.sql index 1c7c88c757..de7f21d3d7 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateWithCollections.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateWithCollections.sql @@ -29,12 +29,14 @@ BEGIN ( [CollectionId], [OrganizationUserId], - [ReadOnly] + [ReadOnly], + [HidePasswords] ) SELECT [Id], @Id, - [ReadOnly] + [ReadOnly], + [HidePasswords] FROM @Collections WHERE diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadWithCollectionsById.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadWithCollectionsById.sql index 39177f37f2..6675be82b4 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadWithCollectionsById.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadWithCollectionsById.sql @@ -8,7 +8,8 @@ BEGIN SELECT CU.[CollectionId] Id, - CU.[ReadOnly] + CU.[ReadOnly], + CU.[HidePasswords] FROM [dbo].[OrganizationUser] OU INNER JOIN diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateWithCollections.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateWithCollections.sql index d351752fb2..713c83f5ad 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateWithCollections.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateWithCollections.sql @@ -21,14 +21,18 @@ BEGIN UPDATE [Target] SET - [Target].[ReadOnly] = [Source].[ReadOnly] + [Target].[ReadOnly] = [Source].[ReadOnly], + [Target].[HidePasswords] = [Source].[HidePasswords] FROM [dbo].[CollectionUser] AS [Target] INNER JOIN @Collections AS [Source] ON [Source].[Id] = [Target].[CollectionId] WHERE [Target].[OrganizationUserId] = @Id - AND [Target].[ReadOnly] != [Source].[ReadOnly] + AND ( + [Target].[ReadOnly] != [Source].[ReadOnly] + OR [Target].[HidePasswords] != [Source].[HidePasswords] + ) -- Insert INSERT INTO @@ -36,7 +40,8 @@ BEGIN SELECT [Source].[Id], @Id, - [Source].[ReadOnly] + [Source].[ReadOnly], + [Source].[HidePasswords] FROM @Collections AS [Source] INNER JOIN diff --git a/src/Sql/dbo/Tables/CollectionGroup.sql b/src/Sql/dbo/Tables/CollectionGroup.sql index d3398b7a25..2329349aa8 100644 --- a/src/Sql/dbo/Tables/CollectionGroup.sql +++ b/src/Sql/dbo/Tables/CollectionGroup.sql @@ -1,7 +1,8 @@ CREATE TABLE [dbo].[CollectionGroup] ( - [CollectionId] UNIQUEIDENTIFIER NOT NULL, - [GroupId] UNIQUEIDENTIFIER NOT NULL, - [ReadOnly] BIT NOT NULL, + [CollectionId] UNIQUEIDENTIFIER NOT NULL, + [GroupId] UNIQUEIDENTIFIER NOT NULL, + [ReadOnly] BIT NOT NULL, + [HidePasswords] BIT NOT NULL, CONSTRAINT [PK_CollectionGroup] PRIMARY KEY CLUSTERED ([CollectionId] ASC, [GroupId] ASC), CONSTRAINT [FK_CollectionGroup_Collection] FOREIGN KEY ([CollectionId]) REFERENCES [dbo].[Collection] ([Id]), CONSTRAINT [FK_CollectionGroup_Group] FOREIGN KEY ([GroupId]) REFERENCES [dbo].[Group] ([Id]) ON DELETE CASCADE diff --git a/src/Sql/dbo/Tables/CollectionUser.sql b/src/Sql/dbo/Tables/CollectionUser.sql index 4bb60ab56c..333bf0f087 100644 --- a/src/Sql/dbo/Tables/CollectionUser.sql +++ b/src/Sql/dbo/Tables/CollectionUser.sql @@ -2,6 +2,7 @@ [CollectionId] UNIQUEIDENTIFIER NOT NULL, [OrganizationUserId] UNIQUEIDENTIFIER NOT NULL, [ReadOnly] BIT NOT NULL, + [HidePasswords] BIT NOT NULL, CONSTRAINT [PK_CollectionUser] PRIMARY KEY CLUSTERED ([CollectionId] ASC, [OrganizationUserId] ASC), CONSTRAINT [FK_CollectionUser_Collection] FOREIGN KEY ([CollectionId]) REFERENCES [dbo].[Collection] ([Id]) ON DELETE CASCADE, CONSTRAINT [FK_CollectionUser_OrganizationUser] FOREIGN KEY ([OrganizationUserId]) REFERENCES [dbo].[OrganizationUser] ([Id]) diff --git a/src/Sql/dbo/User Defined Types/SelectionReadOnlyArray.sql b/src/Sql/dbo/User Defined Types/SelectionReadOnlyArray.sql index 90f737b173..f2e19b1a09 100644 --- a/src/Sql/dbo/User Defined Types/SelectionReadOnlyArray.sql +++ b/src/Sql/dbo/User Defined Types/SelectionReadOnlyArray.sql @@ -1,4 +1,5 @@ CREATE TYPE [dbo].[SelectionReadOnlyArray] AS TABLE ( - [Id] UNIQUEIDENTIFIER NOT NULL, - [ReadOnly] BIT NOT NULL); + [Id] UNIQUEIDENTIFIER NOT NULL, + [ReadOnly] BIT NOT NULL, + [HidePasswords] BIT NOT NULL); diff --git a/util/Migrator/DbScripts/2020-05-22_00_HiddenPassword.sql b/util/Migrator/DbScripts/2020-05-22_00_HiddenPassword.sql new file mode 100644 index 0000000000..e5820f3e51 --- /dev/null +++ b/util/Migrator/DbScripts/2020-05-22_00_HiddenPassword.sql @@ -0,0 +1,953 @@ +/* + * Add HiddenPassword support to collections + */ + +IF OBJECT_ID('[dbo].[Group_CreateWithCollections]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Group_CreateWithCollections] +END +GO + +IF OBJECT_ID('[dbo].[Group_UpdateWithCollections]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Group_UpdateWithCollections] +END +GO + +IF OBJECT_ID('[dbo].[CollectionUser_UpdateUsers]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[CollectionUser_UpdateUsers] +END +GO + +IF OBJECT_ID('[dbo].[OrganizationUser_CreateWithCollections]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[OrganizationUser_CreateWithCollections] +END +GO + +IF OBJECT_ID('[dbo].[OrganizationUser_UpdateWithCollections]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[OrganizationUser_UpdateWithCollections] +END +GO + +IF OBJECT_ID('[dbo].[Collection_CreateWithGroups]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Collection_CreateWithGroups] +END +GO + +IF OBJECT_ID('[dbo].[Collection_UpdateWithGroups]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Collection_UpdateWithGroups] +END +GO + +IF TYPE_ID('[dbo].[SelectionReadOnlyArray]') IS NOT NULL +BEGIN + DROP TYPE [dbo].[SelectionReadOnlyArray] +END +GO + +CREATE TYPE [dbo].[SelectionReadOnlyArray] AS TABLE ( + [Id] UNIQUEIDENTIFIER NOT NULL, + [ReadOnly] BIT NOT NULL, + [HidePasswords] BIT NOT NULL); +GO + +IF COL_LENGTH('[dbo].[CollectionGroup]', 'HidePasswords') IS NULL +BEGIN + ALTER TABLE + [dbo].[CollectionGroup] + ADD + [HidePasswords] BIT NULL +END +GO + +UPDATE + [dbo].[CollectionGroup] +SET + [HidePasswords] = 0 +GO + +ALTER TABLE + [dbo].[CollectionGroup] +ALTER COLUMN + [HidePasswords] BIT NOT NULL +GO + +IF COL_LENGTH('[dbo].[CollectionUser]', 'HidePasswords') IS NULL +BEGIN + ALTER TABLE + [dbo].[CollectionUser] + ADD + [HidePasswords] BIT NULL +END +GO + +UPDATE + [dbo].[CollectionUser] +SET + [HidePasswords] = 0 +GO + +ALTER TABLE + [dbo].[CollectionUser] +ALTER COLUMN + [HidePasswords] BIT NOT NULL +GO + +IF OBJECT_ID('[dbo].[UserCipherDetails]') IS NOT NULL +BEGIN + DROP FUNCTION [dbo].[UserCipherDetails] +END +GO + +CREATE FUNCTION [dbo].[UserCipherDetails](@UserId UNIQUEIDENTIFIER) +RETURNS TABLE +AS RETURN +WITH [CTE] AS ( + SELECT + [Id], + [OrganizationId], + [AccessAll] + FROM + [OrganizationUser] + WHERE + [UserId] = @UserId + AND [Status] = 2 -- Confirmed +) +SELECT + C.*, + CASE + WHEN + OU.[AccessAll] = 1 + OR G.[AccessAll] = 1 + OR COALESCE(CU.[ReadOnly], CG.[ReadOnly], 0) = 0 + THEN 1 + ELSE 0 + END [Edit], + CASE + WHEN + OU.[AccessAll] = 1 + OR G.[AccessAll] = 1 + OR COALESCE(CU.[HidePasswords], CG.[HidePasswords], 0) = 0 + THEN 1 + ELSE 0 + END [ViewPassword], + CASE + WHEN O.[UseTotp] = 1 + THEN 1 + ELSE 0 + END [OrganizationUseTotp] +FROM + [dbo].[CipherDetails](@UserId) C +INNER JOIN + [CTE] OU ON C.[UserId] IS NULL AND C.[OrganizationId] IN (SELECT [OrganizationId] FROM [CTE]) +INNER JOIN + [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] AND O.[Id] = C.[OrganizationId] AND O.[Enabled] = 1 +LEFT JOIN + [dbo].[CollectionCipher] CC ON OU.[AccessAll] = 0 AND CC.[CipherId] = C.[Id] +LEFT JOIN + [dbo].[CollectionUser] CU ON CU.[CollectionId] = CC.[CollectionId] AND CU.[OrganizationUserId] = OU.[Id] +LEFT JOIN + [dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND OU.[AccessAll] = 0 AND GU.[OrganizationUserId] = OU.[Id] +LEFT JOIN + [dbo].[Group] G ON G.[Id] = GU.[GroupId] +LEFT JOIN + [dbo].[CollectionGroup] CG ON G.[AccessAll] = 0 AND CG.[CollectionId] = CC.[CollectionId] AND CG.[GroupId] = GU.[GroupId] +WHERE + OU.[AccessAll] = 1 + OR CU.[CollectionId] IS NOT NULL + OR G.[AccessAll] = 1 + OR CG.[CollectionId] IS NOT NULL + +UNION ALL + +SELECT + *, + 1 [Edit], + 1 [ViewPassword], + 0 [OrganizationUseTotp] +FROM + [dbo].[CipherDetails](@UserId) +WHERE + [UserId] = @UserId +GO + +IF OBJECT_ID('[dbo].[UserCollectionDetails]') IS NOT NULL +BEGIN + DROP FUNCTION [dbo].[UserCollectionDetails] +END +GO + +CREATE FUNCTION [dbo].[UserCollectionDetails](@UserId UNIQUEIDENTIFIER) +RETURNS TABLE +AS RETURN +SELECT + C.*, + CASE + WHEN + OU.[AccessAll] = 1 + OR G.[AccessAll] = 1 + OR COALESCE(CU.[ReadOnly], CG.[ReadOnly], 0) = 0 + THEN 0 + ELSE 1 + END [ReadOnly], + CASE + WHEN + OU.[AccessAll] = 1 + OR G.[AccessAll] = 1 + OR COALESCE(CU.[HidePasswords], CG.[HidePasswords], 0) = 0 + THEN 0 + ELSE 1 + END [HidePasswords] +FROM + [dbo].[CollectionView] C +INNER JOIN + [dbo].[OrganizationUser] OU ON C.[OrganizationId] = OU.[OrganizationId] +INNER JOIN + [dbo].[Organization] O ON O.[Id] = C.[OrganizationId] +LEFT JOIN + [dbo].[CollectionUser] CU ON OU.[AccessAll] = 0 AND CU.[CollectionId] = C.[Id] AND CU.[OrganizationUserId] = [OU].[Id] +LEFT JOIN + [dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND OU.[AccessAll] = 0 AND GU.[OrganizationUserId] = OU.[Id] +LEFT JOIN + [dbo].[Group] G ON G.[Id] = GU.[GroupId] +LEFT JOIN + [dbo].[CollectionGroup] CG ON G.[AccessAll] = 0 AND CG.[CollectionId] = C.[Id] AND CG.[GroupId] = GU.[GroupId] +WHERE + OU.[UserId] = @UserId + AND OU.[Status] = 2 -- 2 = Confirmed + AND O.[Enabled] = 1 + AND ( + OU.[AccessAll] = 1 + OR CU.[CollectionId] IS NOT NULL + OR G.[AccessAll] = 1 + OR CG.[CollectionId] IS NOT NULL + ) +GO + +IF OBJECT_ID('[dbo].[Collection_ReadWithGroupsById]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Collection_ReadWithGroupsById] +END +GO + +CREATE PROCEDURE [dbo].[Collection_ReadWithGroupsById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[Collection_ReadById] @Id + + SELECT + [GroupId] [Id], + [ReadOnly], + [HidePasswords] + FROM + [dbo].[CollectionGroup] + WHERE + [CollectionId] = @Id +END +GO + +CREATE PROCEDURE [dbo].[Group_CreateWithCollections] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Name NVARCHAR(100), + @AccessAll BIT, + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Collections AS [dbo].[SelectionReadOnlyArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[Group_Create] @Id, @OrganizationId, @Name, @AccessAll, @ExternalId, @CreationDate, @RevisionDate + + ;WITH [AvailableCollectionsCTE] AS( + SELECT + [Id] + FROM + [dbo].[Collection] + WHERE + [OrganizationId] = @OrganizationId + ) + INSERT INTO [dbo].[CollectionGroup] + ( + [CollectionId], + [GroupId], + [ReadOnly], + [HidePasswords] + ) + SELECT + [Id], + @Id, + [ReadOnly], + [HidePasswords] + FROM + @Collections + WHERE + [Id] IN (SELECT [Id] FROM [AvailableCollectionsCTE]) + + EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationId] @OrganizationId +END +GO + +IF OBJECT_ID('[dbo].[Group_ReadWithCollectionsById]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Group_ReadWithCollectionsById] +END +GO + +CREATE PROCEDURE [dbo].[Group_ReadWithCollectionsById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[Group_ReadById] @Id + + SELECT + [CollectionId] [Id], + [ReadOnly], + [HidePasswords] + FROM + [dbo].[CollectionGroup] + WHERE + [GroupId] = @Id +END +GO + +CREATE PROCEDURE [dbo].[Group_UpdateWithCollections] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Name NVARCHAR(100), + @AccessAll BIT, + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Collections AS [dbo].[SelectionReadOnlyArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[Group_Update] @Id, @OrganizationId, @Name, @AccessAll, @ExternalId, @CreationDate, @RevisionDate + + ;WITH [AvailableCollectionsCTE] AS( + SELECT + Id + FROM + [dbo].[Collection] + WHERE + OrganizationId = @OrganizationId + ) + MERGE + [dbo].[CollectionGroup] AS [Target] + USING + @Collections AS [Source] + ON + [Target].[CollectionId] = [Source].[Id] + AND [Target].[GroupId] = @Id + WHEN NOT MATCHED BY TARGET + AND [Source].[Id] IN (SELECT [Id] FROM [AvailableCollectionsCTE]) THEN + INSERT VALUES + ( + [Source].[Id], + @Id, + [Source].[ReadOnly], + [Source].[HidePasswords] + ) + WHEN MATCHED AND ( + [Target].[ReadOnly] != [Source].[ReadOnly] + OR [Target].[HidePasswords] != [Source].[HidePasswords] + ) THEN + UPDATE SET [Target].[ReadOnly] = [Source].[ReadOnly], + [Target].[HidePasswords] = [Source].[HidePasswords] + WHEN NOT MATCHED BY SOURCE + AND [Target].[GroupId] = @Id THEN + DELETE + ; + + EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationId] @OrganizationId +END +GO + +CREATE PROCEDURE [dbo].[CollectionUser_UpdateUsers] + @CollectionId UNIQUEIDENTIFIER, + @Users AS [dbo].[SelectionReadOnlyArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + DECLARE @OrgId UNIQUEIDENTIFIER = ( + SELECT TOP 1 + [OrganizationId] + FROM + [dbo].[Collection] + WHERE + [Id] = @CollectionId + ) + + -- Update + UPDATE + [Target] + SET + [Target].[ReadOnly] = [Source].[ReadOnly], + [Target].[HidePasswords] = [Source].[HidePasswords] + FROM + [dbo].[CollectionUser] [Target] + INNER JOIN + @Users [Source] ON [Source].[Id] = [Target].[OrganizationUserId] + WHERE + [Target].[CollectionId] = @CollectionId + AND ( + [Target].[ReadOnly] != [Source].[ReadOnly] + OR [Target].[HidePasswords] != [Source].[HidePasswords] + ) + + -- Insert + INSERT INTO + [dbo].[CollectionUser] + SELECT + @CollectionId, + [Source].[Id], + [Source].[ReadOnly], + [Source].[HidePasswords] + FROM + @Users [Source] + INNER JOIN + [dbo].[OrganizationUser] OU ON [Source].[Id] = OU.[Id] AND OU.[OrganizationId] = @OrgId + WHERE + NOT EXISTS ( + SELECT + 1 + FROM + [dbo].[CollectionUser] + WHERE + [CollectionId] = @CollectionId + AND [OrganizationUserId] = [Source].[Id] + ) + + -- Delete + DELETE + CU + FROM + [dbo].[CollectionUser] CU + WHERE + CU.[CollectionId] = @CollectionId + AND NOT EXISTS ( + SELECT + 1 + FROM + @Users + WHERE + [Id] = CU.[OrganizationUserId] + ) + + EXEC [dbo].[User_BumpAccountRevisionDateByCollectionId] @CollectionId, @OrgId +END +GO + +IF OBJECT_ID('[dbo].[CollectionUser_ReadByCollectionId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[CollectionUser_ReadByCollectionId] +END +GO + +CREATE PROCEDURE [dbo].[CollectionUser_ReadByCollectionId] + @CollectionId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + [OrganizationUserId] [Id], + [ReadOnly], + [HidePasswords] + FROM + [dbo].[CollectionUser] + WHERE + [CollectionId] = @CollectionId +END +GO + +CREATE PROCEDURE [dbo].[OrganizationUser_CreateWithCollections] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @Email NVARCHAR(50), + @Key VARCHAR(MAX), + @Status TINYINT, + @Type TINYINT, + @AccessAll BIT, + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Collections AS [dbo].[SelectionReadOnlyArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[OrganizationUser_Create] @Id, @OrganizationId, @UserId, @Email, @Key, @Status, @Type, @AccessAll, @ExternalId, @CreationDate, @RevisionDate + + ;WITH [AvailableCollectionsCTE] AS( + SELECT + [Id] + FROM + [dbo].[Collection] + WHERE + [OrganizationId] = @OrganizationId + ) + INSERT INTO [dbo].[CollectionUser] + ( + [CollectionId], + [OrganizationUserId], + [ReadOnly], + [HidePasswords] + ) + SELECT + [Id], + @Id, + [ReadOnly], + [HidePasswords] + FROM + @Collections + WHERE + [Id] IN (SELECT [Id] FROM [AvailableCollectionsCTE]) +END +GO + +IF OBJECT_ID('[dbo].[OrganizationUser_ReadWithCollectionsById]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[OrganizationUser_ReadWithCollectionsById] +END +GO + +CREATE PROCEDURE [dbo].[OrganizationUser_ReadWithCollectionsById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + EXEC [OrganizationUser_ReadById] @Id + + SELECT + CU.[CollectionId] Id, + CU.[ReadOnly], + CU.[HidePasswords] + FROM + [dbo].[OrganizationUser] OU + INNER JOIN + [dbo].[CollectionUser] CU ON OU.[AccessAll] = 0 AND CU.[OrganizationUserId] = [OU].[Id] + WHERE + [OrganizationUserId] = @Id +END +GO + +CREATE PROCEDURE [dbo].[OrganizationUser_UpdateWithCollections] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @Email NVARCHAR(50), + @Key VARCHAR(MAX), + @Status TINYINT, + @Type TINYINT, + @AccessAll BIT, + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Collections AS [dbo].[SelectionReadOnlyArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[OrganizationUser_Update] @Id, @OrganizationId, @UserId, @Email, @Key, @Status, @Type, @AccessAll, @ExternalId, @CreationDate, @RevisionDate + + -- Update + UPDATE + [Target] + SET + [Target].[ReadOnly] = [Source].[ReadOnly], + [Target].[HidePasswords] = [Source].[HidePasswords] + FROM + [dbo].[CollectionUser] AS [Target] + INNER JOIN + @Collections AS [Source] ON [Source].[Id] = [Target].[CollectionId] + WHERE + [Target].[OrganizationUserId] = @Id + AND ( + [Target].[ReadOnly] != [Source].[ReadOnly] + OR [Target].[HidePasswords] != [Source].[HidePasswords] + ) + + -- Insert + INSERT INTO + [dbo].[CollectionUser] + SELECT + [Source].[Id], + @Id, + [Source].[ReadOnly], + [Source].[HidePasswords] + FROM + @Collections AS [Source] + INNER JOIN + [dbo].[Collection] C ON C.[Id] = [Source].[Id] AND C.[OrganizationId] = @OrganizationId + WHERE + NOT EXISTS ( + SELECT + 1 + FROM + [dbo].[CollectionUser] + WHERE + [CollectionId] = [Source].[Id] + AND [OrganizationUserId] = @Id + ) + + -- Delete + DELETE + CU + FROM + [dbo].[CollectionUser] CU + WHERE + CU.[OrganizationUserId] = @Id + AND NOT EXISTS ( + SELECT + 1 + FROM + @Collections + WHERE + [Id] = CU.[CollectionId] + ) +END +GO + +IF OBJECT_ID('[dbo].[OrganizationUserUserDetails_ReadWithCollectionsById]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[OrganizationUserUserDetails_ReadWithCollectionsById] +END +GO + +CREATE PROCEDURE [dbo].[OrganizationUserUserDetails_ReadWithCollectionsById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + EXEC [OrganizationUserUserDetails_ReadById] @Id + + SELECT + CU.[CollectionId] Id, + CU.[ReadOnly], + CU.[HidePasswords] + FROM + [dbo].[OrganizationUser] OU + INNER JOIN + [dbo].[CollectionUser] CU ON OU.[AccessAll] = 0 AND CU.[OrganizationUserId] = [OU].[Id] + WHERE + [OrganizationUserId] = @Id +END +GO + +CREATE PROCEDURE [dbo].[Collection_CreateWithGroups] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Name VARCHAR(MAX), + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Groups AS [dbo].[SelectionReadOnlyArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[Collection_Create] @Id, @OrganizationId, @Name, @ExternalId, @CreationDate, @RevisionDate + + ;WITH [AvailableGroupsCTE] AS( + SELECT + [Id] + FROM + [dbo].[Group] + WHERE + [OrganizationId] = @OrganizationId + ) + INSERT INTO [dbo].[CollectionGroup] + ( + [CollectionId], + [GroupId], + [ReadOnly], + [HidePasswords] + ) + SELECT + @Id, + [Id], + [ReadOnly], + [HidePasswords] + FROM + @Groups + WHERE + [Id] IN (SELECT [Id] FROM [AvailableGroupsCTE]) + + EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationId] @OrganizationId +END +GO + +IF OBJECT_ID('[dbo].[Collection_ReadWithGroupsByIdUserId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Collection_ReadWithGroupsByIdUserId] +END +GO + +CREATE PROCEDURE [dbo].[Collection_ReadWithGroupsByIdUserId] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[Collection_ReadByIdUserId] @Id, @UserId + + SELECT + [GroupId] [Id], + [ReadOnly], + [HidePasswords] + FROM + [dbo].[CollectionGroup] + WHERE + [CollectionId] = @Id +END +GO + +CREATE PROCEDURE [dbo].[Collection_UpdateWithGroups] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Name VARCHAR(MAX), + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Groups AS [dbo].[SelectionReadOnlyArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[Collection_Update] @Id, @OrganizationId, @Name, @ExternalId, @CreationDate, @RevisionDate + + ;WITH [AvailableGroupsCTE] AS( + SELECT + Id + FROM + [dbo].[Group] + WHERE + OrganizationId = @OrganizationId + ) + MERGE + [dbo].[CollectionGroup] AS [Target] + USING + @Groups AS [Source] + ON + [Target].[CollectionId] = @Id + AND [Target].[GroupId] = [Source].[Id] + WHEN NOT MATCHED BY TARGET + AND [Source].[Id] IN (SELECT [Id] FROM [AvailableGroupsCTE]) THEN + INSERT VALUES + ( + @Id, + [Source].[Id], + [Source].[ReadOnly], + [Source].[HidePasswords] + ) + WHEN MATCHED AND ( + [Target].[ReadOnly] != [Source].[ReadOnly] + OR [Target].[HidePasswords] != [Source].[HidePasswords] + ) THEN + UPDATE SET [Target].[ReadOnly] = [Source].[ReadOnly], + [Target].[HidePasswords] = [Source].[HidePasswords] + WHEN NOT MATCHED BY SOURCE + AND [Target].[CollectionId] = @Id THEN + DELETE + ; + + EXEC [dbo].[User_BumpAccountRevisionDateByCollectionId] @Id, @OrganizationId +END +GO + +IF OBJECT_ID('[dbo].[CipherDetails_Update]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[CipherDetails_Update] +END +GO + +CREATE 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), -- 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(2) +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, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate, + [DeletedDate] = @DeletedDate + 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 + +IF OBJECT_ID('[dbo].[CipherDetails_Create]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[CipherDetails_Create] +END +GO + +CREATE 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) +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] + ) + 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 + ) + + 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 + +IF OBJECT_ID('[dbo].[CipherDetails_CreateWithCollections]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[CipherDetails_CreateWithCollections] +END +GO + +CREATE 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), + @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 + + DECLARE @UpdateCollectionsSuccess INT + EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds +END +GO