diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs index f9f2fecd3..96c9a912e 100644 --- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs @@ -199,6 +199,11 @@ public class OrganizationRepository : Repository sa.OrganizationId == organization.Id) .ExecuteDeleteAsync(); + await dbContext.NotificationStatuses.Where(ns => ns.Notification.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + await dbContext.Notifications.Where(n => n.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + // The below section are 3 SPROCS in SQL Server but are only called by here await dbContext.OrganizationApiKeys.Where(oa => oa.OrganizationId == organization.Id) .ExecuteDeleteAsync(); diff --git a/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs b/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs index 6211d6063..735625ce4 100644 --- a/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs @@ -255,6 +255,8 @@ public class UserRepository : Repository, IUserR dbContext.EmergencyAccesses.RemoveRange( dbContext.EmergencyAccesses.Where(ea => ea.GrantorId == user.Id || ea.GranteeId == user.Id)); dbContext.Sends.RemoveRange(dbContext.Sends.Where(s => s.UserId == user.Id)); + dbContext.NotificationStatuses.RemoveRange(dbContext.NotificationStatuses.Where(ns => ns.UserId == user.Id)); + dbContext.Notifications.RemoveRange(dbContext.Notifications.Where(n => n.UserId == user.Id)); var mappedUser = Mapper.Map(user); dbContext.Users.Remove(mappedUser); diff --git a/src/Sql/NotificationCenter/dbo/Tables/NotificationStatus.sql b/src/Sql/NotificationCenter/dbo/Tables/NotificationStatus.sql index 0084b9ccf..2f68e2b2f 100644 --- a/src/Sql/NotificationCenter/dbo/Tables/NotificationStatus.sql +++ b/src/Sql/NotificationCenter/dbo/Tables/NotificationStatus.sql @@ -5,5 +5,11 @@ CREATE TABLE [dbo].[NotificationStatus] [ReadDate] DATETIME2 (7) NULL, [DeletedDate] DATETIME2 (7) NULL, CONSTRAINT [PK_NotificationStatus] PRIMARY KEY CLUSTERED ([NotificationId] ASC, [UserId] ASC), + CONSTRAINT [FK_NotificationStatus_Notification] FOREIGN KEY ([NotificationId]) REFERENCES [dbo].[Notification] ([Id]), CONSTRAINT [FK_NotificationStatus_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) ); + +GO +CREATE NONCLUSTERED INDEX [IX_NotificationStatus_UserId] + ON [dbo].[NotificationStatus]([UserId] ASC); + diff --git a/src/Sql/dbo/Stored Procedures/Organization_DeleteById.sql b/src/Sql/dbo/Stored Procedures/Organization_DeleteById.sql index e35947567..e0b7d4746 100644 --- a/src/Sql/dbo/Stored Procedures/Organization_DeleteById.sql +++ b/src/Sql/dbo/Stored Procedures/Organization_DeleteById.sql @@ -119,6 +119,23 @@ BEGIN WHERE [OrganizationId] = @Id + -- Delete Notification Status + DELETE + NS + FROM + [dbo].[NotificationStatus] NS + INNER JOIN + [dbo].[Notification] N ON N.[Id] = NS.[NotificationId] + WHERE + N.[OrganizationId] = @Id + + -- Delete Notification + DELETE + FROM + [dbo].[Notification] + WHERE + [OrganizationId] = @Id + DELETE FROM [dbo].[Organization] diff --git a/src/Sql/dbo/Stored Procedures/User_DeleteById.sql b/src/Sql/dbo/Stored Procedures/User_DeleteById.sql index 266d5e0bc..0608982e3 100644 --- a/src/Sql/dbo/Stored Procedures/User_DeleteById.sql +++ b/src/Sql/dbo/Stored Procedures/User_DeleteById.sql @@ -119,6 +119,20 @@ BEGIN WHERE [UserId] = @Id + -- Delete Notification Status + DELETE + FROM + [dbo].[NotificationStatus] + WHERE + [UserId] = @Id + + -- Delete Notification + DELETE + FROM + [dbo].[Notification] + WHERE + [UserId] = @Id + -- Finally, delete the user DELETE FROM diff --git a/util/Migrator/DbScripts/2024-09-17_00_UpdateNotificationDeleteCascades.sql b/util/Migrator/DbScripts/2024-09-17_00_UpdateNotificationDeleteCascades.sql new file mode 100644 index 000000000..2703c09d5 --- /dev/null +++ b/util/Migrator/DbScripts/2024-09-17_00_UpdateNotificationDeleteCascades.sql @@ -0,0 +1,319 @@ +-- NotificationStatus + +IF OBJECT_ID('[dbo].[FK_NotificationStatus_Notification]', 'F') IS NOT NULL + BEGIN + ALTER TABLE [dbo].[NotificationStatus] + DROP CONSTRAINT [FK_NotificationStatus_Notification] + END +GO + +ALTER TABLE [dbo].[NotificationStatus] + ADD CONSTRAINT [FK_NotificationStatus_Notification] FOREIGN KEY ([NotificationId]) REFERENCES [dbo].[Notification] ([Id]) +GO + +IF NOT EXISTS(SELECT name + FROM sys.indexes + WHERE name = 'IX_NotificationStatus_UserId') + BEGIN + CREATE NONCLUSTERED INDEX [IX_NotificationStatus_UserId] + ON [dbo].[NotificationStatus] ([UserId] ASC); + END +GO + +-- Stored Procedure Organization_DeleteById + +CREATE OR ALTER PROCEDURE [dbo].[Organization_DeleteById] +@Id UNIQUEIDENTIFIER + WITH RECOMPILE +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationId] @Id + + DECLARE @BatchSize INT = 100 + WHILE @BatchSize > 0 + BEGIN + BEGIN TRANSACTION Organization_DeleteById_Ciphers + + DELETE TOP(@BatchSize) + FROM + [dbo].[Cipher] + WHERE + [UserId] IS NULL + AND [OrganizationId] = @Id + + SET @BatchSize = @@ROWCOUNT + + COMMIT TRANSACTION Organization_DeleteById_Ciphers + END + + BEGIN TRANSACTION Organization_DeleteById + + DELETE + FROM + [dbo].[AuthRequest] + WHERE + [OrganizationId] = @Id + + DELETE + FROM + [dbo].[SsoUser] + WHERE + [OrganizationId] = @Id + + DELETE + FROM + [dbo].[SsoConfig] + WHERE + [OrganizationId] = @Id + + DELETE CU + FROM + [dbo].[CollectionUser] CU + INNER JOIN + [dbo].[OrganizationUser] OU ON [CU].[OrganizationUserId] = [OU].[Id] + WHERE + [OU].[OrganizationId] = @Id + + DELETE AP + FROM + [dbo].[AccessPolicy] AP + INNER JOIN + [dbo].[OrganizationUser] OU ON [AP].[OrganizationUserId] = [OU].[Id] + WHERE + [OU].[OrganizationId] = @Id + + DELETE GU + FROM + [dbo].[GroupUser] GU + INNER JOIN + [dbo].[OrganizationUser] OU ON [GU].[OrganizationUserId] = [OU].[Id] + WHERE + [OU].[OrganizationId] = @Id + + DELETE + FROM + [dbo].[OrganizationUser] + WHERE + [OrganizationId] = @Id + + DELETE + FROM + [dbo].[ProviderOrganization] + WHERE + [OrganizationId] = @Id + + EXEC [dbo].[OrganizationApiKey_OrganizationDeleted] @Id + EXEC [dbo].[OrganizationConnection_OrganizationDeleted] @Id + EXEC [dbo].[OrganizationSponsorship_OrganizationDeleted] @Id + EXEC [dbo].[OrganizationDomain_OrganizationDeleted] @Id + + DELETE + FROM + [dbo].[Project] + WHERE + [OrganizationId] = @Id + + DELETE + FROM + [dbo].[Secret] + WHERE + [OrganizationId] = @Id + + DELETE AK + FROM + [dbo].[ApiKey] AK + INNER JOIN + [dbo].[ServiceAccount] SA ON [AK].[ServiceAccountId] = [SA].[Id] + WHERE + [SA].[OrganizationId] = @Id + + DELETE AP + FROM + [dbo].[AccessPolicy] AP + INNER JOIN + [dbo].[ServiceAccount] SA ON [AP].[GrantedServiceAccountId] = [SA].[Id] + WHERE + [SA].[OrganizationId] = @Id + + DELETE + FROM + [dbo].[ServiceAccount] + WHERE + [OrganizationId] = @Id + + -- Delete Notification Status + DELETE + NS + FROM + [dbo].[NotificationStatus] NS + INNER JOIN + [dbo].[Notification] N ON N.[Id] = NS.[NotificationId] + WHERE + N.[OrganizationId] = @Id + + -- Delete Notification + DELETE + FROM + [dbo].[Notification] + WHERE + [OrganizationId] = @Id + + DELETE + FROM + [dbo].[Organization] + WHERE + [Id] = @Id + + COMMIT TRANSACTION Organization_DeleteById +END +GO + +-- Stored Procedure User_DeleteById + +CREATE OR ALTER PROCEDURE [dbo].[User_DeleteById] +@Id UNIQUEIDENTIFIER + WITH RECOMPILE +AS +BEGIN + SET NOCOUNT ON + DECLARE @BatchSize INT = 100 + + -- Delete ciphers + WHILE @BatchSize > 0 + BEGIN + BEGIN TRANSACTION User_DeleteById_Ciphers + + DELETE TOP(@BatchSize) + FROM + [dbo].[Cipher] + WHERE + [UserId] = @Id + + SET @BatchSize = @@ROWCOUNT + + COMMIT TRANSACTION User_DeleteById_Ciphers + END + + BEGIN TRANSACTION User_DeleteById + + -- Delete WebAuthnCredentials + DELETE + FROM + [dbo].[WebAuthnCredential] + WHERE + [UserId] = @Id + + -- Delete folders + DELETE + FROM + [dbo].[Folder] + WHERE + [UserId] = @Id + + -- Delete AuthRequest, must be before Device + DELETE + FROM + [dbo].[AuthRequest] + WHERE + [UserId] = @Id + + -- Delete devices + DELETE + FROM + [dbo].[Device] + WHERE + [UserId] = @Id + + -- Delete collection users + DELETE + CU + FROM + [dbo].[CollectionUser] CU + INNER JOIN + [dbo].[OrganizationUser] OU ON OU.[Id] = CU.[OrganizationUserId] + WHERE + OU.[UserId] = @Id + + -- Delete group users + DELETE + GU + FROM + [dbo].[GroupUser] GU + INNER JOIN + [dbo].[OrganizationUser] OU ON OU.[Id] = GU.[OrganizationUserId] + WHERE + OU.[UserId] = @Id + + -- Delete AccessPolicy + DELETE + AP + FROM + [dbo].[AccessPolicy] AP + INNER JOIN + [dbo].[OrganizationUser] OU ON OU.[Id] = AP.[OrganizationUserId] + WHERE + [UserId] = @Id + + -- Delete organization users + DELETE + FROM + [dbo].[OrganizationUser] + WHERE + [UserId] = @Id + + -- Delete provider users + DELETE + FROM + [dbo].[ProviderUser] + WHERE + [UserId] = @Id + + -- Delete SSO Users + DELETE + FROM + [dbo].[SsoUser] + WHERE + [UserId] = @Id + + -- Delete Emergency Accesses + DELETE + FROM + [dbo].[EmergencyAccess] + WHERE + [GrantorId] = @Id + OR + [GranteeId] = @Id + + -- Delete Sends + DELETE + FROM + [dbo].[Send] + WHERE + [UserId] = @Id + + -- Delete Notification Status + DELETE + FROM + [dbo].[NotificationStatus] + WHERE + [UserId] = @Id + + -- Delete Notification + DELETE + FROM + [dbo].[Notification] + WHERE + [UserId] = @Id + + -- Finally, delete the user + DELETE + FROM + [dbo].[User] + WHERE + [Id] = @Id + + COMMIT TRANSACTION User_DeleteById +END +GO