From fa3f1ad0cee38b41cbedfe58d207eee11cc33890 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Wed, 24 Nov 2021 08:26:11 -0600 Subject: [PATCH] Null out sponsorship values when foreign key deleted (#1733) This allows us to maintain record of sponsorships up until they are explicitly removed. Fixes issues where removing sponsorships from organizations with invalid sponsorships would error --- .../EntityFramework/OrganizationRepository.cs | 3 +- .../OrganizationUserRepository.cs | 12 ++- ...izationSponsorship_OrganizationDeleted.sql | 14 +-- ...ionSponsorship_OrganizationUserDeleted.sql | 7 +- ...onSponsorship_OrganizationUsersDeleted.sql | 25 ++---- ..._NullOrganizationSponsorshipOnFkDelete.sql | 86 +++++++++++++++++++ 6 files changed, 112 insertions(+), 35 deletions(-) create mode 100644 util/Migrator/DbScripts/2021-11-23_00_NullOrganizationSponsorshipOnFkDelete.sql diff --git a/src/Core/Repositories/EntityFramework/OrganizationRepository.cs b/src/Core/Repositories/EntityFramework/OrganizationRepository.cs index 3c73c646df..76f86fa1fd 100644 --- a/src/Core/Repositories/EntityFramework/OrganizationRepository.cs +++ b/src/Core/Repositories/EntityFramework/OrganizationRepository.cs @@ -107,10 +107,9 @@ namespace Bit.Core.Repositories.EntityFramework .Where(os => os.SponsoringOrganizationId == organization.Id || os.SponsoredOrganizationId == organization.Id); - dbContext.RemoveRange(sponsorships.Where(os => os.CloudSponsor)); Guid? UpdatedOrgId(Guid? orgId) => orgId == organization.Id ? null : organization.Id; - foreach (var sponsorship in sponsorships.Where(os => !os.CloudSponsor)) + foreach (var sponsorship in sponsorships) { sponsorship.SponsoredOrganizationId = UpdatedOrgId(sponsorship.SponsoredOrganizationId); sponsorship.SponsoringOrganizationId = UpdatedOrgId(sponsorship.SponsoringOrganizationId); diff --git a/src/Core/Repositories/EntityFramework/OrganizationUserRepository.cs b/src/Core/Repositories/EntityFramework/OrganizationUserRepository.cs index 8cc3f4f2eb..7e991994d7 100644 --- a/src/Core/Repositories/EntityFramework/OrganizationUserRepository.cs +++ b/src/Core/Repositories/EntityFramework/OrganizationUserRepository.cs @@ -77,7 +77,11 @@ namespace Bit.Core.Repositories.EntityFramework var sponsorships = dbContext.OrganizationSponsorships .Where(os => os.SponsoringOrganizationUserId != default && os.SponsoringOrganizationUserId.Value == organizationUserId); - dbContext.RemoveRange(sponsorships); + foreach (var sponsorship in sponsorships) + { + sponsorship.SponsoringOrganizationUserId = null; + } + dbContext.Remove(orgUser); await dbContext.SaveChangesAsync(); } @@ -92,7 +96,11 @@ namespace Bit.Core.Repositories.EntityFramework var sponsorships = dbContext.OrganizationSponsorships .Where(os => os.SponsoringOrganizationUserId != default && organizationUserIds.Contains(os.SponsoringOrganizationUserId ?? default)); - dbContext.RemoveRange(sponsorships); + foreach (var sponsorship in sponsorships) + { + sponsorship.SponsoringOrganizationUserId = null; + } + dbContext.RemoveRange(entities); await dbContext.SaveChangesAsync(); } diff --git a/src/Sql/dbo/Stored Procedures/OrganizationSponsorship_OrganizationDeleted.sql b/src/Sql/dbo/Stored Procedures/OrganizationSponsorship_OrganizationDeleted.sql index f7685b7e17..3b60ccbea4 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationSponsorship_OrganizationDeleted.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationSponsorship_OrganizationDeleted.sql @@ -9,23 +9,13 @@ BEGIN SET [SponsoringOrganizationId] = NULL WHERE - [SponsoringOrganizationId] = @OrganizationId AND - [CloudSponsor] = 0 + [SponsoringOrganizationId] = @OrganizationId UPDATE [dbo].[OrganizationSponsorship] SET [SponsoredOrganizationId] = NULL WHERE - [SponsoredOrganizationId] = @OrganizationId AND - [CloudSponsor] = 0 - - DELETE - FROM - [dbo].[OrganizationSponsorship] - WHERE - [CloudSponsor] = 1 AND - ([SponsoredOrganizationId] = @OrganizationId OR - [SponsoringOrganizationId] = @OrganizationId) + [SponsoredOrganizationId] = @OrganizationId END GO diff --git a/src/Sql/dbo/Stored Procedures/OrganizationSponsorship_OrganizationUserDeleted.sql b/src/Sql/dbo/Stored Procedures/OrganizationSponsorship_OrganizationUserDeleted.sql index a324b76d32..3722dad8b7 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationSponsorship_OrganizationUserDeleted.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationSponsorship_OrganizationUserDeleted.sql @@ -4,9 +4,12 @@ AS BEGIN SET NOCOUNT ON - DELETE + UPDATE + OS + SET + [SponsoringOrganizationUserId] = NULL FROM - [dbo].[OrganizationSponsorship] + [dbo].[OrganizationSponsorship] OS WHERE [SponsoringOrganizationUserId] = @OrganizationUserId END diff --git a/src/Sql/dbo/Stored Procedures/OrganizationSponsorship_OrganizationUsersDeleted.sql b/src/Sql/dbo/Stored Procedures/OrganizationSponsorship_OrganizationUsersDeleted.sql index 97f1e30438..10c8ee77bf 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationSponsorship_OrganizationUsersDeleted.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationSponsorship_OrganizationUsersDeleted.sql @@ -4,22 +4,13 @@ AS BEGIN SET NOCOUNT ON - DECLARE @BatchSize AS INT; - SET @BatchSize = 100; - - WHILE @BatchSize > 0 - BEGIN - BEGIN TRANSACTION OrganizationSponsorship_DeleteOUs - - DELETE TOP(@BatchSize) OS - FROM - [dbo].[OrganizationSponsorship] OS - INNER JOIN - @SponsoringOrganizationUserIds I ON I.Id = OS.SponsoringOrganizationUserId - - SET @BatchSize = @@ROWCOUNT - - COMMIT TRANSACTION OrganizationSponsorship_DeleteOUs - END + UPDATE + OS + SET + [SponsoringOrganizationUserId] = NULL + FROM + [dbo].[OrganizationSponsorship] OS + INNER JOIN + @SponsoringOrganizationUserIds I ON I.Id = OS.SponsoringOrganizationUserId END GO diff --git a/util/Migrator/DbScripts/2021-11-23_00_NullOrganizationSponsorshipOnFkDelete.sql b/util/Migrator/DbScripts/2021-11-23_00_NullOrganizationSponsorshipOnFkDelete.sql new file mode 100644 index 0000000000..cc98448a4b --- /dev/null +++ b/util/Migrator/DbScripts/2021-11-23_00_NullOrganizationSponsorshipOnFkDelete.sql @@ -0,0 +1,86 @@ +-- OrganizationSponsorship_OrganizationDeleted +IF OBJECT_ID('[dbo].[OrganizationSponsorship_OrganizationDeleted]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[OrganizationSponsorship_OrganizationDeleted] +END +GO + +CREATE PROCEDURE [dbo].[OrganizationSponsorship_OrganizationDeleted] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[OrganizationSponsorship] + SET + [SponsoringOrganizationId] = NULL + WHERE + [SponsoringOrganizationId] = @OrganizationId AND + [CloudSponsor] = 0 + + UPDATE + [dbo].[OrganizationSponsorship] + SET + [SponsoredOrganizationId] = NULL + WHERE + [SponsoredOrganizationId] = @OrganizationId AND + [CloudSponsor] = 0 + + DELETE + FROM + [dbo].[OrganizationSponsorship] + WHERE + [CloudSponsor] = 1 AND + ([SponsoredOrganizationId] = @OrganizationId OR + [SponsoringOrganizationId] = @OrganizationId) +END +GO + +-- OrganizationSponsorship_OrganizationUserDeleted +IF OBJECT_ID('[dbo].[OrganizationSponsorship_OrganizationUserDeleted]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[OrganizationSponsorship_OrganizationUserDeleted] +END +GO + +CREATE PROCEDURE [dbo].[OrganizationSponsorship_OrganizationUserDeleted] + @OrganizationUserId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + UPDATE + OS + SET + [SponsoringOrganizationUserId] = NULL + FROM + [dbo].[OrganizationSponsorship] OS + WHERE + [SponsoringOrganizationUserId] = @OrganizationUserId +END +GO + +-- OrganizationSponsorship_OrganizationUsersDeleted +IF OBJECT_ID('[dbo].[OrganizationSponsorship_OrganizationUsersDeleted]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[OrganizationSponsorship_OrganizationUsersDeleted] +END +GO + +CREATE PROCEDURE [dbo].[OrganizationSponsorship_OrganizationUsersDeleted] + @SponsoringOrganizationUserIds [dbo].[GuidIdArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + UPDATE + OS + SET + [SponsoringOrganizationUserId] = NULL + FROM + [dbo].[OrganizationSponsorship] OS + INNER JOIN + @SponsoringOrganizationUserIds I ON I.Id = OS.SponsoringOrganizationUserId +END +GO