diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_Create.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_Create.sql index 887f874b00..5db4d5f1f9 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_Create.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_Create.sql @@ -6,7 +6,7 @@ @Key VARCHAR(MAX), @Status SMALLINT, @Type TINYINT, - @AccessAll BIT, + @AccessAll BIT = 0, @ExternalId NVARCHAR(300), @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7), diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateMany.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateMany.sql new file mode 100644 index 0000000000..0b67e0c084 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateMany.sql @@ -0,0 +1,56 @@ +CREATE PROCEDURE [dbo].[OrganizationUser_CreateMany] + @jsonData NVARCHAR(MAX) +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[OrganizationUser] + ( + [Id], + [OrganizationId], + [UserId], + [Email], + [Key], + [Status], + [Type], + [AccessAll], + [ExternalId], + [CreationDate], + [RevisionDate], + [Permissions], + [ResetPasswordKey], + [AccessSecretsManager] + ) + SELECT + OUI.[Id], + OUI.[OrganizationId], + OUI.[UserId], + OUI.[Email], + OUI.[Key], + OUI.[Status], + OUI.[Type], + 0, -- AccessAll will be removed shortly + OUI.[ExternalId], + OUI.[CreationDate], + OUI.[RevisionDate], + OUI.[Permissions], + OUI.[ResetPasswordKey], + OUI.[AccessSecretsManager] + FROM + OPENJSON(@jsonData) + WITH ( + [Id] UNIQUEIDENTIFIER '$.Id', + [OrganizationId] UNIQUEIDENTIFIER '$.OrganizationId', + [UserId] UNIQUEIDENTIFIER '$.UserId', + [Email] NVARCHAR(256) '$.Email', + [Key] VARCHAR(MAX) '$.Key', + [Status] SMALLINT '$.Status', + [Type] TINYINT '$.Type', + [ExternalId] NVARCHAR(300) '$.ExternalId', + [CreationDate] DATETIME2(7) '$.CreationDate', + [RevisionDate] DATETIME2(7) '$.RevisionDate', + [Permissions] NVARCHAR (MAX) '$.Permissions', + [ResetPasswordKey] VARCHAR (MAX) '$.ResetPasswordKey', + [AccessSecretsManager] BIT '$.AccessSecretsManager' + ) OUI +END diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateWithCollections.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateWithCollections.sql index d8245fe1f4..520480d01a 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateWithCollections.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateWithCollections.sql @@ -6,7 +6,7 @@ CREATE PROCEDURE [dbo].[OrganizationUser_CreateWithCollections] @Key VARCHAR(MAX), @Status SMALLINT, @Type TINYINT, - @AccessAll BIT, + @AccessAll BIT = 0, @ExternalId NVARCHAR(300), @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7), diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_Update.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_Update.sql index a4565d7894..297033bade 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_Update.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_Update.sql @@ -6,7 +6,7 @@ @Key VARCHAR(MAX), @Status SMALLINT, @Type TINYINT, - @AccessAll BIT, + @AccessAll BIT = 0, @ExternalId NVARCHAR(300), @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7), diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateMany.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateMany.sql new file mode 100644 index 0000000000..3843c0e310 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateMany.sql @@ -0,0 +1,84 @@ +CREATE PROCEDURE [dbo].[OrganizationUser_UpdateMany] + @jsonData NVARCHAR(MAX) +AS +BEGIN + SET NOCOUNT ON + + -- Parse the JSON string + DECLARE @OrganizationUserInput AS TABLE ( + [Id] UNIQUEIDENTIFIER, + [OrganizationId] UNIQUEIDENTIFIER, + [UserId] UNIQUEIDENTIFIER, + [Email] NVARCHAR(256), + [Key] VARCHAR(MAX), + [Status] SMALLINT, + [Type] TINYINT, + [ExternalId] NVARCHAR(300), + [CreationDate] DATETIME2(7), + [RevisionDate] DATETIME2(7), + [Permissions] NVARCHAR(MAX), + [ResetPasswordKey] VARCHAR(MAX), + [AccessSecretsManager] BIT + ) + + INSERT INTO @OrganizationUserInput + SELECT + [Id], + [OrganizationId], + [UserId], + [Email], + [Key], + [Status], + [Type], + [ExternalId], + [CreationDate], + [RevisionDate], + [Permissions], + [ResetPasswordKey], + [AccessSecretsManager] + FROM OPENJSON(@jsonData) + WITH ( + [Id] UNIQUEIDENTIFIER '$.Id', + [OrganizationId] UNIQUEIDENTIFIER '$.OrganizationId', + [UserId] UNIQUEIDENTIFIER '$.UserId', + [Email] NVARCHAR(256) '$.Email', + [Key] VARCHAR(MAX) '$.Key', + [Status] SMALLINT '$.Status', + [Type] TINYINT '$.Type', + [ExternalId] NVARCHAR(300) '$.ExternalId', + [CreationDate] DATETIME2(7) '$.CreationDate', + [RevisionDate] DATETIME2(7) '$.RevisionDate', + [Permissions] NVARCHAR (MAX) '$.Permissions', + [ResetPasswordKey] VARCHAR (MAX) '$.ResetPasswordKey', + [AccessSecretsManager] BIT '$.AccessSecretsManager' + ) + + -- Perform the update + UPDATE + OU + SET + [OrganizationId] = OUI.[OrganizationId], + [UserId] = OUI.[UserId], + [Email] = OUI.[Email], + [Key] = OUI.[Key], + [Status] = OUI.[Status], + [Type] = OUI.[Type], + [AccessAll] = 0, -- AccessAll will be removed shortly + [ExternalId] = OUI.[ExternalId], + [CreationDate] = OUI.[CreationDate], + [RevisionDate] = OUI.[RevisionDate], + [Permissions] = OUI.[Permissions], + [ResetPasswordKey] = OUI.[ResetPasswordKey], + [AccessSecretsManager] = OUI.[AccessSecretsManager] + FROM + [dbo].[OrganizationUser] OU + INNER JOIN + @OrganizationUserInput OUI ON OU.Id = OUI.Id + + -- Bump account revision dates + EXEC [dbo].[User_BumpManyAccountRevisionDates] + ( + SELECT [UserId] + FROM @OrganizationUserInput + ) +END diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateWithCollections.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateWithCollections.sql index 20797b1442..be3e7d10af 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateWithCollections.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateWithCollections.sql @@ -6,7 +6,7 @@ @Key VARCHAR(MAX), @Status SMALLINT, @Type TINYINT, - @AccessAll BIT, + @AccessAll BIT = 0, @ExternalId NVARCHAR(300), @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7), diff --git a/src/Sql/dbo/Views/OrganizationUserUserDetailsView.sql b/src/Sql/dbo/Views/OrganizationUserUserDetailsView.sql index b1f7689081..ae82a621ca 100644 --- a/src/Sql/dbo/Views/OrganizationUserUserDetailsView.sql +++ b/src/Sql/dbo/Views/OrganizationUserUserDetailsView.sql @@ -11,7 +11,6 @@ SELECT U.[Premium], OU.[Status], OU.[Type], - OU.[AccessAll], OU.[AccessSecretsManager], OU.[ExternalId], SU.[ExternalId] SsoExternalId, diff --git a/util/Migrator/DbScripts/2024-07-24_00_OrganizationUserMakeAccessAllOptional.sql b/util/Migrator/DbScripts/2024-07-24_00_OrganizationUserMakeAccessAllOptional.sql new file mode 100644 index 0000000000..b07d31e883 --- /dev/null +++ b/util/Migrator/DbScripts/2024-07-24_00_OrganizationUserMakeAccessAllOptional.sql @@ -0,0 +1,448 @@ +-- Remove OrganizationUser.AccessAll from all sprocs that read/write it directly + +-- View: don't return the AccessAll value. This is already unused in code. +CREATE OR ALTER VIEW [dbo].[OrganizationUserUserDetailsView] +AS +SELECT + OU.[Id], + OU.[UserId], + OU.[OrganizationId], + U.[Name], + ISNULL(U.[Email], OU.[Email]) Email, + U.[AvatarColor], + U.[TwoFactorProviders], + U.[Premium], + OU.[Status], + OU.[Type], + OU.[AccessSecretsManager], + OU.[ExternalId], + SU.[ExternalId] SsoExternalId, + OU.[Permissions], + OU.[ResetPasswordKey], + U.[UsesKeyConnector], + CASE WHEN U.[MasterPassword] IS NOT NULL THEN 1 ELSE 0 END AS HasMasterPassword +FROM + [dbo].[OrganizationUser] OU +LEFT JOIN + [dbo].[User] U ON U.[Id] = OU.[UserId] +LEFT JOIN + [dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId] +GO + +-- Refresh metadata on sprocs that use the View + +IF OBJECT_ID('[dbo].[OrganizationUser_ReadByMinimumRole]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUser_ReadByMinimumRole]'; +END +GO + +IF OBJECT_ID('[dbo].[OrganizationUserUserDetails_ReadById]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserUserDetails_ReadById]'; +END +GO + +IF OBJECT_ID('[dbo].[OrganizationUserUserDetails_ReadByOrganizationId]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserUserDetails_ReadByOrganizationId]'; +END +GO + +IF OBJECT_ID('[dbo].[OrganizationUser_ReadWithCollectionsById]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUser_ReadWithCollectionsById]'; +END +GO + +-- Sprocs that don't use user-defined types: Give AccessAll a default value so we can remove it from the code +-- before dropping it permanently from the db + +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @OrganizationId UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @Email NVARCHAR(256), + @Key VARCHAR(MAX), + @Status SMALLINT, + @Type TINYINT, + @AccessAll BIT = 0, + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Permissions NVARCHAR(MAX), + @ResetPasswordKey VARCHAR(MAX), + @AccessSecretsManager BIT = 0 +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[OrganizationUser] + ( + [Id], + [OrganizationId], + [UserId], + [Email], + [Key], + [Status], + [Type], + [AccessAll], + [ExternalId], + [CreationDate], + [RevisionDate], + [Permissions], + [ResetPasswordKey], + [AccessSecretsManager] + ) + VALUES + ( + @Id, + @OrganizationId, + @UserId, + @Email, + @Key, + @Status, + @Type, + @AccessAll, + @ExternalId, + @CreationDate, + @RevisionDate, + @Permissions, + @ResetPasswordKey, + @AccessSecretsManager + ) +END +GO + + +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_CreateWithCollections] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @Email NVARCHAR(256), + @Key VARCHAR(MAX), + @Status SMALLINT, + @Type TINYINT, + @AccessAll BIT = 0, + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Permissions NVARCHAR(MAX), + @ResetPasswordKey VARCHAR(MAX), + @Collections AS [dbo].[CollectionAccessSelectionType] READONLY, + @AccessSecretsManager BIT = 0 +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[OrganizationUser_Create] @Id, @OrganizationId, @UserId, @Email, @Key, @Status, @Type, @AccessAll, @ExternalId, @CreationDate, @RevisionDate, @Permissions, @ResetPasswordKey, @AccessSecretsManager + + ;WITH [AvailableCollectionsCTE] AS( + SELECT + [Id] + FROM + [dbo].[Collection] + WHERE + [OrganizationId] = @OrganizationId + ) + INSERT INTO [dbo].[CollectionUser] + ( + [CollectionId], + [OrganizationUserId], + [ReadOnly], + [HidePasswords], + [Manage] + ) + SELECT + [Id], + @Id, + [ReadOnly], + [HidePasswords], + [Manage] + FROM + @Collections + WHERE + [Id] IN (SELECT [Id] FROM [AvailableCollectionsCTE]) +END +GO + + +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_Update] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @Email NVARCHAR(256), + @Key VARCHAR(MAX), + @Status SMALLINT, + @Type TINYINT, + @AccessAll BIT = 0, + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Permissions NVARCHAR(MAX), + @ResetPasswordKey VARCHAR(MAX), + @AccessSecretsManager BIT = 0 +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[OrganizationUser] + SET + [OrganizationId] = @OrganizationId, + [UserId] = @UserId, + [Email] = @Email, + [Key] = @Key, + [Status] = @Status, + [Type] = @Type, + [AccessAll] = @AccessAll, + [ExternalId] = @ExternalId, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate, + [Permissions] = @Permissions, + [ResetPasswordKey] = @ResetPasswordKey, + [AccessSecretsManager] = @AccessSecretsManager + WHERE + [Id] = @Id + + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId +END +GO + + +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_UpdateWithCollections] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @Email NVARCHAR(256), + @Key VARCHAR(MAX), + @Status SMALLINT, + @Type TINYINT, + @AccessAll BIT = 0, + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Permissions NVARCHAR(MAX), + @ResetPasswordKey VARCHAR(MAX), + @Collections AS [dbo].[CollectionAccessSelectionType] READONLY, + @AccessSecretsManager BIT = 0 +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[OrganizationUser_Update] @Id, @OrganizationId, @UserId, @Email, @Key, @Status, @Type, @AccessAll, @ExternalId, @CreationDate, @RevisionDate, @Permissions, @ResetPasswordKey, @AccessSecretsManager + -- Update + UPDATE + [Target] + SET + [Target].[ReadOnly] = [Source].[ReadOnly], + [Target].[HidePasswords] = [Source].[HidePasswords], + [Target].[Manage] = [Source].[Manage] + 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] + OR [Target].[Manage] != [Source].[Manage] + ) + + -- Insert + INSERT INTO [dbo].[CollectionUser] + ( + [CollectionId], + [OrganizationUserId], + [ReadOnly], + [HidePasswords], + [Manage] + ) + SELECT + [Source].[Id], + @Id, + [Source].[ReadOnly], + [Source].[HidePasswords], + [Source].[Manage] + 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 + +-- Sprocs that do use user-defined types: +-- Create a new version of the sproc without using the type, and update that. +-- These were already versioned from a previous update, so take the opportunity to drop the version suffix. + +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_CreateMany] + @jsonData NVARCHAR(MAX) +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[OrganizationUser] + ( + [Id], + [OrganizationId], + [UserId], + [Email], + [Key], + [Status], + [Type], + [AccessAll], + [ExternalId], + [CreationDate], + [RevisionDate], + [Permissions], + [ResetPasswordKey], + [AccessSecretsManager] + ) + SELECT + OUI.[Id], + OUI.[OrganizationId], + OUI.[UserId], + OUI.[Email], + OUI.[Key], + OUI.[Status], + OUI.[Type], + 0, -- AccessAll will be removed shortly + OUI.[ExternalId], + OUI.[CreationDate], + OUI.[RevisionDate], + OUI.[Permissions], + OUI.[ResetPasswordKey], + OUI.[AccessSecretsManager] + FROM + OPENJSON(@jsonData) + WITH ( + [Id] UNIQUEIDENTIFIER '$.Id', + [OrganizationId] UNIQUEIDENTIFIER '$.OrganizationId', + [UserId] UNIQUEIDENTIFIER '$.UserId', + [Email] NVARCHAR(256) '$.Email', + [Key] VARCHAR(MAX) '$.Key', + [Status] SMALLINT '$.Status', + [Type] TINYINT '$.Type', + [ExternalId] NVARCHAR(300) '$.ExternalId', + [CreationDate] DATETIME2(7) '$.CreationDate', + [RevisionDate] DATETIME2(7) '$.RevisionDate', + [Permissions] NVARCHAR (MAX) '$.Permissions', + [ResetPasswordKey] VARCHAR (MAX) '$.ResetPasswordKey', + [AccessSecretsManager] BIT '$.AccessSecretsManager' + ) OUI +END +GO + + +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_UpdateMany] + @jsonData NVARCHAR(MAX) +AS +BEGIN + SET NOCOUNT ON + + -- Parse the JSON string + DECLARE @OrganizationUserInput AS TABLE ( + [Id] UNIQUEIDENTIFIER, + [OrganizationId] UNIQUEIDENTIFIER, + [UserId] UNIQUEIDENTIFIER, + [Email] NVARCHAR(256), + [Key] VARCHAR(MAX), + [Status] SMALLINT, + [Type] TINYINT, + [ExternalId] NVARCHAR(300), + [CreationDate] DATETIME2(7), + [RevisionDate] DATETIME2(7), + [Permissions] NVARCHAR(MAX), + [ResetPasswordKey] VARCHAR(MAX), + [AccessSecretsManager] BIT + ) + + INSERT INTO @OrganizationUserInput + SELECT + [Id], + [OrganizationId], + [UserId], + [Email], + [Key], + [Status], + [Type], + [ExternalId], + [CreationDate], + [RevisionDate], + [Permissions], + [ResetPasswordKey], + [AccessSecretsManager] + FROM OPENJSON(@jsonData) + WITH ( + [Id] UNIQUEIDENTIFIER '$.Id', + [OrganizationId] UNIQUEIDENTIFIER '$.OrganizationId', + [UserId] UNIQUEIDENTIFIER '$.UserId', + [Email] NVARCHAR(256) '$.Email', + [Key] VARCHAR(MAX) '$.Key', + [Status] SMALLINT '$.Status', + [Type] TINYINT '$.Type', + [ExternalId] NVARCHAR(300) '$.ExternalId', + [CreationDate] DATETIME2(7) '$.CreationDate', + [RevisionDate] DATETIME2(7) '$.RevisionDate', + [Permissions] NVARCHAR (MAX) '$.Permissions', + [ResetPasswordKey] VARCHAR (MAX) '$.ResetPasswordKey', + [AccessSecretsManager] BIT '$.AccessSecretsManager' + ) + + -- Perform the update + UPDATE + OU + SET + [OrganizationId] = OUI.[OrganizationId], + [UserId] = OUI.[UserId], + [Email] = OUI.[Email], + [Key] = OUI.[Key], + [Status] = OUI.[Status], + [Type] = OUI.[Type], + [AccessAll] = 0, -- AccessAll will be removed shortly + [ExternalId] = OUI.[ExternalId], + [CreationDate] = OUI.[CreationDate], + [RevisionDate] = OUI.[RevisionDate], + [Permissions] = OUI.[Permissions], + [ResetPasswordKey] = OUI.[ResetPasswordKey], + [AccessSecretsManager] = OUI.[AccessSecretsManager] + FROM + [dbo].[OrganizationUser] OU + INNER JOIN + @OrganizationUserInput OUI ON OU.Id = OUI.Id + + -- Bump account revision dates + EXEC [dbo].[User_BumpManyAccountRevisionDates] + ( + SELECT [UserId] + FROM @OrganizationUserInput + ) +END +GO