mirror of
https://github.com/bitwarden/server.git
synced 2025-04-23 22:15:10 -05:00
[PM-20064] Add cascade deletion for cipher with tasks (#5690)
* add cascade deletion for cipher tasks * add migrations for cascade delete on ciphers and security tasks * remove trailing comma * add SQL migration for PasswordHealthReportApplication - Allow cascade delete when an organization is deleted
This commit is contained in:
parent
90d831d9ef
commit
6809709628
@ -34,6 +34,12 @@ public class NotificationEntityTypeConfiguration : IEntityTypeConfiguration<Noti
|
||||
.HasIndex(n => n.TaskId)
|
||||
.IsClustered(false);
|
||||
|
||||
builder
|
||||
.HasOne(n => n.Task)
|
||||
.WithMany()
|
||||
.HasForeignKey(n => n.TaskId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.ToTable(nameof(Notification));
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,18 @@ public class SecurityTaskEntityTypeConfiguration : IEntityTypeConfiguration<Secu
|
||||
.HasIndex(s => s.CipherId)
|
||||
.IsClustered(false);
|
||||
|
||||
builder
|
||||
.HasOne(p => p.Organization)
|
||||
.WithMany()
|
||||
.HasForeignKey(p => p.OrganizationId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder
|
||||
.HasOne(p => p.Cipher)
|
||||
.WithMany()
|
||||
.HasForeignKey(p => p.CipherId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder
|
||||
.ToTable(nameof(SecurityTask));
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ CREATE TABLE [dbo].[Notification]
|
||||
CONSTRAINT [PK_Notification] PRIMARY KEY CLUSTERED ([Id] ASC),
|
||||
CONSTRAINT [FK_Notification_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]),
|
||||
CONSTRAINT [FK_Notification_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]),
|
||||
CONSTRAINT [FK_Notification_SecurityTask] FOREIGN KEY ([TaskId]) REFERENCES [dbo].[SecurityTask] ([Id])
|
||||
CONSTRAINT [FK_Notification_SecurityTask] FOREIGN KEY ([TaskId]) REFERENCES [dbo].[SecurityTask] ([Id]) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
||||
|
@ -6,7 +6,7 @@ CREATE TABLE [dbo].[PasswordHealthReportApplication]
|
||||
CreationDate DATETIME2(7) NOT NULL,
|
||||
RevisionDate DATETIME2(7) NOT NULL,
|
||||
CONSTRAINT [PK_PasswordHealthReportApplication] PRIMARY KEY CLUSTERED ([Id] ASC),
|
||||
CONSTRAINT [FK_PasswordHealthReportApplication_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]),
|
||||
CONSTRAINT [FK_PasswordHealthReportApplication_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]) ON DELETE CASCADE
|
||||
);
|
||||
GO
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.Text.Json;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
@ -912,4 +913,59 @@ public class CipherRepositoryTests
|
||||
Assert.Equal(CipherType.SecureNote, updatedCipher1.Type);
|
||||
Assert.Equal("new_attachments", updatedCipher2.Attachments);
|
||||
}
|
||||
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task DeleteCipherWithSecurityTaskAsync_Works(
|
||||
IOrganizationRepository organizationRepository,
|
||||
ICipherRepository cipherRepository,
|
||||
ISecurityTaskRepository securityTaskRepository)
|
||||
{
|
||||
var organization = await organizationRepository.CreateAsync(new Organization
|
||||
{
|
||||
Name = "Test Org",
|
||||
PlanType = PlanType.EnterpriseAnnually,
|
||||
Plan = "Test Plan",
|
||||
BillingEmail = ""
|
||||
});
|
||||
|
||||
var cipher1 = new Cipher { Type = CipherType.Login, OrganizationId = organization.Id, Data = "", };
|
||||
await cipherRepository.CreateAsync(cipher1);
|
||||
|
||||
var cipher2 = new Cipher { Type = CipherType.Login, OrganizationId = organization.Id, Data = "", };
|
||||
await cipherRepository.CreateAsync(cipher2);
|
||||
|
||||
var tasks = new List<SecurityTask>
|
||||
{
|
||||
new()
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
CipherId = cipher1.Id,
|
||||
Status = SecurityTaskStatus.Pending,
|
||||
Type = SecurityTaskType.UpdateAtRiskCredential,
|
||||
},
|
||||
new()
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
CipherId = cipher2.Id,
|
||||
Status = SecurityTaskStatus.Completed,
|
||||
Type = SecurityTaskType.UpdateAtRiskCredential,
|
||||
}
|
||||
};
|
||||
|
||||
await securityTaskRepository.CreateManyAsync(tasks);
|
||||
|
||||
// Delete cipher with pending security task
|
||||
await cipherRepository.DeleteAsync(cipher1);
|
||||
|
||||
var deletedCipher1 = await cipherRepository.GetByIdAsync(cipher1.Id);
|
||||
|
||||
Assert.Null(deletedCipher1);
|
||||
|
||||
// Delete cipher with completed security task
|
||||
await cipherRepository.DeleteAsync(cipher2);
|
||||
|
||||
var deletedCipher2 = await cipherRepository.GetByIdAsync(cipher2.Id);
|
||||
|
||||
Assert.Null(deletedCipher2);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = 'FK_Notification_SecurityTask')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Notification]
|
||||
DROP CONSTRAINT [FK_Notification_SecurityTask]
|
||||
END
|
||||
|
||||
ALTER TABLE [dbo].[Notification]
|
||||
ADD CONSTRAINT [FK_Notification_SecurityTask] FOREIGN KEY ([TaskId]) REFERENCES [dbo].[SecurityTask] ([Id]) ON DELETE CASCADE
|
||||
END
|
||||
GO
|
@ -0,0 +1,11 @@
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = 'FK_PasswordHealthReportApplication_Organization')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[PasswordHealthReportApplication]
|
||||
DROP CONSTRAINT [FK_PasswordHealthReportApplication_Organization]
|
||||
END
|
||||
|
||||
ALTER TABLE [dbo].[PasswordHealthReportApplication]
|
||||
ADD CONSTRAINT [FK_PasswordHealthReportApplication_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]) ON DELETE CASCADE
|
||||
END
|
||||
GO
|
3111
util/MySqlMigrations/Migrations/20250422181736_NotificationCascadeDelete.Designer.cs
generated
Normal file
3111
util/MySqlMigrations/Migrations/20250422181736_NotificationCascadeDelete.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.MySqlMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class NotificationCascadeDelete : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Notification_SecurityTask_TaskId",
|
||||
table: "Notification");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Notification_SecurityTask_TaskId",
|
||||
table: "Notification",
|
||||
column: "TaskId",
|
||||
principalTable: "SecurityTask",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Notification_SecurityTask_TaskId",
|
||||
table: "Notification");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Notification_SecurityTask_TaskId",
|
||||
table: "Notification",
|
||||
column: "TaskId",
|
||||
principalTable: "SecurityTask",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
3112
util/MySqlMigrations/Migrations/20250422183835_SecurityTaskCascadeDelete.Designer.cs
generated
Normal file
3112
util/MySqlMigrations/Migrations/20250422183835_SecurityTaskCascadeDelete.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.MySqlMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class SecurityTaskCascadeDelete : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_SecurityTask_Cipher_CipherId",
|
||||
table: "SecurityTask");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_SecurityTask_Cipher_CipherId",
|
||||
table: "SecurityTask",
|
||||
column: "CipherId",
|
||||
principalTable: "Cipher",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_SecurityTask_Cipher_CipherId",
|
||||
table: "SecurityTask");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_SecurityTask_Cipher_CipherId",
|
||||
table: "SecurityTask",
|
||||
column: "CipherId",
|
||||
principalTable: "Cipher",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
@ -2737,7 +2737,8 @@ namespace Bit.MySqlMigrations.Migrations
|
||||
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", "Task")
|
||||
.WithMany()
|
||||
.HasForeignKey("TaskId");
|
||||
.HasForeignKey("TaskId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User")
|
||||
.WithMany()
|
||||
@ -2852,7 +2853,8 @@ namespace Bit.MySqlMigrations.Migrations
|
||||
{
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher")
|
||||
.WithMany()
|
||||
.HasForeignKey("CipherId");
|
||||
.HasForeignKey("CipherId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||
.WithMany()
|
||||
|
3117
util/PostgresMigrations/Migrations/20250422181741_NotificationCascadeDelete.Designer.cs
generated
Normal file
3117
util/PostgresMigrations/Migrations/20250422181741_NotificationCascadeDelete.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.PostgresMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class NotificationCascadeDelete : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Notification_SecurityTask_TaskId",
|
||||
table: "Notification");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Notification_SecurityTask_TaskId",
|
||||
table: "Notification",
|
||||
column: "TaskId",
|
||||
principalTable: "SecurityTask",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Notification_SecurityTask_TaskId",
|
||||
table: "Notification");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Notification_SecurityTask_TaskId",
|
||||
table: "Notification",
|
||||
column: "TaskId",
|
||||
principalTable: "SecurityTask",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
3118
util/PostgresMigrations/Migrations/20250422183847_SecurityTaskCascadeDelete.Designer.cs
generated
Normal file
3118
util/PostgresMigrations/Migrations/20250422183847_SecurityTaskCascadeDelete.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.PostgresMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class SecurityTaskCascadeDelete : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_SecurityTask_Cipher_CipherId",
|
||||
table: "SecurityTask");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_SecurityTask_Cipher_CipherId",
|
||||
table: "SecurityTask",
|
||||
column: "CipherId",
|
||||
principalTable: "Cipher",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_SecurityTask_Cipher_CipherId",
|
||||
table: "SecurityTask");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_SecurityTask_Cipher_CipherId",
|
||||
table: "SecurityTask",
|
||||
column: "CipherId",
|
||||
principalTable: "Cipher",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
@ -2743,7 +2743,8 @@ namespace Bit.PostgresMigrations.Migrations
|
||||
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", "Task")
|
||||
.WithMany()
|
||||
.HasForeignKey("TaskId");
|
||||
.HasForeignKey("TaskId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User")
|
||||
.WithMany()
|
||||
@ -2858,7 +2859,8 @@ namespace Bit.PostgresMigrations.Migrations
|
||||
{
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher")
|
||||
.WithMany()
|
||||
.HasForeignKey("CipherId");
|
||||
.HasForeignKey("CipherId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||
.WithMany()
|
||||
|
3100
util/SqliteMigrations/Migrations/20250422181747_NotificationCascadeDelete.Designer.cs
generated
Normal file
3100
util/SqliteMigrations/Migrations/20250422181747_NotificationCascadeDelete.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.SqliteMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class NotificationCascadeDelete : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Notification_SecurityTask_TaskId",
|
||||
table: "Notification");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Notification_SecurityTask_TaskId",
|
||||
table: "Notification",
|
||||
column: "TaskId",
|
||||
principalTable: "SecurityTask",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Notification_SecurityTask_TaskId",
|
||||
table: "Notification");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Notification_SecurityTask_TaskId",
|
||||
table: "Notification",
|
||||
column: "TaskId",
|
||||
principalTable: "SecurityTask",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
3101
util/SqliteMigrations/Migrations/20250422183841_SecurityTaskCascadeDelete.Designer.cs
generated
Normal file
3101
util/SqliteMigrations/Migrations/20250422183841_SecurityTaskCascadeDelete.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.SqliteMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class SecurityTaskCascadeDelete : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_SecurityTask_Cipher_CipherId",
|
||||
table: "SecurityTask");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_SecurityTask_Cipher_CipherId",
|
||||
table: "SecurityTask",
|
||||
column: "CipherId",
|
||||
principalTable: "Cipher",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_SecurityTask_Cipher_CipherId",
|
||||
table: "SecurityTask");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_SecurityTask_Cipher_CipherId",
|
||||
table: "SecurityTask",
|
||||
column: "CipherId",
|
||||
principalTable: "Cipher",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
@ -2726,7 +2726,8 @@ namespace Bit.SqliteMigrations.Migrations
|
||||
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", "Task")
|
||||
.WithMany()
|
||||
.HasForeignKey("TaskId");
|
||||
.HasForeignKey("TaskId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User")
|
||||
.WithMany()
|
||||
@ -2841,7 +2842,8 @@ namespace Bit.SqliteMigrations.Migrations
|
||||
{
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher")
|
||||
.WithMany()
|
||||
.HasForeignKey("CipherId");
|
||||
.HasForeignKey("CipherId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||
.WithMany()
|
||||
|
Loading…
x
Reference in New Issue
Block a user