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..4bd8d2fbd6 --- /dev/null +++ b/util/Migrator/DbScripts/2020-05-22_00_HiddenPassword.sql @@ -0,0 +1,761 @@ +/* + * Add HiddenPassword support to collections + */ + +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); + +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 CU.[ReadOnly] = 0 + OR G.[AccessAll] = 1 + OR CG.[ReadOnly] = 0 + THEN 1 + ELSE 0 + END [Edit], + CASE + WHEN + CU.[HidePasswords] = 0 + OR CG.[HidePasswords] = 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.*, + COALESCE(CU.[ReadOnly], CG.[ReadOnly], 0) AS [ReadOnly], + COALESCE(CU.[HidePasswords], CG.[HidePasswords], 0) AS [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 + +IF OBJECT_ID('[dbo].[Group_CreateWithCollections]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Group_CreateWithCollections] +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 + +IF OBJECT_ID('[dbo].[Group_UpdateWithCollections]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Group_UpdateWithCollections] +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 + +IF OBJECT_ID('[dbo].[CollectionUser_UpdateUsers]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[CollectionUser_UpdateUsers] +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 + +IF OBJECT_ID('[dbo].[OrganizationUser_CreateWithCollections]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[OrganizationUser_CreateWithCollections] +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 + +IF OBJECT_ID('[dbo].[OrganizationUser_UpdateWithCollections]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[OrganizationUser_UpdateWithCollections] +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 + +IF OBJECT_ID('[dbo].[Collection_CreateWithGroups]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Collection_CreateWithGroups] +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 + +IF OBJECT_ID('[dbo].[Collection_UpdateWithGroups]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Collection_UpdateWithGroups] +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