From 8c9dc4818d99e3f7787d5443baf74a7fbce8e23b Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Oct 2026 13:22:44 -0400 Subject: [PATCH 01/20] [PM-41931] feat(dirt): Add ReportExclusions column to Cipher Per-user JSON map of cipher report exclusions, a sibling of Archives. Vault write procedures accept the new parameter but never write it, so saving a cipher cannot erase another user's exclusion. --- src/Core/Vault/Entities/Cipher.cs | 1 + .../Cipher/Cipher_Create.sql | 3 +- .../Cipher/Cipher_CreateWithCollections.sql | 1 + .../Cipher/Cipher_Update.sql | 3 +- .../Cipher/Cipher_UpdateWithCollections.sql | 1 + src/Sql/dbo/Vault/Tables/Cipher.sql | 1 + ...026-10-21_00_AddCipherReportExclusions.sql | 223 + ...1939_AddCipherReportExclusions.Designer.cs | 4629 ++++++++++++++++ ...0261021171939_AddCipherReportExclusions.cs | 28 + .../DatabaseContextModelSnapshot.cs | 3 + ...1844_AddCipherReportExclusions.Designer.cs | 4635 +++++++++++++++++ ...0261021171844_AddCipherReportExclusions.cs | 27 + .../DatabaseContextModelSnapshot.cs | 3 + ...1852_AddCipherReportExclusions.Designer.cs | 4618 ++++++++++++++++ ...0261021171852_AddCipherReportExclusions.cs | 27 + .../DatabaseContextModelSnapshot.cs | 3 + 16 files changed, 14204 insertions(+), 2 deletions(-) create mode 100644 util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql create mode 100644 util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.Designer.cs create mode 100644 util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.cs create mode 100644 util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.Designer.cs create mode 100644 util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.cs create mode 100644 util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.Designer.cs create mode 100644 util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.cs diff --git a/src/Core/Vault/Entities/Cipher.cs b/src/Core/Vault/Entities/Cipher.cs index 08562e5dfdec..fdc68ede867e 100644 --- a/src/Core/Vault/Entities/Cipher.cs +++ b/src/Core/Vault/Entities/Cipher.cs @@ -27,6 +27,7 @@ public class Cipher : ITableObject, ICloneable public Enums.CipherRepromptType? Reprompt { get; set; } public string Key { get; set; } public string Archives { get; set; } + public string ReportExclusions { get; set; } public void SetNewId() { diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Create.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Create.sql index 68e7b11c0710..ce3c00b3a9de 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Create.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Create.sql @@ -12,7 +12,8 @@ @DeletedDate DATETIME2(7), @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, - @Archives NVARCHAR(MAX) = NULL + @Archives NVARCHAR(MAX) = NULL, + @ReportExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN SET NOCOUNT ON diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql index 071bf82f87cd..6b465649b2f1 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql @@ -13,6 +13,7 @@ @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, @Archives NVARCHAR(MAX) = NULL, + @ReportExclusions NVARCHAR(MAX) = NULL, -- not used @CollectionIds AS [dbo].[GuidIdArray] READONLY AS BEGIN diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Update.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Update.sql index e92a8147eac1..b4764b11dd9d 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Update.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Update.sql @@ -12,7 +12,8 @@ @DeletedDate DATETIME2(7), @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, - @Archives NVARCHAR(MAX) = NULL + @Archives NVARCHAR(MAX) = NULL, + @ReportExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN SET NOCOUNT ON diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql index c33d49e92ec2..972159674393 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql @@ -13,6 +13,7 @@ @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, @Archives NVARCHAR(MAX) = NULL, + @ReportExclusions NVARCHAR(MAX) = NULL, -- not used @CollectionIds AS [dbo].[GuidIdArray] READONLY AS BEGIN diff --git a/src/Sql/dbo/Vault/Tables/Cipher.sql b/src/Sql/dbo/Vault/Tables/Cipher.sql index 75ebc1d61803..f02257705438 100644 --- a/src/Sql/dbo/Vault/Tables/Cipher.sql +++ b/src/Sql/dbo/Vault/Tables/Cipher.sql @@ -15,6 +15,7 @@ CREATE TABLE [dbo].[Cipher] ( [Key] VARCHAR(MAX) NULL, [ArchivedDate] DATETIME2 (7) NULL, [Archives] NVARCHAR(MAX) NULL, + [ReportExclusions] NVARCHAR(MAX) NULL, CONSTRAINT [PK_Cipher] PRIMARY KEY CLUSTERED ([Id] ASC), CONSTRAINT [FK_Cipher_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]), CONSTRAINT [FK_Cipher_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) diff --git a/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql b/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql new file mode 100644 index 000000000000..1fbfebeb7094 --- /dev/null +++ b/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql @@ -0,0 +1,223 @@ +IF COL_LENGTH('[dbo].[Cipher]', 'ReportExclusions') IS NULL +BEGIN + ALTER TABLE [dbo].[Cipher] + ADD [ReportExclusions] NVARCHAR(MAX) NULL; +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[Cipher_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), + @Folders NVARCHAR(MAX), + @Attachments NVARCHAR(MAX), -- not used + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @DeletedDate DATETIME2(7), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, + @Archives NVARCHAR(MAX) = NULL, + @ReportExclusions NVARCHAR(MAX) = NULL -- not used +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[Cipher] + ( + [Id], + [UserId], + [OrganizationId], + [Type], + [Data], + [Favorites], + [Folders], + [CreationDate], + [RevisionDate], + [DeletedDate], + [Reprompt], + [Key], + [Archives] + ) + VALUES + ( + @Id, + CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END, + @OrganizationId, + @Type, + @Data, + @Favorites, + @Folders, + @CreationDate, + @RevisionDate, + @DeletedDate, + @Reprompt, + @Key, + @Archives + ) + + IF @OrganizationId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId + END + ELSE IF @UserId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId + END +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[Cipher_Update] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), + @Folders NVARCHAR(MAX), + @Attachments NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @DeletedDate DATETIME2(7), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, + @Archives NVARCHAR(MAX) = NULL, + @ReportExclusions NVARCHAR(MAX) = NULL -- not used +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[Cipher] + SET + [UserId] = CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END, + [OrganizationId] = @OrganizationId, + [Type] = @Type, + [Data] = @Data, + [Favorites] = @Favorites, + [Folders] = @Folders, + [Attachments] = @Attachments, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate, + [DeletedDate] = @DeletedDate, + [Reprompt] = @Reprompt, + [Key] = @Key, + [Archives] = @Archives + WHERE + [Id] = @Id + + IF @OrganizationId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId + END + ELSE IF @UserId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId + END +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[Cipher_CreateWithCollections] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), + @Folders NVARCHAR(MAX), + @Attachments NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @DeletedDate DATETIME2(7), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, + @Archives NVARCHAR(MAX) = NULL, + @ReportExclusions NVARCHAR(MAX) = NULL, -- not used + @CollectionIds AS [dbo].[GuidIdArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[Cipher_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders, + @Attachments, @CreationDate, @RevisionDate, @DeletedDate, @Reprompt, @Key, @Archives + + DECLARE @UpdateCollectionsSuccess INT + EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds + + -- Bump the account revision date AFTER collections are assigned. + IF @UpdateCollectionsSuccess = 0 + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId + END +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[Cipher_UpdateWithCollections] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), + @Folders NVARCHAR(MAX), + @Attachments NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @DeletedDate DATETIME2(7), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, + @Archives NVARCHAR(MAX) = NULL, + @ReportExclusions NVARCHAR(MAX) = NULL, -- not used + @CollectionIds AS [dbo].[GuidIdArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + BEGIN TRANSACTION Cipher_UpdateWithCollections + + DECLARE @UpdateCollectionsSuccess INT + EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds + + IF @UpdateCollectionsSuccess < 0 + BEGIN + COMMIT TRANSACTION Cipher_UpdateWithCollections + SELECT -1 -- -1 = Failure + RETURN + END + + UPDATE + [dbo].[Cipher] + SET + [UserId] = NULL, + [OrganizationId] = @OrganizationId, + [Data] = @Data, + [Attachments] = @Attachments, + [RevisionDate] = @RevisionDate, + [DeletedDate] = @DeletedDate, + [Key] = @Key, + [Folders] = @Folders, + [Favorites] = @Favorites, + [Reprompt] = @Reprompt, + [Archives] = @Archives + -- No need to update CreationDate or Type since that data will not change + WHERE + [Id] = @Id + + COMMIT TRANSACTION Cipher_UpdateWithCollections + + IF @Attachments IS NOT NULL + BEGIN + EXEC [dbo].[Organization_UpdateStorage] @OrganizationId + EXEC [dbo].[User_UpdateStorage] @UserId + END + + EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId + + SELECT 0 -- 0 = Success +END +GO + +EXECUTE sp_refreshview N'[dbo].[CipherView]' +GO diff --git a/util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.Designer.cs b/util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.Designer.cs new file mode 100644 index 000000000000..54c8f45ffc57 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.Designer.cs @@ -0,0 +1,4629 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20261021171939_AddCipherReportExclusions")] + partial class AddCipherReportExclusions + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Bit.Core.Dirt.Reports.Models.Data.OrganizationMemberBaseDetail", b => + { + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CollectionName") + .HasColumnType("longtext"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("GroupName") + .HasColumnType("longtext"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("Manage") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.Property("ResetPasswordKey") + .HasColumnType("longtext"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("UserGuid") + .HasColumnType("char(36)"); + + b.Property("UserName") + .HasColumnType("longtext"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.ToTable("OrganizationMemberBaseDetails"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessRuleId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DefaultUserCollectionEmail") + .HasColumnType("longtext"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AccessRuleId"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("Manage") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("Manage") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AllowAdminAccessToAllCollectionItems") + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("BillingEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExemptFromBillingAutomation") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("LimitCollectionCreation") + .HasColumnType("tinyint(1)"); + + b.Property("LimitCollectionDeletion") + .HasColumnType("tinyint(1)"); + + b.Property("LimitItemDeletion") + .HasColumnType("tinyint(1)"); + + b.Property("MaxAutoscalePamSeats") + .HasColumnType("int"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxAutoscaleSmSeats") + .HasColumnType("int"); + + b.Property("MaxAutoscaleSmServiceAccounts") + .HasColumnType("int"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationKeyId") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("datetime(6)"); + + b.Property("PamSeatMinimum") + .HasColumnType("int"); + + b.Property("PamSeats") + .HasColumnType("int"); + + b.Property("Plan") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Seats") + .HasColumnType("int"); + + b.Property("SelfHost") + .HasColumnType("tinyint(1)"); + + b.Property("SmSeats") + .HasColumnType("int"); + + b.Property("SmServiceAccounts") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("SyncSeats") + .HasColumnType("tinyint(1)"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("Use2fa") + .HasColumnType("tinyint(1)"); + + b.Property("UseAdminSponsoredFamilies") + .HasColumnType("tinyint(1)"); + + b.Property("UseApi") + .HasColumnType("tinyint(1)"); + + b.Property("UseAutomaticUserConfirmation") + .HasColumnType("tinyint(1)"); + + b.Property("UseCustomPermissions") + .HasColumnType("tinyint(1)"); + + b.Property("UseDirectory") + .HasColumnType("tinyint(1)"); + + b.Property("UseDisableSmAdsForUsers") + .HasColumnType("tinyint(1)"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.Property("UseGroups") + .HasColumnType("tinyint(1)"); + + b.Property("UseInviteLinks") + .HasColumnType("tinyint(1)"); + + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("UseMyItems") + .HasColumnType("tinyint(1)"); + + b.Property("UseOrganizationDomains") + .HasColumnType("tinyint(1)"); + + b.Property("UsePam") + .HasColumnType("tinyint(1)"); + + b.Property("UsePasswordManager") + .HasColumnType("tinyint(1)"); + + b.Property("UsePhishingBlocker") + .HasColumnType("tinyint(1)"); + + b.Property("UsePolicies") + .HasColumnType("tinyint(1)"); + + b.Property("UseResetPassword") + .HasColumnType("tinyint(1)"); + + b.Property("UseRiskInsights") + .HasColumnType("tinyint(1)"); + + b.Property("UseScim") + .HasColumnType("tinyint(1)"); + + b.Property("UseSecretsManager") + .HasColumnType("tinyint(1)"); + + b.Property("UseSso") + .HasColumnType("tinyint(1)"); + + b.Property("UseTotp") + .HasColumnType("tinyint(1)"); + + b.Property("UsersGetPremium") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.HasIndex("Id", "Enabled") + .HasAnnotation("Npgsql:IndexInclude", new[] { "UseTotp", "UsersGetPremium" }); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationInviteLink", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AllowedDomains") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Code") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Invite") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SupportsConfirmation") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationInviteLink", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "Type") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasColumnType("longtext"); + + b.Property("BillingPhone") + .HasColumnType("longtext"); + + b.Property("BusinessAddress1") + .HasColumnType("longtext"); + + b.Property("BusinessAddress2") + .HasColumnType("longtext"); + + b.Property("BusinessAddress3") + .HasColumnType("longtext"); + + b.Property("BusinessCountry") + .HasColumnType("longtext"); + + b.Property("BusinessName") + .HasColumnType("longtext"); + + b.Property("BusinessTaxNumber") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DiscountId") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Settings") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("varchar(25)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("AuthenticationDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MasterPasswordHash") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("RequestCountryName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ResponseDate") + .HasColumnType("datetime(6)"); + + b.Property("ResponseDeviceId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("GranteeId") + .HasColumnType("char(36)"); + + b.Property("GrantorId") + .HasColumnType("char(36)"); + + b.Property("KeyEncrypted") + .HasColumnType("longtext"); + + b.Property("LastNotificationDate") + .HasColumnType("datetime(6)"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("WaitTimeDays") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ConsumedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id") + .HasName("PK_Grant") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ExpirationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId"); + + b.HasIndex("OrganizationId", "ExternalId") + .IsUnique() + .HasAnnotation("Npgsql:IndexInclude", new[] { "UserId" }) + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "UserId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AaGuid") + .HasColumnType("char(36)"); + + b.Property("Counter") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("CredentialId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EncryptedPrivateKey") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("EncryptedPublicKey") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("EncryptedUserKey") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PublicKey") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SupportsPrf") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("WebAuthnCredential", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ClientOrganizationMigrationRecord", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("GatewayCustomerId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("Seats") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId", "OrganizationId") + .IsUnique(); + + b.ToTable("ClientOrganizationMigrationRecord", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("InstallationId") + .HasColumnType("char(36)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("InstallationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationInstallation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohort", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ChurnDiscountCouponCode") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("MigrationPathId") + .HasColumnType("tinyint unsigned"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ProactiveDiscountCouponCode") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("Name") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationPlanMigrationCohort", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohortAssignment", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ChurnDiscountAppliedDate") + .HasColumnType("datetime(6)"); + + b.Property("CohortId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("MigratedDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RenewalNotificationSentDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ScheduledDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CohortId", "CreationDate", "Id") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CohortId", "ScheduledDate", "MigratedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationPlanMigrationCohortAssignment", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AssignedSeats") + .HasColumnType("int"); + + b.Property("ClientId") + .HasColumnType("char(36)"); + + b.Property("ClientName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("InvoiceNumber") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("Total") + .HasColumnType("decimal(65,30)"); + + b.Property("UsedSeats") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderInvoiceItem", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AllocatedSeats") + .HasColumnType("int"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("PurchasedSeats") + .HasColumnType("int"); + + b.Property("SeatMinimum") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("Id", "PlanType") + .IsUnique(); + + b.ToTable("ProviderPlan", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.SubscriptionDiscount", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AmountOff") + .HasColumnType("bigint"); + + b.Property("AudienceType") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Currency") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Duration") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("DurationInMonths") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("PercentOff") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("StartDate") + .HasColumnType("datetime(6)"); + + b.Property("StripeCouponId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("StripeProductIds") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("StripeCouponId") + .IsUnique(); + + b.HasIndex("StartDate", "EndDate") + .HasDatabaseName("IX_SubscriptionDiscount_DateRange") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SubscriptionDiscount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationApplication", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Applications") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ContentEncryptionKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationApplication", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationDeleteTask", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CompletedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("FailureCount") + .HasColumnType("int"); + + b.Property("ItemsDeletedCount") + .HasColumnType("bigint"); + + b.Property("LastError") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("StartDate") + .HasColumnType("datetime(6)"); + + b.Property("TaskType") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("CompletedDate", "CreationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationDeleteTask", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Configuration") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "Type") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationIntegration", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegrationConfiguration", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Configuration") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .HasColumnType("int"); + + b.Property("Filters") + .HasColumnType("longtext"); + + b.Property("OrganizationIntegrationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Template") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationIntegrationId"); + + b.ToTable("OrganizationIntegrationConfiguration", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationReport", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApplicationAtRiskCount") + .HasColumnType("int"); + + b.Property("ApplicationCount") + .HasColumnType("int"); + + b.Property("ApplicationData") + .HasColumnType("longtext"); + + b.Property("ContentEncryptionKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("CriticalApplicationAtRiskCount") + .HasColumnType("int"); + + b.Property("CriticalApplicationCount") + .HasColumnType("int"); + + b.Property("CriticalMemberAtRiskCount") + .HasColumnType("int"); + + b.Property("CriticalMemberCount") + .HasColumnType("int"); + + b.Property("CriticalPasswordAtRiskCount") + .HasColumnType("int"); + + b.Property("CriticalPasswordCount") + .HasColumnType("int"); + + b.Property("MemberAtRiskCount") + .HasColumnType("int"); + + b.Property("MemberCount") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PasswordAtRiskCount") + .HasColumnType("int"); + + b.Property("PasswordCount") + .HasColumnType("int"); + + b.Property("ReportData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ReportFile") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SummaryData") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationReport", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Uri") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("PasswordHealthReportApplication", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cache", b => + { + b.Property("Id") + .HasMaxLength(449) + .HasColumnType("varchar(449)"); + + b.Property("AbsoluteExpiration") + .HasColumnType("datetime(6)"); + + b.Property("ExpiresAtTime") + .HasColumnType("datetime(6)"); + + b.Property("SlidingExpirationInSeconds") + .HasColumnType("bigint"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longblob"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ExpiresAtTime") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Active") + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("ClientVersion") + .HasMaxLength(43) + .HasColumnType("varchar(43)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("EncryptedPrivateKey") + .HasColumnType("longtext"); + + b.Property("EncryptedPublicKey") + .HasColumnType("longtext"); + + b.Property("EncryptedUserKey") + .HasColumnType("longtext"); + + b.Property("Identifier") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LastActivityDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("Identifier") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "Identifier") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ActingUserId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("DeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("DomainName") + .HasColumnType("longtext"); + + b.Property("GrantedServiceAccountId") + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("InstallationId") + .HasColumnType("char(36)"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("PolicyId") + .HasColumnType("char(36)"); + + b.Property("ProjectId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("ProviderOrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderUserId") + .HasColumnType("char(36)"); + + b.Property("SecretId") + .HasColumnType("char(36)"); + + b.Property("SendId") + .HasColumnType("char(36)"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)"); + + b.Property("SystemUser") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasDatabaseName("IX_Event_OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "SendId", "Date") + .HasDatabaseName("IX_Event_OrganizationIdSendIdDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("Date", "OrganizationId", "ActingUserId", "CipherId") + .HasDatabaseName("IX_Event_DateOrganizationIdUserId") + .HasAnnotation("SqlServer:Clustered", false) + .HasAnnotation("SqlServer:Include", new[] { "ServiceAccountId", "GrantedServiceAccountId" }); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Config") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DomainName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("JobRunCount") + .HasColumnType("int"); + + b.Property("LastCheckedDate") + .HasColumnType("datetime(6)"); + + b.Property("NextRunDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Txt") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("VerifiedDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsAdminInitiated") + .HasColumnType("tinyint(1)"); + + b.Property("LastSyncDate") + .HasColumnType("datetime(6)"); + + b.Property("Notes") + .HasColumnType("longtext"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("tinyint unsigned"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("ToDelete") + .HasColumnType("tinyint(1)"); + + b.Property("ValidUntil") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.HasIndex("SponsoringOrganizationUserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessPam") + .HasColumnType("tinyint(1)"); + + b.Property("AccessSecretsManager") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ResetPasswordKey") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("RevocationReason") + .HasColumnType("tinyint unsigned"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("StatusNew") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("V2UpgradeToken") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.PlayItem", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PlayId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("PlayId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ProviderId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("PlayItem", null, t => + { + t.HasCheckConstraint("CK_PlayItem_UserOrOrganizationOrProvider", "((CASE WHEN \"UserId\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"OrganizationId\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"ProviderId\" IS NOT NULL THEN 1 ELSE 0 END)) = 1"); + }); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("AuthType") + .HasColumnType("tinyint unsigned"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DeletionDate") + .HasColumnType("datetime(6)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("Emails") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("HideEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MaxAccessCount") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DeletionDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PostalCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Rate") + .HasColumnType("decimal(65,30)"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PaymentMethodType") + .HasColumnType("tinyint unsigned"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("Refunded") + .HasColumnType("tinyint(1)"); + + b.Property("RefundedAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "OrganizationId", "CreationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccountRevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Culture") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailVerified") + .HasColumnType("tinyint(1)"); + + b.Property("EquivalentDomains") + .HasColumnType("longtext"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("longtext"); + + b.Property("FailedLoginCount") + .HasColumnType("int"); + + b.Property("ForcePasswordReset") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Kdf") + .HasColumnType("tinyint unsigned"); + + b.Property("KdfIterations") + .HasColumnType("int"); + + b.Property("KdfMemory") + .HasColumnType("int"); + + b.Property("KdfParallelism") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("LastApiKeyRotationDate") + .HasColumnType("datetime(6)"); + + b.Property("LastEmailChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LastFailedLoginDate") + .HasColumnType("datetime(6)"); + + b.Property("LastKdfChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LastKeyRotationDate") + .HasColumnType("datetime(6)"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MasterPasswordSalt") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Premium") + .HasColumnType("tinyint(1)"); + + b.Property("PremiumExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RenewalReminderDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("SecurityState") + .HasColumnType("longtext"); + + b.Property("SecurityVersion") + .HasColumnType("int"); + + b.Property("SignedPublicKey") + .HasColumnType("longtext"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UserKeyId") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("V2UpgradeToken") + .HasColumnType("longtext"); + + b.Property("VerifyDevices") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.HasIndex("Premium", "PremiumExpirationDate", "RenewalReminderDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SignatureAlgorithm") + .HasColumnType("tinyint unsigned"); + + b.Property("SigningKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("VerifyingKey") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("UserSignatureKeyPair", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Body") + .HasMaxLength(3000) + .HasColumnType("varchar(3000)"); + + b.Property("ClientType") + .HasColumnType("tinyint unsigned"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Global") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Priority") + .HasColumnType("tinyint unsigned"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("TaskId") + .HasColumnType("char(36)"); + + b.Property("Title") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("TaskId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ClientType", "Global", "UserId", "OrganizationId", "Priority", "CreationDate") + .IsDescending(false, false, false, false, true, true) + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Notification", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.NotificationStatus", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("NotificationId") + .HasColumnType("char(36)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("ReadDate") + .HasColumnType("datetime(6)"); + + b.HasKey("UserId", "NotificationId") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("NotificationId"); + + b.ToTable("NotificationStatus", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessAuditEvent", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessConnectorId") + .HasColumnType("char(36)"); + + b.Property("AccessConnectorName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("AccessLeaseId") + .HasColumnType("char(36)"); + + b.Property("AccessRequestId") + .HasColumnType("char(36)"); + + b.Property("AccessRuleId") + .HasColumnType("char(36)"); + + b.Property("ActorEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ActorId") + .HasColumnType("char(36)"); + + b.Property("ActorName") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CorrelationId") + .HasColumnType("char(36)"); + + b.Property("Detail") + .HasColumnType("longtext"); + + b.Property("Kind") + .HasColumnType("tinyint unsigned"); + + b.Property("LeaseNotAfter") + .HasColumnType("datetime(6)"); + + b.Property("LeaseNotBefore") + .HasColumnType("datetime(6)"); + + b.Property("OccurredDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Phase") + .HasColumnType("tinyint unsigned"); + + b.Property("RequesterEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RequesterId") + .HasColumnType("char(36)"); + + b.Property("RequesterName") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RotationConfigId") + .HasColumnType("char(36)"); + + b.Property("RotationJobId") + .HasColumnType("char(36)"); + + b.Property("RotationSource") + .HasColumnType("tinyint unsigned"); + + b.Property("RuleName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("SyncState") + .HasColumnType("tinyint unsigned"); + + b.Property("TargetSystemId") + .HasColumnType("char(36)"); + + b.Property("TargetSystemName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("CorrelationId"); + + b.HasIndex("OrganizationId", "OccurredDate", "Id") + .IsDescending(false, true, true); + + b.ToTable("AccessAuditEvent", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessDecision", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessRequestId") + .HasColumnType("char(36)"); + + b.Property("ApproverId") + .HasColumnType("char(36)"); + + b.Property("Comment") + .HasColumnType("longtext"); + + b.Property("ConditionKind") + .HasColumnType("tinyint unsigned"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeciderKind") + .HasColumnType("tinyint unsigned"); + + b.Property("EvaluationContext") + .HasColumnType("longtext"); + + b.Property("Verdict") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("AccessRequestId"); + + b.ToTable("AccessDecision", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessLease", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessRequestId") + .HasColumnType("char(36)"); + + b.Property("Action") + .HasColumnType("tinyint unsigned"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("NotAfter") + .HasColumnType("datetime(6)"); + + b.Property("NotBefore") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RequesterId") + .HasColumnType("char(36)"); + + b.Property("RevokedBy") + .HasColumnType("char(36)"); + + b.Property("RevokedDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("AccessRequestId") + .IsUnique(); + + b.HasIndex("OrganizationId"); + + b.HasIndex("CollectionId", "Action"); + + b.HasIndex("NotAfter", "Action"); + + b.HasIndex("CipherId", "Action", "NotAfter") + .IsDescending(false, false, true); + + b.HasIndex("RequesterId", "CipherId", "Action"); + + b.ToTable("AccessLease", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRequest", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Action") + .HasColumnType("tinyint unsigned"); + + b.Property("ActionDate") + .HasColumnType("datetime(6)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExtensionOfLeaseId") + .HasColumnType("char(36)"); + + b.Property("NotAfter") + .HasColumnType("datetime(6)"); + + b.Property("NotBefore") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Reason") + .HasColumnType("longtext"); + + b.Property("RequesterId") + .HasColumnType("char(36)"); + + b.Property("RuleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ExtensionOfLeaseId"); + + b.HasIndex("RuleId"); + + b.HasIndex("CollectionId", "CreationDate"); + + b.HasIndex("OrganizationId", "Action"); + + b.HasIndex("RequesterId", "CreationDate"); + + b.HasIndex("CollectionId", "Action", "NotAfter"); + + b.HasIndex("RequesterId", "CipherId", "Action"); + + b.ToTable("AccessRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRule", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AllowsExtensions") + .HasColumnType("tinyint(1)"); + + b.Property("Conditions") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DefaultLeaseDurationSeconds") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("LastEditedBy") + .HasColumnType("char(36)"); + + b.Property("MaxExtensionDurationSeconds") + .HasColumnType("int"); + + b.Property("MaxLeaseDurationSeconds") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SingleActiveLease") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "Name") + .IsUnique(); + + b.ToTable("AccessRule", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnector", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("LastHeartbeatDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId") + .IsUnique(); + + b.HasIndex("OrganizationId"); + + b.ToTable("PamAccessConnector", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnectorTargetAssignment", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessConnectorId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("TargetSystemId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("TargetSystemId"); + + b.HasIndex("AccessConnectorId", "TargetSystemId") + .IsUnique(); + + b.ToTable("PamAccessConnectorTargetAssignment", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamLeaseExpirySweep", b => + { + b.Property("AccessLeaseId") + .HasColumnType("char(36)"); + + b.Property("SweptDate") + .HasColumnType("datetime(6)"); + + b.HasKey("AccessLeaseId"); + + b.ToTable("PamLeaseExpirySweep", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationAttempt", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Action") + .HasColumnType("tinyint unsigned"); + + b.Property("CipherUpdated") + .HasColumnType("tinyint(1)"); + + b.Property("ClaimedByAccessConnectorId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("FailureReason") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("JobId") + .HasColumnType("char(36)"); + + b.Property("ResolvedDate") + .HasColumnType("datetime(6)"); + + b.Property("SessionTermination") + .HasColumnType("tinyint unsigned"); + + b.Property("SyncState") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("ClaimedByAccessConnectorId", "CreationDate") + .IsDescending(false, true); + + b.HasIndex("JobId", "Action"); + + b.ToTable("PamRotationAttempt", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationConfig", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("LastRotationDate") + .HasColumnType("datetime(6)"); + + b.Property("NextRotationDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("RotateOnAccessEnd") + .HasColumnType("tinyint(1)"); + + b.Property("ScheduleCron") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SealedData") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("varchar(10000)"); + + b.Property("TargetSystemId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CipherId") + .IsUnique(); + + b.HasIndex("NextRotationDate"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("TargetSystemId"); + + b.ToTable("PamRotationConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJob", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Action") + .HasColumnType("tinyint unsigned"); + + b.Property("ClaimedByAccessConnectorId") + .HasColumnType("char(36)"); + + b.Property("ClaimedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("NextClaimableDate") + .HasColumnType("datetime(6)"); + + b.Property("RotationConfigId") + .HasColumnType("char(36)"); + + b.Property("Source") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("Action", "ExpirationDate"); + + b.HasIndex("ClaimedByAccessConnectorId", "Action"); + + b.HasIndex("RotationConfigId", "Action"); + + b.ToTable("PamRotationJob", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJobTimeoutSweep", b => + { + b.Property("RotationJobId") + .HasColumnType("char(36)"); + + b.Property("SweptDate") + .HasColumnType("datetime(6)"); + + b.HasKey("RotationJobId"); + + b.ToTable("PamRotationJobTimeoutSweep", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamTargetSystem", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Method") + .HasColumnType("tinyint unsigned"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SealedData") + .HasMaxLength(10000) + .HasColumnType("varchar(10000)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("PamTargetSystem", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Platform.Installation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("LastActivityDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(34) + .HasColumnType("varchar(34)"); + + b.Property("Read") + .HasColumnType("tinyint(1)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Write") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator().HasValue("AccessPolicy"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ClientSecretHash") + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("EncryptedPayload") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Scope") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Note") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.SecretVersion", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("EditorOrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("EditorServiceAccountId") + .HasColumnType("char(36)"); + + b.Property("SecretId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("VersionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("EditorOrganizationUserId") + .HasDatabaseName("IX_SecretVersion_EditorOrganizationUserId"); + + b.HasIndex("EditorServiceAccountId") + .HasDatabaseName("IX_SecretVersion_EditorServiceAccountId"); + + b.HasIndex("SecretId") + .HasDatabaseName("IX_SecretVersion_SecretId"); + + b.ToTable("SecretVersion"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Archives") + .HasColumnType("longtext"); + + b.Property("Attachments") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Favorites") + .HasColumnType("longtext"); + + b.Property("Folders") + .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("ReportExclusions") + .HasColumnType("longtext"); + + b.Property("Reprompt") + .HasColumnType("tinyint unsigned"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("CipherId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SecurityTask", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("char(36)"); + + b.Property("SecretsId") + .HasColumnType("char(36)"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedSecretId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedSecretId"); + + b.Property("ServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedSecretId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRule", null) + .WithMany() + .HasForeignKey("AccessRuleId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Collections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationInviteLink", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Platform.Installation", "Installation") + .WithMany() + .HasForeignKey("InstallationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Installation"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohortAssignment", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohort", "Cohort") + .WithMany() + .HasForeignKey("CohortId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cohort"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationApplication", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegrationConfiguration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", "OrganizationIntegration") + .WithMany() + .HasForeignKey("OrganizationIntegrationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationIntegration"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationReport", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.PlayItem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", "Task") + .WithMany() + .HasForeignKey("TaskId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("Task"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.NotificationStatus", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", "Notification") + .WithMany() + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessAuditEvent", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessDecision", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRequest", null) + .WithMany() + .HasForeignKey("AccessRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessLease", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRequest", null) + .WithMany() + .HasForeignKey("AccessRequestId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessLease", null) + .WithMany() + .HasForeignKey("ExtensionOfLeaseId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRule", null) + .WithMany() + .HasForeignKey("RuleId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRule", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnector", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", null) + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnectorTargetAssignment", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnector", null) + .WithMany() + .HasForeignKey("AccessConnectorId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamTargetSystem", null) + .WithMany() + .HasForeignKey("TargetSystemId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamLeaseExpirySweep", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessLease", null) + .WithMany() + .HasForeignKey("AccessLeaseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationAttempt", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJob", null) + .WithMany() + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamTargetSystem", null) + .WithMany() + .HasForeignKey("TargetSystemId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJob", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationConfig", null) + .WithMany() + .HasForeignKey("RotationConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJobTimeoutSweep", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJob", null) + .WithMany() + .HasForeignKey("RotationJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamTargetSystem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany("ApiKeys") + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.SecretVersion", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "EditorOrganizationUser") + .WithMany() + .HasForeignKey("EditorOrganizationUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "EditorServiceAccount") + .WithMany() + .HasForeignKey("EditorServiceAccountId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "Secret") + .WithMany("SecretVersions") + .HasForeignKey("SecretId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EditorOrganizationUser"); + + b.Navigation("EditorServiceAccount"); + + b.Navigation("Secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany() + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedSecret"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany("ProjectAccessPolicies") + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedSecret"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedSecret"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Collections"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("SecretVersions"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ProjectAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.cs b/util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.cs new file mode 100644 index 000000000000..db2b091004b7 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +/// +public partial class AddCipherReportExclusions : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ReportExclusions", + table: "Cipher", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ReportExclusions", + table: "Cipher"); + } +} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index 6cc3a6f84224..2bba2f0967aa 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -3349,6 +3349,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("OrganizationId") .HasColumnType("char(36)"); + b.Property("ReportExclusions") + .HasColumnType("longtext"); + b.Property("Reprompt") .HasColumnType("tinyint unsigned"); diff --git a/util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.Designer.cs b/util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.Designer.cs new file mode 100644 index 000000000000..0edb37a0211e --- /dev/null +++ b/util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.Designer.cs @@ -0,0 +1,4635 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20261021171844_AddCipherReportExclusions")] + partial class AddCipherReportExclusions + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Bit.Core.Dirt.Reports.Models.Data.OrganizationMemberBaseDetail", b => + { + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CollectionName") + .HasColumnType("text"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("GroupName") + .HasColumnType("text"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("Manage") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("UserGuid") + .HasColumnType("uuid"); + + b.Property("UserName") + .HasColumnType("text"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.ToTable("OrganizationMemberBaseDetails"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessRuleId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DefaultUserCollectionEmail") + .HasColumnType("text"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AccessRuleId"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("Manage") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("Manage") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowAdminAccessToAllCollectionItems") + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("BillingEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExemptFromBillingAutomation") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("LimitCollectionCreation") + .HasColumnType("boolean"); + + b.Property("LimitCollectionDeletion") + .HasColumnType("boolean"); + + b.Property("LimitItemDeletion") + .HasColumnType("boolean"); + + b.Property("MaxAutoscalePamSeats") + .HasColumnType("integer"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxAutoscaleSmSeats") + .HasColumnType("integer"); + + b.Property("MaxAutoscaleSmServiceAccounts") + .HasColumnType("integer"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationKeyId") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("timestamp with time zone"); + + b.Property("PamSeatMinimum") + .HasColumnType("integer"); + + b.Property("PamSeats") + .HasColumnType("integer"); + + b.Property("Plan") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("SelfHost") + .HasColumnType("boolean"); + + b.Property("SmSeats") + .HasColumnType("integer"); + + b.Property("SmServiceAccounts") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("SyncSeats") + .HasColumnType("boolean"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("Use2fa") + .HasColumnType("boolean"); + + b.Property("UseAdminSponsoredFamilies") + .HasColumnType("boolean"); + + b.Property("UseApi") + .HasColumnType("boolean"); + + b.Property("UseAutomaticUserConfirmation") + .HasColumnType("boolean"); + + b.Property("UseCustomPermissions") + .HasColumnType("boolean"); + + b.Property("UseDirectory") + .HasColumnType("boolean"); + + b.Property("UseDisableSmAdsForUsers") + .HasColumnType("boolean"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.Property("UseGroups") + .HasColumnType("boolean"); + + b.Property("UseInviteLinks") + .HasColumnType("boolean"); + + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + + b.Property("UseMyItems") + .HasColumnType("boolean"); + + b.Property("UseOrganizationDomains") + .HasColumnType("boolean"); + + b.Property("UsePam") + .HasColumnType("boolean"); + + b.Property("UsePasswordManager") + .HasColumnType("boolean"); + + b.Property("UsePhishingBlocker") + .HasColumnType("boolean"); + + b.Property("UsePolicies") + .HasColumnType("boolean"); + + b.Property("UseResetPassword") + .HasColumnType("boolean"); + + b.Property("UseRiskInsights") + .HasColumnType("boolean"); + + b.Property("UseScim") + .HasColumnType("boolean"); + + b.Property("UseSecretsManager") + .HasColumnType("boolean"); + + b.Property("UseSso") + .HasColumnType("boolean"); + + b.Property("UseTotp") + .HasColumnType("boolean"); + + b.Property("UsersGetPremium") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.HasIndex("Id", "Enabled"); + + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Id", "Enabled"), new[] { "UseTotp", "UsersGetPremium" }); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationInviteLink", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowedDomains") + .IsRequired() + .HasColumnType("text"); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Invite") + .IsRequired() + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SupportsConfirmation") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationInviteLink", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "Type") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasColumnType("text"); + + b.Property("BillingPhone") + .HasColumnType("text"); + + b.Property("BusinessAddress1") + .HasColumnType("text"); + + b.Property("BusinessAddress2") + .HasColumnType("text"); + + b.Property("BusinessAddress3") + .HasColumnType("text"); + + b.Property("BusinessCountry") + .HasColumnType("text"); + + b.Property("BusinessName") + .HasColumnType("text"); + + b.Property("BusinessTaxNumber") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DiscountId") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.Property("Approved") + .HasColumnType("boolean"); + + b.Property("AuthenticationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MasterPasswordHash") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("RequestCountryName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ResponseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ResponseDeviceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GranteeId") + .HasColumnType("uuid"); + + b.Property("GrantorId") + .HasColumnType("uuid"); + + b.Property("KeyEncrypted") + .HasColumnType("text"); + + b.Property("LastNotificationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("WaitTimeDays") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id") + .HasName("PK_Grant") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ExpirationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId"); + + b.HasIndex("OrganizationId", "ExternalId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("OrganizationId", "ExternalId"), new[] { "UserId" }); + + b.HasIndex("OrganizationId", "UserId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AaGuid") + .HasColumnType("uuid"); + + b.Property("Counter") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CredentialId") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EncryptedPrivateKey") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("EncryptedPublicKey") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("EncryptedUserKey") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PublicKey") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SupportsPrf") + .HasColumnType("boolean"); + + b.Property("Type") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("WebAuthnCredential", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ClientOrganizationMigrationRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("GatewayCustomerId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId", "OrganizationId") + .IsUnique(); + + b.ToTable("ClientOrganizationMigrationRecord", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("InstallationId") + .HasColumnType("uuid"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("InstallationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationInstallation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohort", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ChurnDiscountCouponCode") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("MigrationPathId") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ProactiveDiscountCouponCode") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("Name") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationPlanMigrationCohort", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohortAssignment", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ChurnDiscountAppliedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CohortId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("MigratedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RenewalNotificationSentDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ScheduledDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CohortId", "CreationDate", "Id") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CohortId", "ScheduledDate", "MigratedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationPlanMigrationCohortAssignment", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AssignedSeats") + .HasColumnType("integer"); + + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("ClientName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("InvoiceId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("InvoiceNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("Total") + .HasColumnType("numeric"); + + b.Property("UsedSeats") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderInvoiceItem", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllocatedSeats") + .HasColumnType("integer"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("PurchasedSeats") + .HasColumnType("integer"); + + b.Property("SeatMinimum") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("Id", "PlanType") + .IsUnique(); + + b.ToTable("ProviderPlan", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.SubscriptionDiscount", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AmountOff") + .HasColumnType("bigint"); + + b.Property("AudienceType") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Currency") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Duration") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("DurationInMonths") + .HasColumnType("integer"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PercentOff") + .HasPrecision(5, 2) + .HasColumnType("numeric(5,2)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("StripeCouponId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("StripeProductIds") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("StripeCouponId") + .IsUnique(); + + b.HasIndex("StartDate", "EndDate") + .HasDatabaseName("IX_SubscriptionDiscount_DateRange") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SubscriptionDiscount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationApplication", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Applications") + .IsRequired() + .HasColumnType("text"); + + b.Property("ContentEncryptionKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationApplication", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationDeleteTask", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CompletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("FailureCount") + .HasColumnType("integer"); + + b.Property("ItemsDeletedCount") + .HasColumnType("bigint"); + + b.Property("LastError") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("TaskType") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("CompletedDate", "CreationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationDeleteTask", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Configuration") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "Type") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationIntegration", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegrationConfiguration", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Configuration") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("EventType") + .HasColumnType("integer"); + + b.Property("Filters") + .HasColumnType("text"); + + b.Property("OrganizationIntegrationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Template") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationIntegrationId"); + + b.ToTable("OrganizationIntegrationConfiguration", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationReport", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationAtRiskCount") + .HasColumnType("integer"); + + b.Property("ApplicationCount") + .HasColumnType("integer"); + + b.Property("ApplicationData") + .HasColumnType("text"); + + b.Property("ContentEncryptionKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CriticalApplicationAtRiskCount") + .HasColumnType("integer"); + + b.Property("CriticalApplicationCount") + .HasColumnType("integer"); + + b.Property("CriticalMemberAtRiskCount") + .HasColumnType("integer"); + + b.Property("CriticalMemberCount") + .HasColumnType("integer"); + + b.Property("CriticalPasswordAtRiskCount") + .HasColumnType("integer"); + + b.Property("CriticalPasswordCount") + .HasColumnType("integer"); + + b.Property("MemberAtRiskCount") + .HasColumnType("integer"); + + b.Property("MemberCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PasswordAtRiskCount") + .HasColumnType("integer"); + + b.Property("PasswordCount") + .HasColumnType("integer"); + + b.Property("ReportData") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReportFile") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SummaryData") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationReport", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Uri") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("PasswordHealthReportApplication", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cache", b => + { + b.Property("Id") + .HasMaxLength(449) + .HasColumnType("character varying(449)"); + + b.Property("AbsoluteExpiration") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAtTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SlidingExpirationInSeconds") + .HasColumnType("bigint"); + + b.Property("Value") + .IsRequired() + .HasColumnType("bytea"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ExpiresAtTime") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("ClientVersion") + .HasMaxLength(43) + .HasColumnType("character varying(43)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("EncryptedPrivateKey") + .HasColumnType("text"); + + b.Property("EncryptedPublicKey") + .HasColumnType("text"); + + b.Property("EncryptedUserKey") + .HasColumnType("text"); + + b.Property("Identifier") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("LastActivityDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("Identifier") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "Identifier") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ActingUserId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceType") + .HasColumnType("smallint"); + + b.Property("DomainName") + .HasColumnType("text"); + + b.Property("GrantedServiceAccountId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("InstallationId") + .HasColumnType("uuid"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("PolicyId") + .HasColumnType("uuid"); + + b.Property("ProjectId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("ProviderOrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderUserId") + .HasColumnType("uuid"); + + b.Property("SecretId") + .HasColumnType("uuid"); + + b.Property("SendId") + .HasColumnType("uuid"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid"); + + b.Property("SystemUser") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasDatabaseName("IX_Event_OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "SendId", "Date") + .HasDatabaseName("IX_Event_OrganizationIdSendIdDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("Date", "OrganizationId", "ActingUserId", "CipherId") + .HasDatabaseName("IX_Event_DateOrganizationIdUserId") + .HasAnnotation("SqlServer:Clustered", false) + .HasAnnotation("SqlServer:Include", new[] { "ServiceAccountId", "GrantedServiceAccountId" }); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Config") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DomainName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("JobRunCount") + .HasColumnType("integer"); + + b.Property("LastCheckedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("NextRunDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Txt") + .IsRequired() + .HasColumnType("text"); + + b.Property("VerifiedDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsAdminInitiated") + .HasColumnType("boolean"); + + b.Property("LastSyncDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Notes") + .HasColumnType("text"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("smallint"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("uuid"); + + b.Property("ToDelete") + .HasColumnType("boolean"); + + b.Property("ValidUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.HasIndex("SponsoringOrganizationUserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessPam") + .HasColumnType("boolean"); + + b.Property("AccessSecretsManager") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevocationReason") + .HasColumnType("smallint"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("StatusNew") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("V2UpgradeToken") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.PlayItem", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PlayId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("PlayId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ProviderId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("PlayItem", null, t => + { + t.HasCheckConstraint("CK_PlayItem_UserOrOrganizationOrProvider", "((CASE WHEN \"UserId\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"OrganizationId\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"ProviderId\" IS NOT NULL THEN 1 ELSE 0 END)) = 1"); + }); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCount") + .HasColumnType("integer"); + + b.Property("AuthType") + .HasColumnType("smallint"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeletionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("Emails") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HideEmail") + .HasColumnType("boolean"); + + b.Property("Key") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxAccessCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DeletionDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Rate") + .HasColumnType("numeric"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PaymentMethodType") + .HasColumnType("smallint"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("Refunded") + .HasColumnType("boolean"); + + b.Property("RefundedAmount") + .HasColumnType("numeric"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "OrganizationId", "CreationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccountRevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("character varying(7)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Culture") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("EquivalentDomains") + .HasColumnType("text"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("text"); + + b.Property("FailedLoginCount") + .HasColumnType("integer"); + + b.Property("ForcePasswordReset") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Kdf") + .HasColumnType("smallint"); + + b.Property("KdfIterations") + .HasColumnType("integer"); + + b.Property("KdfMemory") + .HasColumnType("integer"); + + b.Property("KdfParallelism") + .HasColumnType("integer"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("LastApiKeyRotationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastEmailChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastFailedLoginDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastKdfChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastKeyRotationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MasterPasswordSalt") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Premium") + .HasColumnType("boolean"); + + b.Property("PremiumExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RenewalReminderDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("SecurityState") + .HasColumnType("text"); + + b.Property("SecurityVersion") + .HasColumnType("integer"); + + b.Property("SignedPublicKey") + .HasColumnType("text"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UserKeyId") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.Property("V2UpgradeToken") + .HasColumnType("text"); + + b.Property("VerifyDevices") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.HasIndex("Premium", "PremiumExpirationDate", "RenewalReminderDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SignatureAlgorithm") + .HasColumnType("smallint"); + + b.Property("SigningKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("VerifyingKey") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("UserSignatureKeyPair", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Body") + .HasMaxLength(3000) + .HasColumnType("character varying(3000)"); + + b.Property("ClientType") + .HasColumnType("smallint"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Global") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Priority") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("TaskId") + .HasColumnType("uuid"); + + b.Property("Title") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("TaskId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ClientType", "Global", "UserId", "OrganizationId", "Priority", "CreationDate") + .IsDescending(false, false, false, false, true, true) + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Notification", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.NotificationStatus", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("NotificationId") + .HasColumnType("uuid"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReadDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("UserId", "NotificationId") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("NotificationId"); + + b.ToTable("NotificationStatus", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessAuditEvent", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessConnectorId") + .HasColumnType("uuid"); + + b.Property("AccessConnectorName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("AccessLeaseId") + .HasColumnType("uuid"); + + b.Property("AccessRequestId") + .HasColumnType("uuid"); + + b.Property("AccessRuleId") + .HasColumnType("uuid"); + + b.Property("ActorEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ActorId") + .HasColumnType("uuid"); + + b.Property("ActorName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CorrelationId") + .HasColumnType("uuid"); + + b.Property("Detail") + .HasColumnType("text"); + + b.Property("Kind") + .HasColumnType("smallint"); + + b.Property("LeaseNotAfter") + .HasColumnType("timestamp with time zone"); + + b.Property("LeaseNotBefore") + .HasColumnType("timestamp with time zone"); + + b.Property("OccurredDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Phase") + .HasColumnType("smallint"); + + b.Property("RequesterEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RequesterId") + .HasColumnType("uuid"); + + b.Property("RequesterName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RotationConfigId") + .HasColumnType("uuid"); + + b.Property("RotationJobId") + .HasColumnType("uuid"); + + b.Property("RotationSource") + .HasColumnType("smallint"); + + b.Property("RuleName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SyncState") + .HasColumnType("smallint"); + + b.Property("TargetSystemId") + .HasColumnType("uuid"); + + b.Property("TargetSystemName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("CorrelationId"); + + b.HasIndex("OrganizationId", "OccurredDate", "Id") + .IsDescending(false, true, true); + + b.ToTable("AccessAuditEvent", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessDecision", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessRequestId") + .HasColumnType("uuid"); + + b.Property("ApproverId") + .HasColumnType("uuid"); + + b.Property("Comment") + .HasColumnType("text"); + + b.Property("ConditionKind") + .HasColumnType("smallint"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeciderKind") + .HasColumnType("smallint"); + + b.Property("EvaluationContext") + .HasColumnType("text"); + + b.Property("Verdict") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccessRequestId"); + + b.ToTable("AccessDecision", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessLease", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessRequestId") + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("smallint"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("NotAfter") + .HasColumnType("timestamp with time zone"); + + b.Property("NotBefore") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RequesterId") + .HasColumnType("uuid"); + + b.Property("RevokedBy") + .HasColumnType("uuid"); + + b.Property("RevokedDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("AccessRequestId") + .IsUnique(); + + b.HasIndex("OrganizationId"); + + b.HasIndex("CollectionId", "Action"); + + b.HasIndex("NotAfter", "Action"); + + b.HasIndex("CipherId", "Action", "NotAfter") + .IsDescending(false, false, true); + + b.HasIndex("RequesterId", "CipherId", "Action"); + + b.ToTable("AccessLease", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("smallint"); + + b.Property("ActionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtensionOfLeaseId") + .HasColumnType("uuid"); + + b.Property("NotAfter") + .HasColumnType("timestamp with time zone"); + + b.Property("NotBefore") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Reason") + .HasColumnType("text"); + + b.Property("RequesterId") + .HasColumnType("uuid"); + + b.Property("RuleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ExtensionOfLeaseId"); + + b.HasIndex("RuleId"); + + b.HasIndex("CollectionId", "CreationDate"); + + b.HasIndex("OrganizationId", "Action"); + + b.HasIndex("RequesterId", "CreationDate"); + + b.HasIndex("CollectionId", "Action", "NotAfter"); + + b.HasIndex("RequesterId", "CipherId", "Action"); + + b.ToTable("AccessRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRule", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowsExtensions") + .HasColumnType("boolean"); + + b.Property("Conditions") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DefaultLeaseDurationSeconds") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("LastEditedBy") + .HasColumnType("uuid"); + + b.Property("MaxExtensionDurationSeconds") + .HasColumnType("integer"); + + b.Property("MaxLeaseDurationSeconds") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SingleActiveLease") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "Name") + .IsUnique(); + + b.ToTable("AccessRule", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnector", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastHeartbeatDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId") + .IsUnique(); + + b.HasIndex("OrganizationId"); + + b.ToTable("PamAccessConnector", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnectorTargetAssignment", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessConnectorId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("TargetSystemId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("TargetSystemId"); + + b.HasIndex("AccessConnectorId", "TargetSystemId") + .IsUnique(); + + b.ToTable("PamAccessConnectorTargetAssignment", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamLeaseExpirySweep", b => + { + b.Property("AccessLeaseId") + .HasColumnType("uuid"); + + b.Property("SweptDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("AccessLeaseId"); + + b.ToTable("PamLeaseExpirySweep", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationAttempt", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("smallint"); + + b.Property("CipherUpdated") + .HasColumnType("boolean"); + + b.Property("ClaimedByAccessConnectorId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("FailureReason") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("JobId") + .HasColumnType("uuid"); + + b.Property("ResolvedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionTermination") + .HasColumnType("smallint"); + + b.Property("SyncState") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ClaimedByAccessConnectorId", "CreationDate") + .IsDescending(false, true); + + b.HasIndex("JobId", "Action"); + + b.ToTable("PamRotationAttempt", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationConfig", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("LastRotationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("NextRotationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RotateOnAccessEnd") + .HasColumnType("boolean"); + + b.Property("ScheduleCron") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SealedData") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b.Property("TargetSystemId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CipherId") + .IsUnique(); + + b.HasIndex("NextRotationDate"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("TargetSystemId"); + + b.ToTable("PamRotationConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJob", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("smallint"); + + b.Property("ClaimedByAccessConnectorId") + .HasColumnType("uuid"); + + b.Property("ClaimedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("NextClaimableDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RotationConfigId") + .HasColumnType("uuid"); + + b.Property("Source") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("Action", "ExpirationDate"); + + b.HasIndex("ClaimedByAccessConnectorId", "Action"); + + b.HasIndex("RotationConfigId", "Action"); + + b.ToTable("PamRotationJob", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJobTimeoutSweep", b => + { + b.Property("RotationJobId") + .HasColumnType("uuid"); + + b.Property("SweptDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("RotationJobId"); + + b.ToTable("PamRotationJobTimeoutSweep", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamTargetSystem", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Method") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SealedData") + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("PamTargetSystem", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Platform.Installation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("LastActivityDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(34) + .HasColumnType("character varying(34)"); + + b.Property("Read") + .HasColumnType("boolean"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Write") + .HasColumnType("boolean"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator().HasValue("AccessPolicy"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClientSecretHash") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("EncryptedPayload") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Scope") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.SecretVersion", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("EditorOrganizationUserId") + .HasColumnType("uuid"); + + b.Property("EditorServiceAccountId") + .HasColumnType("uuid"); + + b.Property("SecretId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.Property("VersionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("EditorOrganizationUserId") + .HasDatabaseName("IX_SecretVersion_EditorOrganizationUserId"); + + b.HasIndex("EditorServiceAccountId") + .HasDatabaseName("IX_SecretVersion_EditorServiceAccountId"); + + b.HasIndex("SecretId") + .HasDatabaseName("IX_SecretVersion_SecretId"); + + b.ToTable("SecretVersion"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Archives") + .HasColumnType("text"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Favorites") + .HasColumnType("text"); + + b.Property("Folders") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ReportExclusions") + .HasColumnType("text"); + + b.Property("Reprompt") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("CipherId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SecurityTask", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("uuid"); + + b.Property("SecretsId") + .HasColumnType("uuid"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedSecretId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedSecretId"); + + b.Property("ServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedSecretId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRule", null) + .WithMany() + .HasForeignKey("AccessRuleId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Collections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationInviteLink", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Platform.Installation", "Installation") + .WithMany() + .HasForeignKey("InstallationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Installation"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohortAssignment", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohort", "Cohort") + .WithMany() + .HasForeignKey("CohortId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cohort"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationApplication", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegrationConfiguration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", "OrganizationIntegration") + .WithMany() + .HasForeignKey("OrganizationIntegrationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationIntegration"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationReport", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.PlayItem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", "Task") + .WithMany() + .HasForeignKey("TaskId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("Task"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.NotificationStatus", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", "Notification") + .WithMany() + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessAuditEvent", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessDecision", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRequest", null) + .WithMany() + .HasForeignKey("AccessRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessLease", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRequest", null) + .WithMany() + .HasForeignKey("AccessRequestId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessLease", null) + .WithMany() + .HasForeignKey("ExtensionOfLeaseId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRule", null) + .WithMany() + .HasForeignKey("RuleId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRule", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnector", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", null) + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnectorTargetAssignment", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnector", null) + .WithMany() + .HasForeignKey("AccessConnectorId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamTargetSystem", null) + .WithMany() + .HasForeignKey("TargetSystemId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamLeaseExpirySweep", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessLease", null) + .WithMany() + .HasForeignKey("AccessLeaseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationAttempt", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJob", null) + .WithMany() + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamTargetSystem", null) + .WithMany() + .HasForeignKey("TargetSystemId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJob", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationConfig", null) + .WithMany() + .HasForeignKey("RotationConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJobTimeoutSweep", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJob", null) + .WithMany() + .HasForeignKey("RotationJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamTargetSystem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany("ApiKeys") + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.SecretVersion", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "EditorOrganizationUser") + .WithMany() + .HasForeignKey("EditorOrganizationUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "EditorServiceAccount") + .WithMany() + .HasForeignKey("EditorServiceAccountId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "Secret") + .WithMany("SecretVersions") + .HasForeignKey("SecretId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EditorOrganizationUser"); + + b.Navigation("EditorServiceAccount"); + + b.Navigation("Secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany() + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedSecret"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany("ProjectAccessPolicies") + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedSecret"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedSecret"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Collections"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("SecretVersions"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ProjectAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.cs b/util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.cs new file mode 100644 index 000000000000..6b279b80e2ae --- /dev/null +++ b/util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +/// +public partial class AddCipherReportExclusions : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ReportExclusions", + table: "Cipher", + type: "text", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ReportExclusions", + table: "Cipher"); + } +} diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index db4d63071ac2..4f333304b1df 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -3355,6 +3355,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("OrganizationId") .HasColumnType("uuid"); + b.Property("ReportExclusions") + .HasColumnType("text"); + b.Property("Reprompt") .HasColumnType("smallint"); diff --git a/util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.Designer.cs b/util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.Designer.cs new file mode 100644 index 000000000000..4f1613d3303a --- /dev/null +++ b/util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.Designer.cs @@ -0,0 +1,4618 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20261021171852_AddCipherReportExclusions")] + partial class AddCipherReportExclusions + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.11"); + + modelBuilder.Entity("Bit.Core.Dirt.Reports.Models.Data.OrganizationMemberBaseDetail", b => + { + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("CollectionName") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("GroupName") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("Manage") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.Property("ResetPasswordKey") + .HasColumnType("TEXT"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("UserGuid") + .HasColumnType("TEXT"); + + b.Property("UserName") + .HasColumnType("TEXT"); + + b.Property("UsesKeyConnector") + .HasColumnType("INTEGER"); + + b.ToTable("OrganizationMemberBaseDetails"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessRuleId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DefaultUserCollectionEmail") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("AccessRuleId"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("Manage") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("Manage") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AllowAdminAccessToAllCollectionItems") + .HasColumnType("INTEGER") + .HasDefaultValue(true); + + b.Property("BillingEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("ExemptFromBillingAutomation") + .HasColumnType("INTEGER"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("LimitCollectionCreation") + .HasColumnType("INTEGER"); + + b.Property("LimitCollectionDeletion") + .HasColumnType("INTEGER"); + + b.Property("LimitItemDeletion") + .HasColumnType("INTEGER"); + + b.Property("MaxAutoscalePamSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxAutoscaleSmSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxAutoscaleSmServiceAccounts") + .HasColumnType("INTEGER"); + + b.Property("MaxCollections") + .HasColumnType("INTEGER"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationKeyId") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("TEXT"); + + b.Property("PamSeatMinimum") + .HasColumnType("INTEGER"); + + b.Property("PamSeats") + .HasColumnType("INTEGER"); + + b.Property("Plan") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PlanType") + .HasColumnType("INTEGER"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("ReferenceData") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Seats") + .HasColumnType("INTEGER"); + + b.Property("SelfHost") + .HasColumnType("INTEGER"); + + b.Property("SmSeats") + .HasColumnType("INTEGER"); + + b.Property("SmServiceAccounts") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Storage") + .HasColumnType("INTEGER"); + + b.Property("SyncSeats") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("Use2fa") + .HasColumnType("INTEGER"); + + b.Property("UseAdminSponsoredFamilies") + .HasColumnType("INTEGER"); + + b.Property("UseApi") + .HasColumnType("INTEGER"); + + b.Property("UseAutomaticUserConfirmation") + .HasColumnType("INTEGER"); + + b.Property("UseCustomPermissions") + .HasColumnType("INTEGER"); + + b.Property("UseDirectory") + .HasColumnType("INTEGER"); + + b.Property("UseDisableSmAdsForUsers") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.Property("UseGroups") + .HasColumnType("INTEGER"); + + b.Property("UseInviteLinks") + .HasColumnType("INTEGER"); + + b.Property("UseKeyConnector") + .HasColumnType("INTEGER"); + + b.Property("UseMyItems") + .HasColumnType("INTEGER"); + + b.Property("UseOrganizationDomains") + .HasColumnType("INTEGER"); + + b.Property("UsePam") + .HasColumnType("INTEGER"); + + b.Property("UsePasswordManager") + .HasColumnType("INTEGER"); + + b.Property("UsePhishingBlocker") + .HasColumnType("INTEGER"); + + b.Property("UsePolicies") + .HasColumnType("INTEGER"); + + b.Property("UseResetPassword") + .HasColumnType("INTEGER"); + + b.Property("UseRiskInsights") + .HasColumnType("INTEGER"); + + b.Property("UseScim") + .HasColumnType("INTEGER"); + + b.Property("UseSecretsManager") + .HasColumnType("INTEGER"); + + b.Property("UseSso") + .HasColumnType("INTEGER"); + + b.Property("UseTotp") + .HasColumnType("INTEGER"); + + b.Property("UsersGetPremium") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.HasIndex("Id", "Enabled") + .HasAnnotation("Npgsql:IndexInclude", new[] { "UseTotp", "UsersGetPremium" }); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationInviteLink", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AllowedDomains") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Invite") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SupportsConfirmation") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationInviteLink", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "Type") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BillingEmail") + .HasColumnType("TEXT"); + + b.Property("BillingPhone") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DiscountId") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("TEXT"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("AuthenticationDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("MasterPasswordHash") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("RequestCountryName") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("RequestDeviceType") + .HasColumnType("INTEGER"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ResponseDate") + .HasColumnType("TEXT"); + + b.Property("ResponseDeviceId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("GranteeId") + .HasColumnType("TEXT"); + + b.Property("GrantorId") + .HasColumnType("TEXT"); + + b.Property("KeyEncrypted") + .HasColumnType("TEXT"); + + b.Property("LastNotificationDate") + .HasColumnType("TEXT"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("WaitTimeDays") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasName("PK_Grant") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ExpirationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId"); + + b.HasIndex("OrganizationId", "ExternalId") + .IsUnique() + .HasAnnotation("Npgsql:IndexInclude", new[] { "UserId" }) + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "UserId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AaGuid") + .HasColumnType("TEXT"); + + b.Property("Counter") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("CredentialId") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EncryptedPrivateKey") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("EncryptedPublicKey") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("EncryptedUserKey") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SupportsPrf") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("WebAuthnCredential", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ClientOrganizationMigrationRecord", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("GatewayCustomerId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PlanType") + .HasColumnType("INTEGER"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("Seats") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId", "OrganizationId") + .IsUnique(); + + b.ToTable("ClientOrganizationMigrationRecord", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("InstallationId") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("InstallationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationInstallation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohort", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ChurnDiscountCouponCode") + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("MigrationPathId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("ProactiveDiscountCouponCode") + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("Name") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationPlanMigrationCohort", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohortAssignment", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ChurnDiscountAppliedDate") + .HasColumnType("TEXT"); + + b.Property("CohortId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("MigratedDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RenewalNotificationSentDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("ScheduledDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CohortId", "CreationDate", "Id") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CohortId", "ScheduledDate", "MigratedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationPlanMigrationCohortAssignment", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AssignedSeats") + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("TEXT"); + + b.Property("ClientName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Created") + .HasColumnType("TEXT"); + + b.Property("InvoiceId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("InvoiceNumber") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PlanName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("Total") + .HasColumnType("TEXT"); + + b.Property("UsedSeats") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderInvoiceItem", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AllocatedSeats") + .HasColumnType("INTEGER"); + + b.Property("PlanType") + .HasColumnType("INTEGER"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("PurchasedSeats") + .HasColumnType("INTEGER"); + + b.Property("SeatMinimum") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("Id", "PlanType") + .IsUnique(); + + b.ToTable("ProviderPlan", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.SubscriptionDiscount", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AmountOff") + .HasColumnType("INTEGER"); + + b.Property("AudienceType") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Currency") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Duration") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("DurationInMonths") + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("PercentOff") + .HasPrecision(5, 2) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("StripeCouponId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("StripeProductIds") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("StripeCouponId") + .IsUnique(); + + b.HasIndex("StartDate", "EndDate") + .HasDatabaseName("IX_SubscriptionDiscount_DateRange") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SubscriptionDiscount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationApplication", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Applications") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ContentEncryptionKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationApplication", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationDeleteTask", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CompletedDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("FailureCount") + .HasColumnType("INTEGER"); + + b.Property("ItemsDeletedCount") + .HasColumnType("INTEGER"); + + b.Property("LastError") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("TaskType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("CompletedDate", "CreationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationDeleteTask", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Configuration") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "Type") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationIntegration", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegrationConfiguration", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Configuration") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("EventType") + .HasColumnType("INTEGER"); + + b.Property("Filters") + .HasColumnType("TEXT"); + + b.Property("OrganizationIntegrationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Template") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationIntegrationId"); + + b.ToTable("OrganizationIntegrationConfiguration", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationReport", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ApplicationAtRiskCount") + .HasColumnType("INTEGER"); + + b.Property("ApplicationCount") + .HasColumnType("INTEGER"); + + b.Property("ApplicationData") + .HasColumnType("TEXT"); + + b.Property("ContentEncryptionKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("CriticalApplicationAtRiskCount") + .HasColumnType("INTEGER"); + + b.Property("CriticalApplicationCount") + .HasColumnType("INTEGER"); + + b.Property("CriticalMemberAtRiskCount") + .HasColumnType("INTEGER"); + + b.Property("CriticalMemberCount") + .HasColumnType("INTEGER"); + + b.Property("CriticalPasswordAtRiskCount") + .HasColumnType("INTEGER"); + + b.Property("CriticalPasswordCount") + .HasColumnType("INTEGER"); + + b.Property("MemberAtRiskCount") + .HasColumnType("INTEGER"); + + b.Property("MemberCount") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PasswordAtRiskCount") + .HasColumnType("INTEGER"); + + b.Property("PasswordCount") + .HasColumnType("INTEGER"); + + b.Property("ReportData") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ReportFile") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SummaryData") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationReport", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("PasswordHealthReportApplication", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cache", b => + { + b.Property("Id") + .HasMaxLength(449) + .HasColumnType("TEXT"); + + b.Property("AbsoluteExpiration") + .HasColumnType("TEXT"); + + b.Property("ExpiresAtTime") + .HasColumnType("TEXT"); + + b.Property("SlidingExpirationInSeconds") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("BLOB"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ExpiresAtTime") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Active") + .HasColumnType("INTEGER") + .HasDefaultValue(true); + + b.Property("ClientVersion") + .HasMaxLength(43) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("EncryptedPrivateKey") + .HasColumnType("TEXT"); + + b.Property("EncryptedPublicKey") + .HasColumnType("TEXT"); + + b.Property("EncryptedUserKey") + .HasColumnType("TEXT"); + + b.Property("Identifier") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("LastActivityDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Identifier") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "Identifier") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ActingUserId") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("DeviceType") + .HasColumnType("INTEGER"); + + b.Property("DomainName") + .HasColumnType("TEXT"); + + b.Property("GrantedServiceAccountId") + .HasColumnType("TEXT"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("InstallationId") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("PolicyId") + .HasColumnType("TEXT"); + + b.Property("ProjectId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("ProviderOrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderUserId") + .HasColumnType("TEXT"); + + b.Property("SecretId") + .HasColumnType("TEXT"); + + b.Property("SendId") + .HasColumnType("TEXT"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT"); + + b.Property("SystemUser") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasDatabaseName("IX_Event_OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "SendId", "Date") + .HasDatabaseName("IX_Event_OrganizationIdSendIdDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("Date", "OrganizationId", "ActingUserId", "CipherId") + .HasDatabaseName("IX_Event_DateOrganizationIdUserId") + .HasAnnotation("SqlServer:Clustered", false) + .HasAnnotation("SqlServer:Include", new[] { "ServiceAccountId", "GrantedServiceAccountId" }); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Config") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DomainName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("JobRunCount") + .HasColumnType("INTEGER"); + + b.Property("LastCheckedDate") + .HasColumnType("TEXT"); + + b.Property("NextRunDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Txt") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("VerifiedDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("IsAdminInitiated") + .HasColumnType("INTEGER"); + + b.Property("LastSyncDate") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PlanSponsorshipType") + .HasColumnType("INTEGER"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("TEXT"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("TEXT"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("ToDelete") + .HasColumnType("INTEGER"); + + b.Property("ValidUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.HasIndex("SponsoringOrganizationUserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessPam") + .HasColumnType("INTEGER"); + + b.Property("AccessSecretsManager") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ResetPasswordKey") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("RevocationReason") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StatusNew") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("V2UpgradeToken") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.PlayItem", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PlayId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("PlayId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ProviderId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("PlayItem", null, t => + { + t.HasCheckConstraint("CK_PlayItem_UserOrOrganizationOrProvider", "((CASE WHEN \"UserId\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"OrganizationId\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"ProviderId\" IS NOT NULL THEN 1 ELSE 0 END)) = 1"); + }); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCount") + .HasColumnType("INTEGER"); + + b.Property("AuthType") + .HasColumnType("INTEGER"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DeletionDate") + .HasColumnType("TEXT"); + + b.Property("Disabled") + .HasColumnType("INTEGER"); + + b.Property("Emails") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("HideEmail") + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MaxAccessCount") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DeletionDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PostalCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Rate") + .HasColumnType("TEXT"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Amount") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PaymentMethodType") + .HasColumnType("INTEGER"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("Refunded") + .HasColumnType("INTEGER"); + + b.Property("RefundedAmount") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "OrganizationId", "CreationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccountRevisionDate") + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Culture") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailVerified") + .HasColumnType("INTEGER"); + + b.Property("EquivalentDomains") + .HasColumnType("TEXT"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("TEXT"); + + b.Property("FailedLoginCount") + .HasColumnType("INTEGER"); + + b.Property("ForcePasswordReset") + .HasColumnType("INTEGER"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Kdf") + .HasColumnType("INTEGER"); + + b.Property("KdfIterations") + .HasColumnType("INTEGER"); + + b.Property("KdfMemory") + .HasColumnType("INTEGER"); + + b.Property("KdfParallelism") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("LastApiKeyRotationDate") + .HasColumnType("TEXT"); + + b.Property("LastEmailChangeDate") + .HasColumnType("TEXT"); + + b.Property("LastFailedLoginDate") + .HasColumnType("TEXT"); + + b.Property("LastKdfChangeDate") + .HasColumnType("TEXT"); + + b.Property("LastKeyRotationDate") + .HasColumnType("TEXT"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("TEXT"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("MasterPasswordSalt") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Premium") + .HasColumnType("INTEGER"); + + b.Property("PremiumExpirationDate") + .HasColumnType("TEXT"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("ReferenceData") + .HasColumnType("TEXT"); + + b.Property("RenewalReminderDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("SecurityState") + .HasColumnType("TEXT"); + + b.Property("SecurityVersion") + .HasColumnType("INTEGER"); + + b.Property("SignedPublicKey") + .HasColumnType("TEXT"); + + b.Property("Storage") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("UserKeyId") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("UsesKeyConnector") + .HasColumnType("INTEGER"); + + b.Property("V2UpgradeToken") + .HasColumnType("TEXT"); + + b.Property("VerifyDevices") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.HasIndex("Premium", "PremiumExpirationDate", "RenewalReminderDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SignatureAlgorithm") + .HasColumnType("INTEGER"); + + b.Property("SigningKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("VerifyingKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("UserSignatureKeyPair", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Body") + .HasMaxLength(3000) + .HasColumnType("TEXT"); + + b.Property("ClientType") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Global") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("TaskId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("TaskId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ClientType", "Global", "UserId", "OrganizationId", "Priority", "CreationDate") + .IsDescending(false, false, false, false, true, true) + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Notification", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.NotificationStatus", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("NotificationId") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("ReadDate") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "NotificationId") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("NotificationId"); + + b.ToTable("NotificationStatus", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessAuditEvent", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessConnectorId") + .HasColumnType("TEXT"); + + b.Property("AccessConnectorName") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("AccessLeaseId") + .HasColumnType("TEXT"); + + b.Property("AccessRequestId") + .HasColumnType("TEXT"); + + b.Property("AccessRuleId") + .HasColumnType("TEXT"); + + b.Property("ActorEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("ActorId") + .HasColumnType("TEXT"); + + b.Property("ActorName") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .HasColumnType("TEXT"); + + b.Property("Detail") + .HasColumnType("TEXT"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("LeaseNotAfter") + .HasColumnType("TEXT"); + + b.Property("LeaseNotBefore") + .HasColumnType("TEXT"); + + b.Property("OccurredDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Phase") + .HasColumnType("INTEGER"); + + b.Property("RequesterEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("RequesterId") + .HasColumnType("TEXT"); + + b.Property("RequesterName") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("RotationConfigId") + .HasColumnType("TEXT"); + + b.Property("RotationJobId") + .HasColumnType("TEXT"); + + b.Property("RotationSource") + .HasColumnType("INTEGER"); + + b.Property("RuleName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("SyncState") + .HasColumnType("INTEGER"); + + b.Property("TargetSystemId") + .HasColumnType("TEXT"); + + b.Property("TargetSystemName") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CorrelationId"); + + b.HasIndex("OrganizationId", "OccurredDate", "Id") + .IsDescending(false, true, true); + + b.ToTable("AccessAuditEvent", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessDecision", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessRequestId") + .HasColumnType("TEXT"); + + b.Property("ApproverId") + .HasColumnType("TEXT"); + + b.Property("Comment") + .HasColumnType("TEXT"); + + b.Property("ConditionKind") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeciderKind") + .HasColumnType("INTEGER"); + + b.Property("EvaluationContext") + .HasColumnType("TEXT"); + + b.Property("Verdict") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("AccessRequestId"); + + b.ToTable("AccessDecision", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessLease", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessRequestId") + .HasColumnType("TEXT"); + + b.Property("Action") + .HasColumnType("INTEGER"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("NotAfter") + .HasColumnType("TEXT"); + + b.Property("NotBefore") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RequesterId") + .HasColumnType("TEXT"); + + b.Property("RevokedBy") + .HasColumnType("TEXT"); + + b.Property("RevokedDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AccessRequestId") + .IsUnique(); + + b.HasIndex("OrganizationId"); + + b.HasIndex("CollectionId", "Action"); + + b.HasIndex("NotAfter", "Action"); + + b.HasIndex("CipherId", "Action", "NotAfter") + .IsDescending(false, false, true); + + b.HasIndex("RequesterId", "CipherId", "Action"); + + b.ToTable("AccessLease", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRequest", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Action") + .HasColumnType("INTEGER"); + + b.Property("ActionDate") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExtensionOfLeaseId") + .HasColumnType("TEXT"); + + b.Property("NotAfter") + .HasColumnType("TEXT"); + + b.Property("NotBefore") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Reason") + .HasColumnType("TEXT"); + + b.Property("RequesterId") + .HasColumnType("TEXT"); + + b.Property("RuleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ExtensionOfLeaseId"); + + b.HasIndex("RuleId"); + + b.HasIndex("CollectionId", "CreationDate"); + + b.HasIndex("OrganizationId", "Action"); + + b.HasIndex("RequesterId", "CreationDate"); + + b.HasIndex("CollectionId", "Action", "NotAfter"); + + b.HasIndex("RequesterId", "CipherId", "Action"); + + b.ToTable("AccessRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRule", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AllowsExtensions") + .HasColumnType("INTEGER"); + + b.Property("Conditions") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DefaultLeaseDurationSeconds") + .HasColumnType("INTEGER"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("LastEditedBy") + .HasColumnType("TEXT"); + + b.Property("MaxExtensionDurationSeconds") + .HasColumnType("INTEGER"); + + b.Property("MaxLeaseDurationSeconds") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SingleActiveLease") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "Name") + .IsUnique(); + + b.ToTable("AccessRule", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnector", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ApiKeyId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("LastHeartbeatDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId") + .IsUnique(); + + b.HasIndex("OrganizationId"); + + b.ToTable("PamAccessConnector", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnectorTargetAssignment", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessConnectorId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("TargetSystemId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("TargetSystemId"); + + b.HasIndex("AccessConnectorId", "TargetSystemId") + .IsUnique(); + + b.ToTable("PamAccessConnectorTargetAssignment", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamLeaseExpirySweep", b => + { + b.Property("AccessLeaseId") + .HasColumnType("TEXT"); + + b.Property("SweptDate") + .HasColumnType("TEXT"); + + b.HasKey("AccessLeaseId"); + + b.ToTable("PamLeaseExpirySweep", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationAttempt", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Action") + .HasColumnType("INTEGER"); + + b.Property("CipherUpdated") + .HasColumnType("INTEGER"); + + b.Property("ClaimedByAccessConnectorId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("FailureReason") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("JobId") + .HasColumnType("TEXT"); + + b.Property("ResolvedDate") + .HasColumnType("TEXT"); + + b.Property("SessionTermination") + .HasColumnType("INTEGER"); + + b.Property("SyncState") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ClaimedByAccessConnectorId", "CreationDate") + .IsDescending(false, true); + + b.HasIndex("JobId", "Action"); + + b.ToTable("PamRotationAttempt", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationConfig", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("LastRotationDate") + .HasColumnType("TEXT"); + + b.Property("NextRotationDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("RotateOnAccessEnd") + .HasColumnType("INTEGER"); + + b.Property("ScheduleCron") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SealedData") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.Property("TargetSystemId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CipherId") + .IsUnique(); + + b.HasIndex("NextRotationDate"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("TargetSystemId"); + + b.ToTable("PamRotationConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJob", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Action") + .HasColumnType("INTEGER"); + + b.Property("ClaimedByAccessConnectorId") + .HasColumnType("TEXT"); + + b.Property("ClaimedDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("NextClaimableDate") + .HasColumnType("TEXT"); + + b.Property("RotationConfigId") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Action", "ExpirationDate"); + + b.HasIndex("ClaimedByAccessConnectorId", "Action"); + + b.HasIndex("RotationConfigId", "Action"); + + b.ToTable("PamRotationJob", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJobTimeoutSweep", b => + { + b.Property("RotationJobId") + .HasColumnType("TEXT"); + + b.Property("SweptDate") + .HasColumnType("TEXT"); + + b.HasKey("RotationJobId"); + + b.ToTable("PamRotationJobTimeoutSweep", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamTargetSystem", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Method") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SealedData") + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("PamTargetSystem", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Platform.Installation", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("TEXT"); + + b.Property("LastActivityDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(34) + .HasColumnType("TEXT"); + + b.Property("Read") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Write") + .HasColumnType("INTEGER"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator().HasValue("AccessPolicy"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ClientSecretHash") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("EncryptedPayload") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ExpireAt") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Scope") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.SecretVersion", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("EditorOrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("EditorServiceAccountId") + .HasColumnType("TEXT"); + + b.Property("SecretId") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("VersionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("EditorOrganizationUserId") + .HasDatabaseName("IX_SecretVersion_EditorOrganizationUserId"); + + b.HasIndex("EditorServiceAccountId") + .HasDatabaseName("IX_SecretVersion_EditorServiceAccountId"); + + b.HasIndex("SecretId") + .HasDatabaseName("IX_SecretVersion_SecretId"); + + b.ToTable("SecretVersion"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Archives") + .HasColumnType("TEXT"); + + b.Property("Attachments") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Favorites") + .HasColumnType("TEXT"); + + b.Property("Folders") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("ReportExclusions") + .HasColumnType("TEXT"); + + b.Property("Reprompt") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("CipherId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SecurityTask", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("TEXT"); + + b.Property("SecretsId") + .HasColumnType("TEXT"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedSecretId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedSecretId"); + + b.Property("ServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedSecretId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRule", null) + .WithMany() + .HasForeignKey("AccessRuleId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Collections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationInviteLink", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Platform.Installation", "Installation") + .WithMany() + .HasForeignKey("InstallationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Installation"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohortAssignment", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohort", "Cohort") + .WithMany() + .HasForeignKey("CohortId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cohort"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationApplication", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegrationConfiguration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", "OrganizationIntegration") + .WithMany() + .HasForeignKey("OrganizationIntegrationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationIntegration"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationReport", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.PlayItem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", "Task") + .WithMany() + .HasForeignKey("TaskId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("Task"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.NotificationStatus", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", "Notification") + .WithMany() + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessAuditEvent", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessDecision", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRequest", null) + .WithMany() + .HasForeignKey("AccessRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessLease", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRequest", null) + .WithMany() + .HasForeignKey("AccessRequestId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessLease", null) + .WithMany() + .HasForeignKey("ExtensionOfLeaseId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRule", null) + .WithMany() + .HasForeignKey("RuleId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.AccessRule", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnector", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", null) + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnectorTargetAssignment", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamAccessConnector", null) + .WithMany() + .HasForeignKey("AccessConnectorId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamTargetSystem", null) + .WithMany() + .HasForeignKey("TargetSystemId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamLeaseExpirySweep", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.AccessLease", null) + .WithMany() + .HasForeignKey("AccessLeaseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationAttempt", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJob", null) + .WithMany() + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamTargetSystem", null) + .WithMany() + .HasForeignKey("TargetSystemId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJob", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationConfig", null) + .WithMany() + .HasForeignKey("RotationConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJobTimeoutSweep", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Pam.Models.PamRotationJob", null) + .WithMany() + .HasForeignKey("RotationJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Pam.Models.PamTargetSystem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany("ApiKeys") + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.SecretVersion", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "EditorOrganizationUser") + .WithMany() + .HasForeignKey("EditorOrganizationUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "EditorServiceAccount") + .WithMany() + .HasForeignKey("EditorServiceAccountId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "Secret") + .WithMany("SecretVersions") + .HasForeignKey("SecretId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EditorOrganizationUser"); + + b.Navigation("EditorServiceAccount"); + + b.Navigation("Secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany() + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedSecret"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany("ProjectAccessPolicies") + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedSecret"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedSecret"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Collections"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("SecretVersions"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ProjectAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.cs b/util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.cs new file mode 100644 index 000000000000..8b40abb161af --- /dev/null +++ b/util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations; + +/// +public partial class AddCipherReportExclusions : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ReportExclusions", + table: "Cipher", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ReportExclusions", + table: "Cipher"); + } +} diff --git a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs index 31beafa10726..e878eafbe413 100644 --- a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -3338,6 +3338,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("OrganizationId") .HasColumnType("TEXT"); + b.Property("ReportExclusions") + .HasColumnType("TEXT"); + b.Property("Reprompt") .HasColumnType("INTEGER"); From 8b8e9af150dd69052c7995079b5d4c776fbc0e92 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Oct 2026 13:25:02 -0400 Subject: [PATCH 02/20] [PM-41931] feat(dirt): Return the per-user report exclusion date on ciphers Reads this user's entry from ReportExclusions into CipherDetails and the cipher response, so the standard sync delivers every exclusion. EF replaces never write the column. --- .../Models/Response/CipherResponseModel.cs | 2 + src/Core/Vault/Models/Data/CipherDetails.cs | 3 + .../Queries/UserCipherDetailsQuery.cs | 30 +- .../Vault/Repositories/CipherRepository.cs | 17 + .../Queries/CipherDetailsQuery.cs | 3 + src/Sql/dbo/Vault/Functions/CipherDetails.sql | 9 +- .../Cipher/CipherDetails_Create.sql | 4 +- .../CipherDetails_CreateWithCollections.sql | 2 + .../Cipher/CipherDetails_ReadByIdUserId.sql | 4 +- .../Cipher/CipherDetails_Update.sql | 4 +- ...026-10-21_00_AddCipherReportExclusions.sql | 309 ++++++++++++++++++ 11 files changed, 380 insertions(+), 7 deletions(-) diff --git a/src/Api/Vault/Models/Response/CipherResponseModel.cs b/src/Api/Vault/Models/Response/CipherResponseModel.cs index 3adbbb4748e3..8ab8e5fa4404 100644 --- a/src/Api/Vault/Models/Response/CipherResponseModel.cs +++ b/src/Api/Vault/Models/Response/CipherResponseModel.cs @@ -296,6 +296,7 @@ protected CipherResponseModel( Favorite = cipher.Favorite; Edit = cipher.Edit; ArchivedDate = cipher.ArchivedDate; + ReportExcludedDate = cipher.ReportExcludedDate; ViewPassword = cipher.ViewPassword; Permissions = new CipherPermissionsResponseModel(user, cipher, organizationAbility); } @@ -312,6 +313,7 @@ public static CipherResponseModel From(FullCipherAccess access, CipherDetails ci public bool Edit { get; set; } public bool ViewPassword { get; set; } public DateTime? ArchivedDate { get; set; } + public DateTime? ReportExcludedDate { get; set; } public CipherPermissionsResponseModel Permissions { get; set; } } diff --git a/src/Core/Vault/Models/Data/CipherDetails.cs b/src/Core/Vault/Models/Data/CipherDetails.cs index 56f5aa39bbab..49bb9dc1079e 100644 --- a/src/Core/Vault/Models/Data/CipherDetails.cs +++ b/src/Core/Vault/Models/Data/CipherDetails.cs @@ -11,6 +11,8 @@ public class CipherDetails : CipherOrganizationDetails public bool Manage { get; set; } // Per-user archived date from Archives JSON. public DateTime? ArchivedDate { get; set; } + // Per-user health report exclusion date from ReportExclusions JSON. + public DateTime? ReportExcludedDate { get; set; } public CipherDetails() { } public CipherDetails(CipherOrganizationDetails cipher) @@ -53,6 +55,7 @@ public CipherDetailsWithCollections( Key = cipher.Key; FolderId = cipher.FolderId; ArchivedDate = cipher.ArchivedDate; + ReportExcludedDate = cipher.ReportExcludedDate; Favorite = cipher.Favorite; Edit = cipher.Edit; ViewPassword = cipher.ViewPassword; diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs b/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs index 67be55f1a958..aeecd8e16d32 100644 --- a/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs +++ b/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs @@ -71,7 +71,8 @@ from cg in cg_g.DefaultIfEmpty() OrganizationUseTotp = o.UseTotp, c.Reprompt, c.Key, - c.Archives + c.Archives, + c.ReportExclusions }; var query2 = from c in dbContext.Ciphers @@ -95,7 +96,8 @@ from cg in cg_g.DefaultIfEmpty() OrganizationUseTotp = false, c.Reprompt, c.Key, - c.Archives + c.Archives, + c.ReportExclusions }; var union = query.Union(query2).Select(c => new CipherDetails @@ -117,7 +119,8 @@ from cg in cg_g.DefaultIfEmpty() Manage = c.Manage, OrganizationUseTotp = c.OrganizationUseTotp, Key = c.Key, - ArchivedDate = GetArchivedDate(_userId, new Cipher { Id = c.Id, Archives = c.Archives }) + ArchivedDate = GetArchivedDate(_userId, new Cipher { Id = c.Id, Archives = c.Archives }), + ReportExcludedDate = GetReportExcludedDate(_userId, new Cipher { Id = c.Id, ReportExclusions = c.ReportExclusions }) }); return union; } @@ -143,6 +146,27 @@ from cg in cg_g.DefaultIfEmpty() } } + private static DateTime? GetReportExcludedDate(Guid? userId, Cipher cipher) + { + try + { + if (userId.HasValue && !string.IsNullOrWhiteSpace(cipher.ReportExclusions)) + { + var exclusions = JsonSerializer.Deserialize>(cipher.ReportExclusions); + if (exclusions.TryGetValue(userId.Value, out var excludedDate)) + { + return excludedDate; + } + } + + return null; + } + catch + { + return null; + } + } + private static Guid? GetFolderId(Guid? userId, Cipher cipher) { try diff --git a/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs b/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs index 0835790bafaf..51933fee3ac6 100644 --- a/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs +++ b/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs @@ -561,6 +561,22 @@ await idsToMove.ForEachAsync(cipher => } } + public override async Task ReplaceAsync(Core.Vault.Entities.Cipher cipher) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var entity = await dbContext.Ciphers.FindAsync(cipher.Id); + if (entity != null) + { + var mappedEntity = Mapper.Map(cipher); + dbContext.Entry(entity).CurrentValues.SetValues(mappedEntity); + dbContext.Entry(entity).Property(c => c.ReportExclusions).IsModified = false; + await dbContext.SaveChangesAsync(); + } + } + } + public async Task ReplaceAsync(CipherDetails cipher) { using (var scope = ServiceScopeFactory.CreateScope()) @@ -634,6 +650,7 @@ public async Task ReplaceAsync(CipherDetails cipher) var mappedEntity = Mapper.Map(cipher); dbContext.Entry(entity).CurrentValues.SetValues(mappedEntity); + dbContext.Entry(entity).Property(c => c.ReportExclusions).IsModified = false; if (cipher.OrganizationId.HasValue) { diff --git a/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs b/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs index 43f368b23227..fa471cf5e72f 100644 --- a/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs +++ b/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs @@ -37,6 +37,9 @@ public virtual IQueryable Run(DatabaseContext dbContext) ArchivedDate = !_userId.HasValue || c.Archives == null || !c.Archives.ToLowerInvariant().Contains(_userId.Value.ToString()) ? null : CoreHelpers.LoadClassFromJsonData>(c.Archives)[_userId.Value], + ReportExcludedDate = !_userId.HasValue || c.ReportExclusions == null || !c.ReportExclusions.ToLowerInvariant().Contains(_userId.Value.ToString()) ? + null : + CoreHelpers.LoadClassFromJsonData>(c.ReportExclusions)[_userId.Value], }; return query; } diff --git a/src/Sql/dbo/Vault/Functions/CipherDetails.sql b/src/Sql/dbo/Vault/Functions/CipherDetails.sql index 51930ec10e6e..f1b700889d3d 100644 --- a/src/Sql/dbo/Vault/Functions/CipherDetails.sql +++ b/src/Sql/dbo/Vault/Functions/CipherDetails.sql @@ -34,6 +34,13 @@ SELECT OR C.[Archives] IS NULL THEN NULL ELSE TRY_CONVERT(DATETIME2(7), JSON_VALUE(C.[Archives], CONCAT('$."', @UserId, '"'))) - END [ArchivedDate] + END [ArchivedDate], + CASE + WHEN + @UserId IS NULL + OR C.[ReportExclusions] IS NULL + THEN NULL + ELSE TRY_CONVERT(DATETIME2(7), JSON_VALUE(C.[ReportExclusions], CONCAT('$."', @UserId, '"'))) + END [ReportExcludedDate] FROM [dbo].[Cipher] C; diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Create.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Create.sql index 846f3bcf8a11..4100650cf366 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Create.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Create.sql @@ -19,7 +19,9 @@ @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, - @Archives NVARCHAR(MAX) = NULL -- not used + @Archives NVARCHAR(MAX) = NULL, -- not used + @ReportExcludedDate DATETIME2(7) = NULL, -- not used + @ReportExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN SET NOCOUNT ON diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql index da573b1d1395..4e02aeb870bf 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql @@ -20,6 +20,8 @@ @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used + @ReportExcludedDate DATETIME2(7) = NULL, -- not used + @ReportExclusions NVARCHAR(MAX) = NULL, -- not used @CollectionIds AS [dbo].[GuidIdArray] READONLY AS BEGIN diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_ReadByIdUserId.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_ReadByIdUserId.sql index 2646159b623e..20e7450d8d6c 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_ReadByIdUserId.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_ReadByIdUserId.sql @@ -21,6 +21,7 @@ SELECT [Key], [OrganizationUseTotp], [ArchivedDate], + [ReportExcludedDate], MAX ([Edit]) AS [Edit], MAX ([ViewPassword]) AS [ViewPassword], MAX ([Manage]) AS [Manage] @@ -43,5 +44,6 @@ SELECT [Reprompt], [Key], [OrganizationUseTotp], - [ArchivedDate] + [ArchivedDate], + [ReportExcludedDate] END diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Update.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Update.sql index 4676f20fd38b..f6c57134418b 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Update.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Update.sql @@ -19,7 +19,9 @@ @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, - @Archives NVARCHAR(MAX) = NULL -- not used + @Archives NVARCHAR(MAX) = NULL, -- not used + @ReportExcludedDate DATETIME2(7) = NULL, -- not used + @ReportExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN SET NOCOUNT ON diff --git a/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql b/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql index 1fbfebeb7094..10767da37789 100644 --- a/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql +++ b/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql @@ -5,6 +5,60 @@ BEGIN END GO +CREATE OR ALTER FUNCTION [dbo].[CipherDetails](@UserId UNIQUEIDENTIFIER) +RETURNS TABLE +AS RETURN +SELECT + C.[Id], + C.[UserId], + C.[OrganizationId], + C.[Type], + C.[Data], + C.[Attachments], + C.[CreationDate], + C.[RevisionDate], + CASE + WHEN + @UserId IS NULL + OR C.[Favorites] IS NULL + OR JSON_VALUE(C.[Favorites], CONCAT('$."', @UserId, '"')) IS NULL + THEN 0 + ELSE 1 + END [Favorite], + CASE + WHEN + @UserId IS NULL + OR C.[Folders] IS NULL + THEN NULL + ELSE TRY_CONVERT(UNIQUEIDENTIFIER, JSON_VALUE(C.[Folders], CONCAT('$."', @UserId, '"'))) + END [FolderId], + C.[DeletedDate], + C.[Reprompt], + C.[Key], + CASE + WHEN + @UserId IS NULL + OR C.[Archives] IS NULL + THEN NULL + ELSE TRY_CONVERT(DATETIME2(7), JSON_VALUE(C.[Archives], CONCAT('$."', @UserId, '"'))) + END [ArchivedDate], + CASE + WHEN + @UserId IS NULL + OR C.[ReportExclusions] IS NULL + THEN NULL + ELSE TRY_CONVERT(DATETIME2(7), JSON_VALUE(C.[ReportExclusions], CONCAT('$."', @UserId, '"'))) + END [ReportExcludedDate] +FROM + [dbo].[Cipher] C; +GO + +IF OBJECT_ID('[dbo].[UserCipherDetails]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[UserCipherDetails]'; +END +GO + CREATE OR ALTER PROCEDURE [dbo].[Cipher_Create] @Id UNIQUEIDENTIFIER OUTPUT, @UserId UNIQUEIDENTIFIER, @@ -219,5 +273,260 @@ BEGIN END GO +CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_Create] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), -- not used + @Folders NVARCHAR(MAX), -- not used + @Attachments NVARCHAR(MAX), -- not used + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @FolderId UNIQUEIDENTIFIER, + @Favorite BIT, + @Edit BIT, -- not used + @ViewPassword BIT, -- not used + @Manage BIT, -- not used + @OrganizationUseTotp BIT, -- not used + @DeletedDate DATETIME2(7), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, + @ArchivedDate DATETIME2(7) = NULL, + @Archives NVARCHAR(MAX) = NULL, -- not used + @ReportExcludedDate DATETIME2(7) = NULL, -- not used + @ReportExclusions NVARCHAR(MAX) = NULL -- not used +AS +BEGIN + SET NOCOUNT ON + + DECLARE @UserIdKey VARCHAR(50) = CONCAT('"', @UserId, '"') + DECLARE @UserIdPath VARCHAR(50) = CONCAT('$.', @UserIdKey) + + INSERT INTO [dbo].[Cipher] + ( + [Id], + [UserId], + [OrganizationId], + [Type], + [Data], + [Favorites], + [Folders], + [CreationDate], + [RevisionDate], + [DeletedDate], + [Reprompt], + [Key], + [Archives] + ) + VALUES + ( + @Id, + CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END, + @OrganizationId, + @Type, + @Data, + CASE WHEN @Favorite = 1 THEN CONCAT('{', @UserIdKey, ':true}') ELSE NULL END, + CASE WHEN @FolderId IS NOT NULL THEN CONCAT('{', @UserIdKey, ':"', @FolderId, '"', '}') ELSE NULL END, + @CreationDate, + @RevisionDate, + @DeletedDate, + @Reprompt, + @Key, + CASE WHEN @ArchivedDate IS NOT NULL THEN CONCAT('{', @UserIdKey, ':"', CONVERT(NVARCHAR(30), @ArchivedDate, 127), '"}') ELSE NULL END + ) + + IF @OrganizationId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId + END + ELSE IF @UserId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId + END +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_CreateWithCollections] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), -- not used + @Folders NVARCHAR(MAX), -- not used + @Attachments NVARCHAR(MAX), -- not used + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @FolderId UNIQUEIDENTIFIER, + @Favorite BIT, + @Edit BIT, -- not used + @ViewPassword BIT, -- not used + @Manage BIT, -- not used + @OrganizationUseTotp BIT, -- not used + @DeletedDate DATETIME2(7), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, + @ArchivedDate DATETIME2(7) = NULL, + @Archives NVARCHAR(MAX) = NULL, -- not used + @ReportExcludedDate DATETIME2(7) = NULL, -- not used + @ReportExclusions NVARCHAR(MAX) = NULL, -- not used + @CollectionIds AS [dbo].[GuidIdArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[CipherDetails_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders, + @Attachments, @CreationDate, @RevisionDate, @FolderId, @Favorite, @Edit, @ViewPassword, @Manage, + @OrganizationUseTotp, @DeletedDate, @Reprompt, @Key, @ArchivedDate + + DECLARE @UpdateCollectionsSuccess INT + EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds + + -- Bump the account revision date AFTER collections are assigned. + IF @UpdateCollectionsSuccess = 0 + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId + END +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_Update] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), -- not used + @Folders NVARCHAR(MAX), -- not used + @Attachments NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @FolderId UNIQUEIDENTIFIER, + @Favorite BIT, + @Edit BIT, -- not used + @ViewPassword BIT, -- not used + @Manage BIT, -- not used + @OrganizationUseTotp BIT, -- not used + @DeletedDate DATETIME2(2), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, + @ArchivedDate DATETIME2(7) = NULL, + @Archives NVARCHAR(MAX) = NULL, -- not used + @ReportExcludedDate DATETIME2(7) = NULL, -- not used + @ReportExclusions NVARCHAR(MAX) = NULL -- not used +AS +BEGIN + SET NOCOUNT ON + + DECLARE @UserIdKey VARCHAR(50) = CONCAT('"', @UserId, '"') + DECLARE @UserIdPath VARCHAR(50) = CONCAT('$.', @UserIdKey) + + UPDATE + [dbo].[Cipher] + SET + [UserId] = CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END, + [OrganizationId] = @OrganizationId, + [Type] = @Type, + [Data] = @Data, + [Folders] = + CASE + WHEN @FolderId IS NOT NULL AND [Folders] IS NULL THEN + CONCAT('{', @UserIdKey, ':"', @FolderId, '"', '}') + WHEN @FolderId IS NOT NULL THEN + JSON_MODIFY([Folders], @UserIdPath, CAST(@FolderId AS VARCHAR(50))) + ELSE + JSON_MODIFY([Folders], @UserIdPath, NULL) + END, + [Favorites] = + CASE + WHEN @Favorite = 1 AND [Favorites] IS NULL THEN + CONCAT('{', @UserIdKey, ':true}') + WHEN @Favorite = 1 THEN + JSON_MODIFY([Favorites], @UserIdPath, CAST(1 AS BIT)) + ELSE + JSON_MODIFY([Favorites], @UserIdPath, NULL) + END, + [Archives] = + CASE + WHEN @ArchivedDate IS NOT NULL AND [Archives] IS NULL THEN + CONCAT('{', @UserIdKey, ':"', CONVERT(NVARCHAR(30), @ArchivedDate, 127), '"}') + WHEN @ArchivedDate IS NOT NULL THEN + JSON_MODIFY([Archives], @UserIdPath, CONVERT(NVARCHAR(30), @ArchivedDate, 127)) + ELSE + JSON_MODIFY([Archives], @UserIdPath, NULL) + END, + [Attachments] = @Attachments, + [Reprompt] = @Reprompt, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate, + [DeletedDate] = @DeletedDate, + [Key] = @Key + WHERE + [Id] = @Id + + IF @OrganizationId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId + END + ELSE IF @UserId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId + END +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_ReadByIdUserId] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +SELECT + [Id], + [UserId], + [OrganizationId], + [Type], + [Data], + [Attachments], + [CreationDate], + [RevisionDate], + [Favorite], + [FolderId], + [DeletedDate], + [Reprompt], + [Key], + [OrganizationUseTotp], + [ArchivedDate], + [ReportExcludedDate], + MAX ([Edit]) AS [Edit], + MAX ([ViewPassword]) AS [ViewPassword], + MAX ([Manage]) AS [Manage] + FROM + [dbo].[UserCipherDetails](@UserId) + WHERE + [Id] = @Id + GROUP BY + [Id], + [UserId], + [OrganizationId], + [Type], + [Data], + [Attachments], + [CreationDate], + [RevisionDate], + [Favorite], + [FolderId], + [DeletedDate], + [Reprompt], + [Key], + [OrganizationUseTotp], + [ArchivedDate], + [ReportExcludedDate] +END +GO + EXECUTE sp_refreshview N'[dbo].[CipherView]' GO From 5bba0de0cdde28f64d51f7b558023d936829a6b9 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Oct 2026 13:27:02 -0400 Subject: [PATCH 03/20] [PM-41931] feat(dirt): Add the cipher report exclusion repository DIRT-owned writer for Cipher.ReportExclusions. Only the user's own personal, not-deleted logins can be excluded, and only the account revision is bumped, so other devices sync without an edit conflict. --- .../ICipherReportExclusionRepository.cs | 7 +++ .../DapperServiceCollectionExtensions.cs | 1 + .../Dirt/CipherReportExclusionRepository.cs | 25 ++++++++++ .../Dirt/CipherReportExclusionRepository.cs | 46 +++++++++++++++++++ ...ityFrameworkServiceCollectionExtensions.cs | 1 + .../Cipher_ExcludeFromReports.sql | 35 ++++++++++++++ ...026-10-21_00_AddCipherReportExclusions.sql | 37 +++++++++++++++ 7 files changed, 152 insertions(+) create mode 100644 src/Core/Dirt/Repositories/ICipherReportExclusionRepository.cs create mode 100644 src/Infrastructure.Dapper/Dirt/CipherReportExclusionRepository.cs create mode 100644 src/Infrastructure.EntityFramework/Dirt/CipherReportExclusionRepository.cs create mode 100644 src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql diff --git a/src/Core/Dirt/Repositories/ICipherReportExclusionRepository.cs b/src/Core/Dirt/Repositories/ICipherReportExclusionRepository.cs new file mode 100644 index 000000000000..ac17ede1b67b --- /dev/null +++ b/src/Core/Dirt/Repositories/ICipherReportExclusionRepository.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.Dirt.Repositories; + +public interface ICipherReportExclusionRepository +{ + /// Excludes the user's personal, not-deleted logins from health reports. Returns how many were excluded. + Task ExcludeAsync(IEnumerable cipherIds, Guid userId); +} diff --git a/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs b/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs index 9f9d3c6f5bdc..9912bc6ca8b9 100644 --- a/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs +++ b/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs @@ -96,6 +96,7 @@ public static void AddDapperRepositories(this IServiceCollection services, bool services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); if (selfHosted) { diff --git a/src/Infrastructure.Dapper/Dirt/CipherReportExclusionRepository.cs b/src/Infrastructure.Dapper/Dirt/CipherReportExclusionRepository.cs new file mode 100644 index 000000000000..5214c0dbfa66 --- /dev/null +++ b/src/Infrastructure.Dapper/Dirt/CipherReportExclusionRepository.cs @@ -0,0 +1,25 @@ +using System.Data; +using Bit.Core.Dirt.Repositories; +using Bit.Core.Settings; +using Bit.Infrastructure.Dapper.Repositories; +using Dapper; +using Microsoft.Data.SqlClient; + +namespace Bit.Infrastructure.Dapper.Dirt; + +public class CipherReportExclusionRepository : BaseRepository, ICipherReportExclusionRepository +{ + public CipherReportExclusionRepository(GlobalSettings globalSettings) + : base(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString) + { + } + + public async Task ExcludeAsync(IEnumerable cipherIds, Guid userId) + { + await using var connection = new SqlConnection(ConnectionString); + return await connection.ExecuteScalarAsync( + "[dbo].[Cipher_ExcludeFromReports]", + new { Ids = cipherIds.ToGuidIdArrayTVP(), UserId = userId }, + commandType: CommandType.StoredProcedure); + } +} diff --git a/src/Infrastructure.EntityFramework/Dirt/CipherReportExclusionRepository.cs b/src/Infrastructure.EntityFramework/Dirt/CipherReportExclusionRepository.cs new file mode 100644 index 000000000000..272202e39d45 --- /dev/null +++ b/src/Infrastructure.EntityFramework/Dirt/CipherReportExclusionRepository.cs @@ -0,0 +1,46 @@ +using AutoMapper; +using Bit.Core.Dirt.Repositories; +using Bit.Core.Utilities; +using Bit.Core.Vault.Enums; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Bit.Infrastructure.EntityFramework.Dirt; + +public class CipherReportExclusionRepository : BaseEntityFrameworkRepository, ICipherReportExclusionRepository +{ + public CipherReportExclusionRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) + : base(serviceScopeFactory, mapper) + { + } + + public async Task ExcludeAsync(IEnumerable cipherIds, Guid userId) + { + await using var scope = ServiceScopeFactory.CreateAsyncScope(); + var dbContext = GetDatabaseContext(scope); + var ids = cipherIds.ToList(); + var ciphers = await dbContext.Ciphers + .Where(c => ids.Contains(c.Id) && c.UserId == userId && c.OrganizationId == null + && c.Type == CipherType.Login && c.DeletedDate == null) + .ToListAsync(); + + var utcNow = DateTime.UtcNow; + foreach (var cipher in ciphers) + { + var exclusions = string.IsNullOrWhiteSpace(cipher.ReportExclusions) + ? new Dictionary() + : CoreHelpers.LoadClassFromJsonData>(cipher.ReportExclusions) + ?? new Dictionary(); + exclusions[userId] = utcNow; + cipher.ReportExclusions = CoreHelpers.ClassToJsonData(exclusions); + } + + if (ciphers.Count > 0) + { + await dbContext.UserBumpAccountRevisionDateAsync(userId); + } + await dbContext.SaveChangesAsync(); + return ciphers.Count; + } +} diff --git a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs index da2dc1dc108c..2fc87c16cbca 100644 --- a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs +++ b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs @@ -132,6 +132,7 @@ public static void AddPasswordManagerEFRepositories(this IServiceCollection serv services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); if (selfHosted) diff --git a/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql b/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql new file mode 100644 index 000000000000..2f873296dd66 --- /dev/null +++ b/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql @@ -0,0 +1,35 @@ +CREATE PROCEDURE [dbo].[Cipher_ExcludeFromReports] + @Ids AS [dbo].[GuidIdArray] READONLY, + @UserId AS UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + DECLARE @UtcNow DATETIME2(7) = SYSUTCDATETIME(); + + UPDATE + C + SET + [ReportExclusions] = JSON_MODIFY( + COALESCE(C.[ReportExclusions], N'{}'), + CONCAT('$."', @UserId, '"'), + CONVERT(NVARCHAR(30), @UtcNow, 127) + ) + FROM + [dbo].[Cipher] C + INNER JOIN @Ids I ON I.[Id] = C.[Id] + WHERE + C.[UserId] = @UserId + AND C.[OrganizationId] IS NULL + AND C.[Type] = 1 -- Login + AND C.[DeletedDate] IS NULL + + DECLARE @Count INT = @@ROWCOUNT + + IF @Count > 0 + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId + END + + SELECT @Count +END diff --git a/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql b/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql index 10767da37789..6a6022480a42 100644 --- a/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql +++ b/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql @@ -528,5 +528,42 @@ SELECT END GO +CREATE OR ALTER PROCEDURE [dbo].[Cipher_ExcludeFromReports] + @Ids AS [dbo].[GuidIdArray] READONLY, + @UserId AS UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + DECLARE @UtcNow DATETIME2(7) = SYSUTCDATETIME(); + + UPDATE + C + SET + [ReportExclusions] = JSON_MODIFY( + COALESCE(C.[ReportExclusions], N'{}'), + CONCAT('$."', @UserId, '"'), + CONVERT(NVARCHAR(30), @UtcNow, 127) + ) + FROM + [dbo].[Cipher] C + INNER JOIN @Ids I ON I.[Id] = C.[Id] + WHERE + C.[UserId] = @UserId + AND C.[OrganizationId] IS NULL + AND C.[Type] = 1 -- Login + AND C.[DeletedDate] IS NULL + + DECLARE @Count INT = @@ROWCOUNT + + IF @Count > 0 + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId + END + + SELECT @Count +END +GO + EXECUTE sp_refreshview N'[dbo].[CipherView]' GO From e324b97df53b7b4eed266fc3f1df8b058d780eff Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Oct 2026 13:28:34 -0400 Subject: [PATCH 04/20] [PM-41931] feat(dirt): Add the exclude from reports endpoint PUT ciphers/{id}/exclude-from-reports, behind the health report exclusions flag. Requires premium, and pushes a cipher sync so the user's other devices pick up the exclusion. --- .../CipherReportExclusionsController.cs | 33 +++++++++++++++ .../ExcludeCipherFromReportsCommand.cs | 40 +++++++++++++++++++ .../IExcludeCipherFromReportsCommand.cs | 7 ++++ .../ReportingServiceCollectionExtensions.cs | 1 + 4 files changed, 81 insertions(+) create mode 100644 src/Api/Dirt/Controllers/CipherReportExclusionsController.cs create mode 100644 src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs create mode 100644 src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs diff --git a/src/Api/Dirt/Controllers/CipherReportExclusionsController.cs b/src/Api/Dirt/Controllers/CipherReportExclusionsController.cs new file mode 100644 index 000000000000..0a6bab2e3efb --- /dev/null +++ b/src/Api/Dirt/Controllers/CipherReportExclusionsController.cs @@ -0,0 +1,33 @@ +using Bit.Core; +using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; +using Bit.Core.Services; +using Bitwarden.Server.Sdk.Features; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Bit.Api.Dirt.Controllers; + +[Route("ciphers")] +[Authorize("Application")] +public class CipherReportExclusionsController : Controller +{ + private readonly IUserService _userService; + private readonly IExcludeCipherFromReportsCommand _excludeCipherFromReportsCommand; + + public CipherReportExclusionsController( + IUserService userService, + IExcludeCipherFromReportsCommand excludeCipherFromReportsCommand) + { + _userService = userService; + _excludeCipherFromReportsCommand = excludeCipherFromReportsCommand; + } + + /// Excludes the caller's personal login from their health reports. + [HttpPut("{id}/exclude-from-reports")] + [RequireFeature(FeatureFlagKeys.HealthReportExclusions)] + public async Task PutExcludeFromReportsAsync(Guid id) + { + var userId = _userService.GetProperUserId(User)!.Value; + await _excludeCipherFromReportsCommand.ExcludeAsync(id, userId); + } +} diff --git a/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs b/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs new file mode 100644 index 000000000000..891f0f1a1c01 --- /dev/null +++ b/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs @@ -0,0 +1,40 @@ +using Bit.Core.Billing.Premium.Queries; +using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; +using Bit.Core.Dirt.Repositories; +using Bit.Core.Exceptions; +using Bit.Core.Platform.Push; + +namespace Bit.Core.Dirt.Reports.ReportFeatures; + +public class ExcludeCipherFromReportsCommand : IExcludeCipherFromReportsCommand +{ + private readonly ICipherReportExclusionRepository _cipherReportExclusionRepository; + private readonly IHasPremiumAccessQuery _hasPremiumAccessQuery; + private readonly IPushNotificationService _pushService; + + public ExcludeCipherFromReportsCommand( + ICipherReportExclusionRepository cipherReportExclusionRepository, + IHasPremiumAccessQuery hasPremiumAccessQuery, + IPushNotificationService pushService) + { + _cipherReportExclusionRepository = cipherReportExclusionRepository; + _hasPremiumAccessQuery = hasPremiumAccessQuery; + _pushService = pushService; + } + + public async Task ExcludeAsync(Guid cipherId, Guid userId) + { + if (!await _hasPremiumAccessQuery.HasPremiumAccessAsync(userId)) + { + throw new BadRequestException("Premium required to exclude items from reports."); + } + + var excluded = await _cipherReportExclusionRepository.ExcludeAsync([cipherId], userId); + if (excluded == 0) + { + throw new NotFoundException(); + } + + await _pushService.PushSyncCiphersAsync(userId, true); + } +} diff --git a/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs b/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs new file mode 100644 index 000000000000..fb0d89921645 --- /dev/null +++ b/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; + +public interface IExcludeCipherFromReportsCommand +{ + /// Excludes the user's personal login from their health reports. Requires premium. + Task ExcludeAsync(Guid cipherId, Guid userId); +} diff --git a/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs b/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs index 6aca3160daca..f2a5e02caf75 100644 --- a/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs +++ b/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs @@ -28,6 +28,7 @@ public static void AddReportingServices(this IServiceCollection services, IGloba services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); // v2 file storage commands services.AddScoped(); From ee40663b0f7e7daf7c252efbab6816e59c28ec64 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Oct 2026 13:36:02 -0400 Subject: [PATCH 05/20] [PM-41931] test(dirt): Cover health report exclusions Repository tests run on every database and pin the scope rules, the per-user read, the revision date behavior, and that Vault replaces keep the exclusion. Adds command and controller unit tests. --- .../CipherReportExclusionsControllerTests.cs | 26 ++ .../ExcludeCipherFromReportsCommandTests.cs | 60 +++++ .../CipherReportExclusionRepositoryTests.cs | 247 ++++++++++++++++++ 3 files changed, 333 insertions(+) create mode 100644 test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs create mode 100644 test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs create mode 100644 test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs diff --git a/test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs b/test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs new file mode 100644 index 000000000000..b7004e157ee6 --- /dev/null +++ b/test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs @@ -0,0 +1,26 @@ +using System.Security.Claims; +using Bit.Api.Dirt.Controllers; +using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; +using Bit.Core.Services; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Api.Test.Dirt; + +[ControllerCustomize(typeof(CipherReportExclusionsController))] +[SutProviderCustomize] +public class CipherReportExclusionsControllerTests +{ + [Theory, BitAutoData] + public async Task PutExcludeFromReports_CallsCommandWithCallingUser( + SutProvider sutProvider, Guid cipherId, Guid userId) + { + sutProvider.GetDependency().GetProperUserId(Arg.Any()).Returns(userId); + + await sutProvider.Sut.PutExcludeFromReportsAsync(cipherId); + + await sutProvider.GetDependency().Received(1).ExcludeAsync(cipherId, userId); + } +} diff --git a/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs b/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs new file mode 100644 index 000000000000..4caceac150f8 --- /dev/null +++ b/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs @@ -0,0 +1,60 @@ +using Bit.Core.Billing.Premium.Queries; +using Bit.Core.Dirt.Reports.ReportFeatures; +using Bit.Core.Dirt.Repositories; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.Models; +using Bit.Core.Platform.Push; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.Dirt.ReportFeatures; + +[SutProviderCustomize] +public class ExcludeCipherFromReportsCommandTests +{ + [Theory, BitAutoData] + public async Task ExcludeAsync_WithoutPremium_ThrowsBadRequest_AndDoesNotWrite( + SutProvider sutProvider, Guid cipherId, Guid userId) + { + sutProvider.GetDependency().HasPremiumAccessAsync(userId).Returns(false); + + await Assert.ThrowsAsync(() => sutProvider.Sut.ExcludeAsync(cipherId, userId)); + + await sutProvider.GetDependency() + .DidNotReceiveWithAnyArgs().ExcludeAsync(default!, default); + await sutProvider.GetDependency() + .DidNotReceiveWithAnyArgs().PushAsync(Arg.Any>()); + } + + [Theory, BitAutoData] + public async Task ExcludeAsync_NothingExcluded_ThrowsNotFound_AndDoesNotPush( + SutProvider sutProvider, Guid cipherId, Guid userId) + { + sutProvider.GetDependency().HasPremiumAccessAsync(userId).Returns(true); + sutProvider.GetDependency() + .ExcludeAsync(Arg.Any>(), userId).Returns(0); + + await Assert.ThrowsAsync(() => sutProvider.Sut.ExcludeAsync(cipherId, userId)); + + await sutProvider.GetDependency() + .DidNotReceiveWithAnyArgs().PushAsync(Arg.Any>()); + } + + [Theory, BitAutoData] + public async Task ExcludeAsync_Excluded_PushesCipherSyncExcludingCurrentContext( + SutProvider sutProvider, Guid cipherId, Guid userId) + { + sutProvider.GetDependency().HasPremiumAccessAsync(userId).Returns(true); + sutProvider.GetDependency() + .ExcludeAsync(Arg.Is>(ids => ids.Single() == cipherId), userId).Returns(1); + + await sutProvider.Sut.ExcludeAsync(cipherId, userId); + + await sutProvider.GetDependency().Received(1) + .PushAsync(Arg.Is>(n => + n.Type == PushType.SyncCiphers && n.TargetId == userId && n.ExcludeCurrentContext)); + } +} diff --git a/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs new file mode 100644 index 000000000000..23041387241d --- /dev/null +++ b/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs @@ -0,0 +1,247 @@ +using Bit.Core.AdminConsole.Entities; +using Bit.Core.Dirt.Repositories; +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Models.Data; +using Bit.Core.Repositories; +using Bit.Core.Vault.Entities; +using Bit.Core.Vault.Enums; +using Bit.Core.Vault.Repositories; +using Xunit; + +namespace Bit.Infrastructure.IntegrationTest.Dirt.Repositories; + +public class CipherReportExclusionRepositoryTests +{ + [DatabaseTheory, DatabaseData] + public async Task ExcludeAsync_PersonalLogin_ReturnsOneAndSetsUsersReportExcludedDate( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var user = await CreateUserAsync(userRepository); + var cipher = await CreateCipherAsync(cipherRepository, user.Id); + + var excluded = await sut.ExcludeAsync([cipher.Id], user.Id); + + Assert.Equal(1, excluded); + var details = await cipherRepository.GetByIdAsync(cipher.Id, user.Id); + Assert.NotNull(details.ReportExcludedDate); + } + + [DatabaseTheory, DatabaseData] + public async Task ExcludeAsync_OrganizationCipher_ReturnsZero( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository, + IOrganizationRepository organizationRepository) + { + var user = await CreateUserAsync(userRepository); + var org = await CreateOrganizationAsync(organizationRepository, user); + var cipher = await cipherRepository.CreateAsync(new Cipher + { + Type = CipherType.Login, + UserId = user.Id, + OrganizationId = org.Id, + Data = "", + }); + + Assert.Equal(0, await sut.ExcludeAsync([cipher.Id], user.Id)); + } + + [DatabaseTheory, DatabaseData] + public async Task ExcludeAsync_NonLoginCipher_ReturnsZero( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var user = await CreateUserAsync(userRepository); + var cipher = await CreateCipherAsync(cipherRepository, user.Id, CipherType.SecureNote); + + Assert.Equal(0, await sut.ExcludeAsync([cipher.Id], user.Id)); + } + + [DatabaseTheory, DatabaseData] + public async Task ExcludeAsync_DeletedCipher_ReturnsZero( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var user = await CreateUserAsync(userRepository); + var cipher = await CreateCipherAsync(cipherRepository, user.Id); + await cipherRepository.SoftDeleteAsync([cipher.Id], user.Id); + + Assert.Equal(0, await sut.ExcludeAsync([cipher.Id], user.Id)); + } + + [DatabaseTheory, DatabaseData] + public async Task ExcludeAsync_OtherUsersCipher_ReturnsZero( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var owner = await CreateUserAsync(userRepository); + var otherUser = await CreateUserAsync(userRepository); + var cipher = await CreateCipherAsync(cipherRepository, owner.Id); + + Assert.Equal(0, await sut.ExcludeAsync([cipher.Id], otherUser.Id)); + Assert.Null((await cipherRepository.GetByIdAsync(cipher.Id, owner.Id)).ReportExcludedDate); + } + + [DatabaseTheory, DatabaseData] + public async Task ExcludeAsync_AlreadyExcluded_ReturnsOne( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var user = await CreateUserAsync(userRepository); + var cipher = await CreateCipherAsync(cipherRepository, user.Id); + await sut.ExcludeAsync([cipher.Id], user.Id); + var firstDate = (await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ReportExcludedDate; + + Assert.Equal(1, await sut.ExcludeAsync([cipher.Id], user.Id)); + Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ReportExcludedDate >= firstDate); + } + + [DatabaseTheory, DatabaseData] + public async Task ExcludeAsync_DoesNotChangeCipherRevisionDate( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var user = await CreateUserAsync(userRepository); + var cipher = await CreateCipherAsync(cipherRepository, user.Id); + var before = (await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).RevisionDate; + + await sut.ExcludeAsync([cipher.Id], user.Id); + + Assert.Equal(before, (await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).RevisionDate); + } + + [DatabaseTheory, DatabaseData] + public async Task ExcludeAsync_BumpsAccountRevisionDate( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var user = await CreateUserAsync(userRepository); + var cipher = await CreateCipherAsync(cipherRepository, user.Id); + var before = (await userRepository.GetByIdAsync(user.Id))!.AccountRevisionDate; + + await sut.ExcludeAsync([cipher.Id], user.Id); + + Assert.True((await userRepository.GetByIdAsync(user.Id))!.AccountRevisionDate > before); + } + + [DatabaseTheory, DatabaseData] + public async Task GetManyByUserIdAsync_ReturnsReportExcludedDate( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var user = await CreateUserAsync(userRepository); + var excludedCipher = await CreateCipherAsync(cipherRepository, user.Id); + var otherCipher = await CreateCipherAsync(cipherRepository, user.Id); + + await sut.ExcludeAsync([excludedCipher.Id], user.Id); + + var ciphers = await cipherRepository.GetManyByUserIdAsync(user.Id); + Assert.NotNull(ciphers.Single(c => c.Id == excludedCipher.Id).ReportExcludedDate); + Assert.Null(ciphers.Single(c => c.Id == otherCipher.Id).ReportExcludedDate); + } + + [DatabaseTheory, DatabaseData] + public async Task ReplaceAsync_CipherDetails_PreservesReportExclusions( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var user = await CreateUserAsync(userRepository); + var cipher = await CreateCipherAsync(cipherRepository, user.Id); + await sut.ExcludeAsync([cipher.Id], user.Id); + + var details = await cipherRepository.GetByIdAsync(cipher.Id, user.Id); + details.Data = "{\"updated\":true}"; + await cipherRepository.ReplaceAsync(details); + + Assert.NotNull((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ReportExcludedDate); + } + + [DatabaseTheory, DatabaseData] + public async Task ReplaceAsync_Cipher_PreservesReportExclusions( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var user = await CreateUserAsync(userRepository); + var cipher = await CreateCipherAsync(cipherRepository, user.Id); + await sut.ExcludeAsync([cipher.Id], user.Id); + + cipher.Data = "{\"updated\":true}"; + await cipherRepository.ReplaceAsync(cipher); + + Assert.NotNull((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ReportExcludedDate); + } + + [DatabaseTheory, DatabaseData] + public async Task SoftDeleteAsync_KeepsReportExclusion( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var user = await CreateUserAsync(userRepository); + var cipher = await CreateCipherAsync(cipherRepository, user.Id); + await sut.ExcludeAsync([cipher.Id], user.Id); + + await cipherRepository.SoftDeleteAsync([cipher.Id], user.Id); + + Assert.NotNull((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ReportExcludedDate); + } + + [DatabaseTheory, DatabaseData] + public async Task DeleteAsync_RemovesExclusion( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var user = await CreateUserAsync(userRepository); + var cipher = await CreateCipherAsync(cipherRepository, user.Id); + await sut.ExcludeAsync([cipher.Id], user.Id); + + await cipherRepository.DeleteAsync(cipher); + + Assert.DoesNotContain(await cipherRepository.GetManyByUserIdAsync(user.Id), c => c.Id == cipher.Id); + } + + [DatabaseTheory, DatabaseData] + public async Task ReplaceAsync_ShareToOrg_KeepsOwnersExclusionOnly( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository, + IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, + ICollectionRepository collectionRepository) + { + var owner = await CreateUserAsync(userRepository); + var member = await CreateUserAsync(userRepository); + var org = await CreateOrganizationAsync(organizationRepository, owner); + var ownerOrgUser = await CreateOrgUserAsync(organizationUserRepository, org, owner, OrganizationUserType.Owner); + var memberOrgUser = await CreateOrgUserAsync(organizationUserRepository, org, member, OrganizationUserType.User); + var collection = await collectionRepository.CreateAsync(new Collection { Name = "Shared", OrganizationId = org.Id }); + await collectionRepository.UpdateUsersAsync(collection.Id, new List + { + new() { Id = ownerOrgUser.Id, Manage = true }, + new() { Id = memberOrgUser.Id, Manage = true }, + }); + var cipher = await CreateCipherAsync(cipherRepository, owner.Id); + await sut.ExcludeAsync([cipher.Id], owner.Id); + + cipher.OrganizationId = org.Id; + await cipherRepository.ReplaceAsync(cipher, [collection.Id]); + + Assert.NotNull((await cipherRepository.GetByIdAsync(cipher.Id, owner.Id)).ReportExcludedDate); + Assert.Null((await cipherRepository.GetManyByUserIdAsync(member.Id)).Single(c => c.Id == cipher.Id).ReportExcludedDate); + } + + private static Task CreateUserAsync(IUserRepository userRepository) => + userRepository.CreateAsync(new User + { + Name = "Test User", + Email = $"test+{Guid.NewGuid()}@email.com", + ApiKey = "TEST", + SecurityStamp = "stamp", + }); + + private static Task CreateCipherAsync(ICipherRepository cipherRepository, Guid userId, + CipherType type = CipherType.Login) => + cipherRepository.CreateAsync(new Cipher { Type = type, UserId = userId, Data = "" }); + + private static Task CreateOrganizationAsync(IOrganizationRepository organizationRepository, User user) => + organizationRepository.CreateAsync(new Organization + { + Name = "Test Organization", + BillingEmail = user.Email, + Plan = "Test", + }); + + private static Task CreateOrgUserAsync(IOrganizationUserRepository organizationUserRepository, + Organization org, User user, OrganizationUserType type) => + organizationUserRepository.CreateAsync(new OrganizationUser + { + UserId = user.Id, + OrganizationId = org.Id, + Status = OrganizationUserStatusType.Confirmed, + Type = type, + }); +} From 80ea39a49e21fa2f64ba1ea461df5da44e2e9466 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Oct 2026 13:39:53 -0400 Subject: [PATCH 06/20] fixup! [PM-41931] feat(dirt): Add the cipher report exclusion repository --- .../dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql | 4 +--- .../DbScripts/2026-10-21_00_AddCipherReportExclusions.sql | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql b/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql index 2f873296dd66..372561b58931 100644 --- a/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql +++ b/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql @@ -5,15 +5,13 @@ AS BEGIN SET NOCOUNT ON - DECLARE @UtcNow DATETIME2(7) = SYSUTCDATETIME(); - UPDATE C SET [ReportExclusions] = JSON_MODIFY( COALESCE(C.[ReportExclusions], N'{}'), CONCAT('$."', @UserId, '"'), - CONVERT(NVARCHAR(30), @UtcNow, 127) + CONVERT(NVARCHAR(30), SYSUTCDATETIME(), 127) ) FROM [dbo].[Cipher] C diff --git a/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql b/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql index 6a6022480a42..cb1e0702127a 100644 --- a/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql +++ b/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql @@ -535,15 +535,13 @@ AS BEGIN SET NOCOUNT ON - DECLARE @UtcNow DATETIME2(7) = SYSUTCDATETIME(); - UPDATE C SET [ReportExclusions] = JSON_MODIFY( COALESCE(C.[ReportExclusions], N'{}'), CONCAT('$."', @UserId, '"'), - CONVERT(NVARCHAR(30), @UtcNow, 127) + CONVERT(NVARCHAR(30), SYSUTCDATETIME(), 127) ) FROM [dbo].[Cipher] C From 9aecee9a8633318ac9ab534461cf2a75b9ff4965 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Oct 2026 13:40:12 -0400 Subject: [PATCH 07/20] fixup! [PM-41931] test(dirt): Cover health report exclusions --- .../CipherReportExclusionRepositoryTests.cs | 84 +++++-------------- 1 file changed, 21 insertions(+), 63 deletions(-) diff --git a/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs index 23041387241d..9b63369a515a 100644 --- a/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs +++ b/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs @@ -1,12 +1,11 @@ -using Bit.Core.AdminConsole.Entities; -using Bit.Core.Dirt.Repositories; +using Bit.Core.Dirt.Repositories; using Bit.Core.Entities; -using Bit.Core.Enums; using Bit.Core.Models.Data; using Bit.Core.Repositories; using Bit.Core.Vault.Entities; using Bit.Core.Vault.Enums; using Bit.Core.Vault.Repositories; +using Bit.Infrastructure.IntegrationTest.AdminConsole; using Xunit; namespace Bit.Infrastructure.IntegrationTest.Dirt.Repositories; @@ -17,7 +16,7 @@ public class CipherReportExclusionRepositoryTests public async Task ExcludeAsync_PersonalLogin_ReturnsOneAndSetsUsersReportExcludedDate( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { - var user = await CreateUserAsync(userRepository); + var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); var excluded = await sut.ExcludeAsync([cipher.Id], user.Id); @@ -32,8 +31,8 @@ public async Task ExcludeAsync_OrganizationCipher_ReturnsZero( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository, IOrganizationRepository organizationRepository) { - var user = await CreateUserAsync(userRepository); - var org = await CreateOrganizationAsync(organizationRepository, user); + var user = await userRepository.CreateTestUserAsync(); + var org = await organizationRepository.CreateTestOrganizationAsync(); var cipher = await cipherRepository.CreateAsync(new Cipher { Type = CipherType.Login, @@ -49,7 +48,7 @@ public async Task ExcludeAsync_OrganizationCipher_ReturnsZero( public async Task ExcludeAsync_NonLoginCipher_ReturnsZero( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { - var user = await CreateUserAsync(userRepository); + var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id, CipherType.SecureNote); Assert.Equal(0, await sut.ExcludeAsync([cipher.Id], user.Id)); @@ -59,7 +58,7 @@ public async Task ExcludeAsync_NonLoginCipher_ReturnsZero( public async Task ExcludeAsync_DeletedCipher_ReturnsZero( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { - var user = await CreateUserAsync(userRepository); + var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); await cipherRepository.SoftDeleteAsync([cipher.Id], user.Id); @@ -70,8 +69,8 @@ public async Task ExcludeAsync_DeletedCipher_ReturnsZero( public async Task ExcludeAsync_OtherUsersCipher_ReturnsZero( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { - var owner = await CreateUserAsync(userRepository); - var otherUser = await CreateUserAsync(userRepository); + var owner = await userRepository.CreateTestUserAsync(); + var otherUser = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, owner.Id); Assert.Equal(0, await sut.ExcludeAsync([cipher.Id], otherUser.Id)); @@ -82,20 +81,18 @@ public async Task ExcludeAsync_OtherUsersCipher_ReturnsZero( public async Task ExcludeAsync_AlreadyExcluded_ReturnsOne( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { - var user = await CreateUserAsync(userRepository); + var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); await sut.ExcludeAsync([cipher.Id], user.Id); - var firstDate = (await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ReportExcludedDate; Assert.Equal(1, await sut.ExcludeAsync([cipher.Id], user.Id)); - Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ReportExcludedDate >= firstDate); } [DatabaseTheory, DatabaseData] public async Task ExcludeAsync_DoesNotChangeCipherRevisionDate( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { - var user = await CreateUserAsync(userRepository); + var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); var before = (await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).RevisionDate; @@ -108,7 +105,7 @@ public async Task ExcludeAsync_DoesNotChangeCipherRevisionDate( public async Task ExcludeAsync_BumpsAccountRevisionDate( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { - var user = await CreateUserAsync(userRepository); + var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); var before = (await userRepository.GetByIdAsync(user.Id))!.AccountRevisionDate; @@ -121,7 +118,7 @@ public async Task ExcludeAsync_BumpsAccountRevisionDate( public async Task GetManyByUserIdAsync_ReturnsReportExcludedDate( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { - var user = await CreateUserAsync(userRepository); + var user = await userRepository.CreateTestUserAsync(); var excludedCipher = await CreateCipherAsync(cipherRepository, user.Id); var otherCipher = await CreateCipherAsync(cipherRepository, user.Id); @@ -136,7 +133,7 @@ public async Task GetManyByUserIdAsync_ReturnsReportExcludedDate( public async Task ReplaceAsync_CipherDetails_PreservesReportExclusions( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { - var user = await CreateUserAsync(userRepository); + var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); await sut.ExcludeAsync([cipher.Id], user.Id); @@ -151,7 +148,7 @@ public async Task ReplaceAsync_CipherDetails_PreservesReportExclusions( public async Task ReplaceAsync_Cipher_PreservesReportExclusions( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { - var user = await CreateUserAsync(userRepository); + var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); await sut.ExcludeAsync([cipher.Id], user.Id); @@ -165,7 +162,7 @@ public async Task ReplaceAsync_Cipher_PreservesReportExclusions( public async Task SoftDeleteAsync_KeepsReportExclusion( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { - var user = await CreateUserAsync(userRepository); + var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); await sut.ExcludeAsync([cipher.Id], user.Id); @@ -174,30 +171,17 @@ public async Task SoftDeleteAsync_KeepsReportExclusion( Assert.NotNull((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ReportExcludedDate); } - [DatabaseTheory, DatabaseData] - public async Task DeleteAsync_RemovesExclusion( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) - { - var user = await CreateUserAsync(userRepository); - var cipher = await CreateCipherAsync(cipherRepository, user.Id); - await sut.ExcludeAsync([cipher.Id], user.Id); - - await cipherRepository.DeleteAsync(cipher); - - Assert.DoesNotContain(await cipherRepository.GetManyByUserIdAsync(user.Id), c => c.Id == cipher.Id); - } - [DatabaseTheory, DatabaseData] public async Task ReplaceAsync_ShareToOrg_KeepsOwnersExclusionOnly( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository, IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, ICollectionRepository collectionRepository) { - var owner = await CreateUserAsync(userRepository); - var member = await CreateUserAsync(userRepository); - var org = await CreateOrganizationAsync(organizationRepository, owner); - var ownerOrgUser = await CreateOrgUserAsync(organizationUserRepository, org, owner, OrganizationUserType.Owner); - var memberOrgUser = await CreateOrgUserAsync(organizationUserRepository, org, member, OrganizationUserType.User); + var owner = await userRepository.CreateTestUserAsync(); + var member = await userRepository.CreateTestUserAsync(); + var org = await organizationRepository.CreateTestOrganizationAsync(); + var ownerOrgUser = await organizationUserRepository.CreateTestOrganizationUserAsync(org, owner); + var memberOrgUser = await organizationUserRepository.CreateTestOrganizationUserAsync(org, member); var collection = await collectionRepository.CreateAsync(new Collection { Name = "Shared", OrganizationId = org.Id }); await collectionRepository.UpdateUsersAsync(collection.Id, new List { @@ -213,35 +197,9 @@ public async Task ReplaceAsync_ShareToOrg_KeepsOwnersExclusionOnly( Assert.NotNull((await cipherRepository.GetByIdAsync(cipher.Id, owner.Id)).ReportExcludedDate); Assert.Null((await cipherRepository.GetManyByUserIdAsync(member.Id)).Single(c => c.Id == cipher.Id).ReportExcludedDate); } - - private static Task CreateUserAsync(IUserRepository userRepository) => - userRepository.CreateAsync(new User - { - Name = "Test User", - Email = $"test+{Guid.NewGuid()}@email.com", - ApiKey = "TEST", - SecurityStamp = "stamp", - }); - private static Task CreateCipherAsync(ICipherRepository cipherRepository, Guid userId, CipherType type = CipherType.Login) => cipherRepository.CreateAsync(new Cipher { Type = type, UserId = userId, Data = "" }); - private static Task CreateOrganizationAsync(IOrganizationRepository organizationRepository, User user) => - organizationRepository.CreateAsync(new Organization - { - Name = "Test Organization", - BillingEmail = user.Email, - Plan = "Test", - }); - private static Task CreateOrgUserAsync(IOrganizationUserRepository organizationUserRepository, - Organization org, User user, OrganizationUserType type) => - organizationUserRepository.CreateAsync(new OrganizationUser - { - UserId = user.Id, - OrganizationId = org.Id, - Status = OrganizationUserStatusType.Confirmed, - Type = type, - }); } From 573111abf5bced347e3b353cd7a6b10ffff8820c Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Oct 2026 14:27:59 -0400 Subject: [PATCH 08/20] [PM-41931] fix(dirt): Return report exclusion date in EF sync without organizations The EF GetManyByUserIdAsync branch for users without organizations rebuilds CipherDetails field by field and dropped ReportExcludedDate, so sync on Postgres, MySQL and SQLite returned null for users with no org. MSSQL was unaffected (its procedure selects every column). --- .../Vault/Repositories/CipherRepository.cs | 1 + .../CipherReportExclusionRepositoryTests.cs | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs b/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs index 51933fee3ac6..ee45305675bf 100644 --- a/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs +++ b/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs @@ -502,6 +502,7 @@ public async Task> GetManyByUserIdAsync(Guid userId, OrganizationUseTotp = false, Key = c.Key, ArchivedDate = c.ArchivedDate, + ReportExcludedDate = c.ReportExcludedDate, }; } diff --git a/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs index 9b63369a515a..09ac45df0065 100644 --- a/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs +++ b/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs @@ -129,6 +129,19 @@ public async Task GetManyByUserIdAsync_ReturnsReportExcludedDate( Assert.Null(ciphers.Single(c => c.Id == otherCipher.Id).ReportExcludedDate); } + [DatabaseTheory, DatabaseData] + public async Task GetManyByUserIdAsync_WithoutOrganizations_ReturnsReportExcludedDate( + ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + { + var user = await userRepository.CreateTestUserAsync(); + var cipher = await CreateCipherAsync(cipherRepository, user.Id); + + await sut.ExcludeAsync([cipher.Id], user.Id); + + var ciphers = await cipherRepository.GetManyByUserIdAsync(user.Id, withOrganizations: false); + Assert.NotNull(ciphers.Single(c => c.Id == cipher.Id).ReportExcludedDate); + } + [DatabaseTheory, DatabaseData] public async Task ReplaceAsync_CipherDetails_PreservesReportExclusions( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) From c6a8fba8a9f293f729f777f39e3f684250ba3e32 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Oct 2026 15:01:01 -0400 Subject: [PATCH 09/20] [PM-41931] refactor(dirt): Store report exclusions as a per-user flag Store { "": true } instead of the exclusion date, matching the tech breakdown and the Favorites pattern. Nothing reads the date, and a flag tells the server less. Ciphers now expose a per-user ExcludedFromReports boolean in place of ReportExcludedDate. --- .../Models/Response/CipherResponseModel.cs | 4 +-- src/Core/Vault/Models/Data/CipherDetails.cs | 6 ++--- .../Dirt/CipherReportExclusionRepository.cs | 9 +++---- .../Queries/UserCipherDetailsQuery.cs | 22 +--------------- .../Vault/Repositories/CipherRepository.cs | 2 +- .../Queries/CipherDetailsQuery.cs | 4 +-- .../Cipher_ExcludeFromReports.sql | 2 +- src/Sql/dbo/Vault/Functions/CipherDetails.sql | 7 ++--- .../Cipher/CipherDetails_Create.sql | 2 +- .../CipherDetails_CreateWithCollections.sql | 2 +- .../Cipher/CipherDetails_ReadByIdUserId.sql | 4 +-- .../Cipher/CipherDetails_Update.sql | 2 +- .../CipherReportExclusionRepositoryTests.cs | 26 +++++++++---------- ...026-10-21_00_AddCipherReportExclusions.sql | 19 +++++++------- 14 files changed, 45 insertions(+), 66 deletions(-) diff --git a/src/Api/Vault/Models/Response/CipherResponseModel.cs b/src/Api/Vault/Models/Response/CipherResponseModel.cs index 8ab8e5fa4404..d737759f0b11 100644 --- a/src/Api/Vault/Models/Response/CipherResponseModel.cs +++ b/src/Api/Vault/Models/Response/CipherResponseModel.cs @@ -296,7 +296,7 @@ protected CipherResponseModel( Favorite = cipher.Favorite; Edit = cipher.Edit; ArchivedDate = cipher.ArchivedDate; - ReportExcludedDate = cipher.ReportExcludedDate; + ExcludedFromReports = cipher.ExcludedFromReports; ViewPassword = cipher.ViewPassword; Permissions = new CipherPermissionsResponseModel(user, cipher, organizationAbility); } @@ -313,7 +313,7 @@ public static CipherResponseModel From(FullCipherAccess access, CipherDetails ci public bool Edit { get; set; } public bool ViewPassword { get; set; } public DateTime? ArchivedDate { get; set; } - public DateTime? ReportExcludedDate { get; set; } + public bool ExcludedFromReports { get; set; } public CipherPermissionsResponseModel Permissions { get; set; } } diff --git a/src/Core/Vault/Models/Data/CipherDetails.cs b/src/Core/Vault/Models/Data/CipherDetails.cs index 49bb9dc1079e..124c9888549d 100644 --- a/src/Core/Vault/Models/Data/CipherDetails.cs +++ b/src/Core/Vault/Models/Data/CipherDetails.cs @@ -11,8 +11,8 @@ public class CipherDetails : CipherOrganizationDetails public bool Manage { get; set; } // Per-user archived date from Archives JSON. public DateTime? ArchivedDate { get; set; } - // Per-user health report exclusion date from ReportExclusions JSON. - public DateTime? ReportExcludedDate { get; set; } + // Per-user health report exclusion flag from ReportExclusions JSON. + public bool ExcludedFromReports { get; set; } public CipherDetails() { } public CipherDetails(CipherOrganizationDetails cipher) @@ -55,7 +55,7 @@ public CipherDetailsWithCollections( Key = cipher.Key; FolderId = cipher.FolderId; ArchivedDate = cipher.ArchivedDate; - ReportExcludedDate = cipher.ReportExcludedDate; + ExcludedFromReports = cipher.ExcludedFromReports; Favorite = cipher.Favorite; Edit = cipher.Edit; ViewPassword = cipher.ViewPassword; diff --git a/src/Infrastructure.EntityFramework/Dirt/CipherReportExclusionRepository.cs b/src/Infrastructure.EntityFramework/Dirt/CipherReportExclusionRepository.cs index 272202e39d45..bf3393fa92fd 100644 --- a/src/Infrastructure.EntityFramework/Dirt/CipherReportExclusionRepository.cs +++ b/src/Infrastructure.EntityFramework/Dirt/CipherReportExclusionRepository.cs @@ -25,14 +25,13 @@ public async Task ExcludeAsync(IEnumerable cipherIds, Guid userId) && c.Type == CipherType.Login && c.DeletedDate == null) .ToListAsync(); - var utcNow = DateTime.UtcNow; foreach (var cipher in ciphers) { var exclusions = string.IsNullOrWhiteSpace(cipher.ReportExclusions) - ? new Dictionary() - : CoreHelpers.LoadClassFromJsonData>(cipher.ReportExclusions) - ?? new Dictionary(); - exclusions[userId] = utcNow; + ? new Dictionary() + : CoreHelpers.LoadClassFromJsonData>(cipher.ReportExclusions) + ?? new Dictionary(); + exclusions[userId] = true; cipher.ReportExclusions = CoreHelpers.ClassToJsonData(exclusions); } diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs b/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs index aeecd8e16d32..ef9ed692da15 100644 --- a/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs +++ b/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs @@ -120,7 +120,7 @@ from cg in cg_g.DefaultIfEmpty() OrganizationUseTotp = c.OrganizationUseTotp, Key = c.Key, ArchivedDate = GetArchivedDate(_userId, new Cipher { Id = c.Id, Archives = c.Archives }), - ReportExcludedDate = GetReportExcludedDate(_userId, new Cipher { Id = c.Id, ReportExclusions = c.ReportExclusions }) + ExcludedFromReports = _userId.HasValue && c.ReportExclusions != null && c.ReportExclusions.ToLowerInvariant().Contains($"\"{_userId}\":true") }); return union; } @@ -146,26 +146,6 @@ from cg in cg_g.DefaultIfEmpty() } } - private static DateTime? GetReportExcludedDate(Guid? userId, Cipher cipher) - { - try - { - if (userId.HasValue && !string.IsNullOrWhiteSpace(cipher.ReportExclusions)) - { - var exclusions = JsonSerializer.Deserialize>(cipher.ReportExclusions); - if (exclusions.TryGetValue(userId.Value, out var excludedDate)) - { - return excludedDate; - } - } - - return null; - } - catch - { - return null; - } - } private static Guid? GetFolderId(Guid? userId, Cipher cipher) { diff --git a/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs b/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs index ee45305675bf..ced1770c5a21 100644 --- a/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs +++ b/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs @@ -502,7 +502,7 @@ public async Task> GetManyByUserIdAsync(Guid userId, OrganizationUseTotp = false, Key = c.Key, ArchivedDate = c.ArchivedDate, - ReportExcludedDate = c.ReportExcludedDate, + ExcludedFromReports = c.ExcludedFromReports, }; } diff --git a/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs b/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs index fa471cf5e72f..996cde102233 100644 --- a/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs +++ b/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs @@ -37,9 +37,7 @@ public virtual IQueryable Run(DatabaseContext dbContext) ArchivedDate = !_userId.HasValue || c.Archives == null || !c.Archives.ToLowerInvariant().Contains(_userId.Value.ToString()) ? null : CoreHelpers.LoadClassFromJsonData>(c.Archives)[_userId.Value], - ReportExcludedDate = !_userId.HasValue || c.ReportExclusions == null || !c.ReportExclusions.ToLowerInvariant().Contains(_userId.Value.ToString()) ? - null : - CoreHelpers.LoadClassFromJsonData>(c.ReportExclusions)[_userId.Value], + ExcludedFromReports = _userId.HasValue && c.ReportExclusions != null && c.ReportExclusions.ToLowerInvariant().Contains($"\"{_userId}\":true"), }; return query; } diff --git a/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql b/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql index 372561b58931..f2a3984d66c2 100644 --- a/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql +++ b/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql @@ -11,7 +11,7 @@ BEGIN [ReportExclusions] = JSON_MODIFY( COALESCE(C.[ReportExclusions], N'{}'), CONCAT('$."', @UserId, '"'), - CONVERT(NVARCHAR(30), SYSUTCDATETIME(), 127) + CAST(1 AS BIT) ) FROM [dbo].[Cipher] C diff --git a/src/Sql/dbo/Vault/Functions/CipherDetails.sql b/src/Sql/dbo/Vault/Functions/CipherDetails.sql index f1b700889d3d..d1a30fd2648c 100644 --- a/src/Sql/dbo/Vault/Functions/CipherDetails.sql +++ b/src/Sql/dbo/Vault/Functions/CipherDetails.sql @@ -39,8 +39,9 @@ SELECT WHEN @UserId IS NULL OR C.[ReportExclusions] IS NULL - THEN NULL - ELSE TRY_CONVERT(DATETIME2(7), JSON_VALUE(C.[ReportExclusions], CONCAT('$."', @UserId, '"'))) - END [ReportExcludedDate] + OR JSON_VALUE(C.[ReportExclusions], CONCAT('$."', @UserId, '"')) IS NULL + THEN 0 + ELSE 1 + END [ExcludedFromReports] FROM [dbo].[Cipher] C; diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Create.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Create.sql index 4100650cf366..8d3e2df0560d 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Create.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Create.sql @@ -20,7 +20,7 @@ @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used - @ReportExcludedDate DATETIME2(7) = NULL, -- not used + @ExcludedFromReports BIT = NULL, -- not used @ReportExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql index 4e02aeb870bf..cd2d4c37d376 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql @@ -20,7 +20,7 @@ @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used - @ReportExcludedDate DATETIME2(7) = NULL, -- not used + @ExcludedFromReports BIT = NULL, -- not used @ReportExclusions NVARCHAR(MAX) = NULL, -- not used @CollectionIds AS [dbo].[GuidIdArray] READONLY AS diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_ReadByIdUserId.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_ReadByIdUserId.sql index 20e7450d8d6c..21b6e8c2add6 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_ReadByIdUserId.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_ReadByIdUserId.sql @@ -21,7 +21,7 @@ SELECT [Key], [OrganizationUseTotp], [ArchivedDate], - [ReportExcludedDate], + [ExcludedFromReports], MAX ([Edit]) AS [Edit], MAX ([ViewPassword]) AS [ViewPassword], MAX ([Manage]) AS [Manage] @@ -45,5 +45,5 @@ SELECT [Key], [OrganizationUseTotp], [ArchivedDate], - [ReportExcludedDate] + [ExcludedFromReports] END diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Update.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Update.sql index f6c57134418b..0b4f4332a319 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Update.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Update.sql @@ -20,7 +20,7 @@ @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used - @ReportExcludedDate DATETIME2(7) = NULL, -- not used + @ExcludedFromReports BIT = NULL, -- not used @ReportExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN diff --git a/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs index 09ac45df0065..e17fb5a38f00 100644 --- a/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs +++ b/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs @@ -13,7 +13,7 @@ namespace Bit.Infrastructure.IntegrationTest.Dirt.Repositories; public class CipherReportExclusionRepositoryTests { [DatabaseTheory, DatabaseData] - public async Task ExcludeAsync_PersonalLogin_ReturnsOneAndSetsUsersReportExcludedDate( + public async Task ExcludeAsync_PersonalLogin_ReturnsOneAndSetsUsersExcludedFromReports( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); @@ -23,7 +23,7 @@ public async Task ExcludeAsync_PersonalLogin_ReturnsOneAndSetsUsersReportExclude Assert.Equal(1, excluded); var details = await cipherRepository.GetByIdAsync(cipher.Id, user.Id); - Assert.NotNull(details.ReportExcludedDate); + Assert.True(details.ExcludedFromReports); } [DatabaseTheory, DatabaseData] @@ -74,7 +74,7 @@ public async Task ExcludeAsync_OtherUsersCipher_ReturnsZero( var cipher = await CreateCipherAsync(cipherRepository, owner.Id); Assert.Equal(0, await sut.ExcludeAsync([cipher.Id], otherUser.Id)); - Assert.Null((await cipherRepository.GetByIdAsync(cipher.Id, owner.Id)).ReportExcludedDate); + Assert.False((await cipherRepository.GetByIdAsync(cipher.Id, owner.Id)).ExcludedFromReports); } [DatabaseTheory, DatabaseData] @@ -115,7 +115,7 @@ public async Task ExcludeAsync_BumpsAccountRevisionDate( } [DatabaseTheory, DatabaseData] - public async Task GetManyByUserIdAsync_ReturnsReportExcludedDate( + public async Task GetManyByUserIdAsync_ReturnsExcludedFromReports( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); @@ -125,12 +125,12 @@ public async Task GetManyByUserIdAsync_ReturnsReportExcludedDate( await sut.ExcludeAsync([excludedCipher.Id], user.Id); var ciphers = await cipherRepository.GetManyByUserIdAsync(user.Id); - Assert.NotNull(ciphers.Single(c => c.Id == excludedCipher.Id).ReportExcludedDate); - Assert.Null(ciphers.Single(c => c.Id == otherCipher.Id).ReportExcludedDate); + Assert.True(ciphers.Single(c => c.Id == excludedCipher.Id).ExcludedFromReports); + Assert.False(ciphers.Single(c => c.Id == otherCipher.Id).ExcludedFromReports); } [DatabaseTheory, DatabaseData] - public async Task GetManyByUserIdAsync_WithoutOrganizations_ReturnsReportExcludedDate( + public async Task GetManyByUserIdAsync_WithoutOrganizations_ReturnsExcludedFromReports( ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); @@ -139,7 +139,7 @@ public async Task GetManyByUserIdAsync_WithoutOrganizations_ReturnsReportExclude await sut.ExcludeAsync([cipher.Id], user.Id); var ciphers = await cipherRepository.GetManyByUserIdAsync(user.Id, withOrganizations: false); - Assert.NotNull(ciphers.Single(c => c.Id == cipher.Id).ReportExcludedDate); + Assert.True(ciphers.Single(c => c.Id == cipher.Id).ExcludedFromReports); } [DatabaseTheory, DatabaseData] @@ -154,7 +154,7 @@ public async Task ReplaceAsync_CipherDetails_PreservesReportExclusions( details.Data = "{\"updated\":true}"; await cipherRepository.ReplaceAsync(details); - Assert.NotNull((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ReportExcludedDate); + Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ExcludedFromReports); } [DatabaseTheory, DatabaseData] @@ -168,7 +168,7 @@ public async Task ReplaceAsync_Cipher_PreservesReportExclusions( cipher.Data = "{\"updated\":true}"; await cipherRepository.ReplaceAsync(cipher); - Assert.NotNull((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ReportExcludedDate); + Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ExcludedFromReports); } [DatabaseTheory, DatabaseData] @@ -181,7 +181,7 @@ public async Task SoftDeleteAsync_KeepsReportExclusion( await cipherRepository.SoftDeleteAsync([cipher.Id], user.Id); - Assert.NotNull((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ReportExcludedDate); + Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ExcludedFromReports); } [DatabaseTheory, DatabaseData] @@ -207,8 +207,8 @@ public async Task ReplaceAsync_ShareToOrg_KeepsOwnersExclusionOnly( cipher.OrganizationId = org.Id; await cipherRepository.ReplaceAsync(cipher, [collection.Id]); - Assert.NotNull((await cipherRepository.GetByIdAsync(cipher.Id, owner.Id)).ReportExcludedDate); - Assert.Null((await cipherRepository.GetManyByUserIdAsync(member.Id)).Single(c => c.Id == cipher.Id).ReportExcludedDate); + Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, owner.Id)).ExcludedFromReports); + Assert.False((await cipherRepository.GetManyByUserIdAsync(member.Id)).Single(c => c.Id == cipher.Id).ExcludedFromReports); } private static Task CreateCipherAsync(ICipherRepository cipherRepository, Guid userId, CipherType type = CipherType.Login) => diff --git a/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql b/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql index cb1e0702127a..2f2c0af84cfa 100644 --- a/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql +++ b/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql @@ -46,9 +46,10 @@ SELECT WHEN @UserId IS NULL OR C.[ReportExclusions] IS NULL - THEN NULL - ELSE TRY_CONVERT(DATETIME2(7), JSON_VALUE(C.[ReportExclusions], CONCAT('$."', @UserId, '"'))) - END [ReportExcludedDate] + OR JSON_VALUE(C.[ReportExclusions], CONCAT('$."', @UserId, '"')) IS NULL + THEN 0 + ELSE 1 + END [ExcludedFromReports] FROM [dbo].[Cipher] C; GO @@ -295,7 +296,7 @@ CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_Create] @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used - @ReportExcludedDate DATETIME2(7) = NULL, -- not used + @ExcludedFromReports BIT = NULL, -- not used @ReportExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN @@ -370,7 +371,7 @@ CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_CreateWithCollections] @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used - @ReportExcludedDate DATETIME2(7) = NULL, -- not used + @ExcludedFromReports BIT = NULL, -- not used @ReportExclusions NVARCHAR(MAX) = NULL, -- not used @CollectionIds AS [dbo].[GuidIdArray] READONLY AS @@ -414,7 +415,7 @@ CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_Update] @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used - @ReportExcludedDate DATETIME2(7) = NULL, -- not used + @ExcludedFromReports BIT = NULL, -- not used @ReportExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN @@ -500,7 +501,7 @@ SELECT [Key], [OrganizationUseTotp], [ArchivedDate], - [ReportExcludedDate], + [ExcludedFromReports], MAX ([Edit]) AS [Edit], MAX ([ViewPassword]) AS [ViewPassword], MAX ([Manage]) AS [Manage] @@ -524,7 +525,7 @@ SELECT [Key], [OrganizationUseTotp], [ArchivedDate], - [ReportExcludedDate] + [ExcludedFromReports] END GO @@ -541,7 +542,7 @@ BEGIN [ReportExclusions] = JSON_MODIFY( COALESCE(C.[ReportExclusions], N'{}'), CONCAT('$."', @UserId, '"'), - CONVERT(NVARCHAR(30), SYSUTCDATETIME(), 127) + CAST(1 AS BIT) ) FROM [dbo].[Cipher] C From 341805f23a633644bc259db0268ff3c398fe8139 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Oct 2026 11:14:49 -0400 Subject: [PATCH 10/20] [PM-41931] refactor(dirt): Move the exclude endpoint to its own PR This PR now holds only the database and data-access changes, so it can merge before the endpoint. The command, controller and their unit tests move to a stacked PR. --- .../CipherReportExclusionsController.cs | 33 ---------- .../ExcludeCipherFromReportsCommand.cs | 40 ------------- .../IExcludeCipherFromReportsCommand.cs | 7 --- .../ReportingServiceCollectionExtensions.cs | 1 - .../CipherReportExclusionsControllerTests.cs | 26 -------- .../ExcludeCipherFromReportsCommandTests.cs | 60 ------------------- 6 files changed, 167 deletions(-) delete mode 100644 src/Api/Dirt/Controllers/CipherReportExclusionsController.cs delete mode 100644 src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs delete mode 100644 src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs delete mode 100644 test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs delete mode 100644 test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs diff --git a/src/Api/Dirt/Controllers/CipherReportExclusionsController.cs b/src/Api/Dirt/Controllers/CipherReportExclusionsController.cs deleted file mode 100644 index 0a6bab2e3efb..000000000000 --- a/src/Api/Dirt/Controllers/CipherReportExclusionsController.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Bit.Core; -using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; -using Bit.Core.Services; -using Bitwarden.Server.Sdk.Features; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Bit.Api.Dirt.Controllers; - -[Route("ciphers")] -[Authorize("Application")] -public class CipherReportExclusionsController : Controller -{ - private readonly IUserService _userService; - private readonly IExcludeCipherFromReportsCommand _excludeCipherFromReportsCommand; - - public CipherReportExclusionsController( - IUserService userService, - IExcludeCipherFromReportsCommand excludeCipherFromReportsCommand) - { - _userService = userService; - _excludeCipherFromReportsCommand = excludeCipherFromReportsCommand; - } - - /// Excludes the caller's personal login from their health reports. - [HttpPut("{id}/exclude-from-reports")] - [RequireFeature(FeatureFlagKeys.HealthReportExclusions)] - public async Task PutExcludeFromReportsAsync(Guid id) - { - var userId = _userService.GetProperUserId(User)!.Value; - await _excludeCipherFromReportsCommand.ExcludeAsync(id, userId); - } -} diff --git a/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs b/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs deleted file mode 100644 index 891f0f1a1c01..000000000000 --- a/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Bit.Core.Billing.Premium.Queries; -using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; -using Bit.Core.Dirt.Repositories; -using Bit.Core.Exceptions; -using Bit.Core.Platform.Push; - -namespace Bit.Core.Dirt.Reports.ReportFeatures; - -public class ExcludeCipherFromReportsCommand : IExcludeCipherFromReportsCommand -{ - private readonly ICipherReportExclusionRepository _cipherReportExclusionRepository; - private readonly IHasPremiumAccessQuery _hasPremiumAccessQuery; - private readonly IPushNotificationService _pushService; - - public ExcludeCipherFromReportsCommand( - ICipherReportExclusionRepository cipherReportExclusionRepository, - IHasPremiumAccessQuery hasPremiumAccessQuery, - IPushNotificationService pushService) - { - _cipherReportExclusionRepository = cipherReportExclusionRepository; - _hasPremiumAccessQuery = hasPremiumAccessQuery; - _pushService = pushService; - } - - public async Task ExcludeAsync(Guid cipherId, Guid userId) - { - if (!await _hasPremiumAccessQuery.HasPremiumAccessAsync(userId)) - { - throw new BadRequestException("Premium required to exclude items from reports."); - } - - var excluded = await _cipherReportExclusionRepository.ExcludeAsync([cipherId], userId); - if (excluded == 0) - { - throw new NotFoundException(); - } - - await _pushService.PushSyncCiphersAsync(userId, true); - } -} diff --git a/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs b/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs deleted file mode 100644 index fb0d89921645..000000000000 --- a/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; - -public interface IExcludeCipherFromReportsCommand -{ - /// Excludes the user's personal login from their health reports. Requires premium. - Task ExcludeAsync(Guid cipherId, Guid userId); -} diff --git a/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs b/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs index f2a5e02caf75..6aca3160daca 100644 --- a/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs +++ b/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs @@ -28,7 +28,6 @@ public static void AddReportingServices(this IServiceCollection services, IGloba services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); // v2 file storage commands services.AddScoped(); diff --git a/test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs b/test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs deleted file mode 100644 index b7004e157ee6..000000000000 --- a/test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Security.Claims; -using Bit.Api.Dirt.Controllers; -using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; -using Bit.Core.Services; -using Bit.Test.Common.AutoFixture; -using Bit.Test.Common.AutoFixture.Attributes; -using NSubstitute; -using Xunit; - -namespace Bit.Api.Test.Dirt; - -[ControllerCustomize(typeof(CipherReportExclusionsController))] -[SutProviderCustomize] -public class CipherReportExclusionsControllerTests -{ - [Theory, BitAutoData] - public async Task PutExcludeFromReports_CallsCommandWithCallingUser( - SutProvider sutProvider, Guid cipherId, Guid userId) - { - sutProvider.GetDependency().GetProperUserId(Arg.Any()).Returns(userId); - - await sutProvider.Sut.PutExcludeFromReportsAsync(cipherId); - - await sutProvider.GetDependency().Received(1).ExcludeAsync(cipherId, userId); - } -} diff --git a/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs b/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs deleted file mode 100644 index 4caceac150f8..000000000000 --- a/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Bit.Core.Billing.Premium.Queries; -using Bit.Core.Dirt.Reports.ReportFeatures; -using Bit.Core.Dirt.Repositories; -using Bit.Core.Enums; -using Bit.Core.Exceptions; -using Bit.Core.Models; -using Bit.Core.Platform.Push; -using Bit.Test.Common.AutoFixture; -using Bit.Test.Common.AutoFixture.Attributes; -using NSubstitute; -using Xunit; - -namespace Bit.Core.Test.Dirt.ReportFeatures; - -[SutProviderCustomize] -public class ExcludeCipherFromReportsCommandTests -{ - [Theory, BitAutoData] - public async Task ExcludeAsync_WithoutPremium_ThrowsBadRequest_AndDoesNotWrite( - SutProvider sutProvider, Guid cipherId, Guid userId) - { - sutProvider.GetDependency().HasPremiumAccessAsync(userId).Returns(false); - - await Assert.ThrowsAsync(() => sutProvider.Sut.ExcludeAsync(cipherId, userId)); - - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs().ExcludeAsync(default!, default); - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs().PushAsync(Arg.Any>()); - } - - [Theory, BitAutoData] - public async Task ExcludeAsync_NothingExcluded_ThrowsNotFound_AndDoesNotPush( - SutProvider sutProvider, Guid cipherId, Guid userId) - { - sutProvider.GetDependency().HasPremiumAccessAsync(userId).Returns(true); - sutProvider.GetDependency() - .ExcludeAsync(Arg.Any>(), userId).Returns(0); - - await Assert.ThrowsAsync(() => sutProvider.Sut.ExcludeAsync(cipherId, userId)); - - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs().PushAsync(Arg.Any>()); - } - - [Theory, BitAutoData] - public async Task ExcludeAsync_Excluded_PushesCipherSyncExcludingCurrentContext( - SutProvider sutProvider, Guid cipherId, Guid userId) - { - sutProvider.GetDependency().HasPremiumAccessAsync(userId).Returns(true); - sutProvider.GetDependency() - .ExcludeAsync(Arg.Is>(ids => ids.Single() == cipherId), userId).Returns(1); - - await sutProvider.Sut.ExcludeAsync(cipherId, userId); - - await sutProvider.GetDependency().Received(1) - .PushAsync(Arg.Is>(n => - n.Type == PushType.SyncCiphers && n.TargetId == userId && n.ExcludeCurrentContext)); - } -} From d06457fd59e3136839d4c090466e2a3d1e0b84bb Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Oct 2026 11:32:23 -0400 Subject: [PATCH 11/20] [PM-41931] chore(dirt): Re-date migrations after syncing with main Main gained newer migrations, so the SQL script moves to 2026-10-23 and the three EF migrations are regenerated on top of main's model. --- ...26-10-23_00_AddCipherReportExclusions.sql} | 0 ...949_AddCipherReportExclusions.Designer.cs} | 447 ++++++++++-------- ...261023152949_AddCipherReportExclusions.cs} | 0 ...935_AddCipherReportExclusions.Designer.cs} | 447 ++++++++++-------- ...261023152935_AddCipherReportExclusions.cs} | 0 ...942_AddCipherReportExclusions.Designer.cs} | 447 ++++++++++-------- ...261023152942_AddCipherReportExclusions.cs} | 0 7 files changed, 780 insertions(+), 561 deletions(-) rename util/Migrator/DbScripts/{2026-10-21_00_AddCipherReportExclusions.sql => 2026-10-23_00_AddCipherReportExclusions.sql} (100%) rename util/MySqlMigrations/Migrations/{20261021171939_AddCipherReportExclusions.Designer.cs => 20261023152949_AddCipherReportExclusions.Designer.cs} (98%) rename util/MySqlMigrations/Migrations/{20261021171939_AddCipherReportExclusions.cs => 20261023152949_AddCipherReportExclusions.cs} (100%) rename util/PostgresMigrations/Migrations/{20261021171844_AddCipherReportExclusions.Designer.cs => 20261023152935_AddCipherReportExclusions.Designer.cs} (98%) rename util/PostgresMigrations/Migrations/{20261021171844_AddCipherReportExclusions.cs => 20261023152935_AddCipherReportExclusions.cs} (100%) rename util/SqliteMigrations/Migrations/{20261021171852_AddCipherReportExclusions.Designer.cs => 20261023152942_AddCipherReportExclusions.Designer.cs} (98%) rename util/SqliteMigrations/Migrations/{20261021171852_AddCipherReportExclusions.cs => 20261023152942_AddCipherReportExclusions.cs} (100%) diff --git a/util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql b/util/Migrator/DbScripts/2026-10-23_00_AddCipherReportExclusions.sql similarity index 100% rename from util/Migrator/DbScripts/2026-10-21_00_AddCipherReportExclusions.sql rename to util/Migrator/DbScripts/2026-10-23_00_AddCipherReportExclusions.sql diff --git a/util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.Designer.cs b/util/MySqlMigrations/Migrations/20261023152949_AddCipherReportExclusions.Designer.cs similarity index 98% rename from util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.Designer.cs rename to util/MySqlMigrations/Migrations/20261023152949_AddCipherReportExclusions.Designer.cs index 54c8f45ffc57..446231c54b65 100644 --- a/util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.Designer.cs +++ b/util/MySqlMigrations/Migrations/20261023152949_AddCipherReportExclusions.Designer.cs @@ -12,7 +12,7 @@ namespace Bit.MySqlMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20261021171939_AddCipherReportExclusions")] + [Migration("20261023152949_AddCipherReportExclusions")] partial class AddCipherReportExclusions { /// @@ -475,152 +475,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("Policy", (string)null); }); - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("BillingEmail") - .HasColumnType("longtext"); - - b.Property("BillingPhone") - .HasColumnType("longtext"); - - b.Property("BusinessAddress1") - .HasColumnType("longtext"); - - b.Property("BusinessAddress2") - .HasColumnType("longtext"); - - b.Property("BusinessAddress3") - .HasColumnType("longtext"); - - b.Property("BusinessCountry") - .HasColumnType("longtext"); - - b.Property("BusinessName") - .HasColumnType("longtext"); - - b.Property("BusinessTaxNumber") - .HasColumnType("longtext"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("DiscountId") - .HasColumnType("longtext"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("Gateway") - .HasColumnType("tinyint unsigned"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Status") - .HasColumnType("tinyint unsigned"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UseEvents") - .HasColumnType("tinyint(1)"); - - b.HasKey("Id"); - - b.HasIndex("GatewayCustomerId"); - - b.HasIndex("GatewaySubscriptionId"); - - b.ToTable("Provider", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("ProviderId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Settings") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ProviderId"); - - b.ToTable("ProviderOrganization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Email") - .HasColumnType("longtext"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("Permissions") - .HasColumnType("longtext"); - - b.Property("ProviderId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Status") - .HasColumnType("tinyint unsigned"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("ProviderId"); - - b.HasIndex("UserId"); - - b.ToTable("ProviderUser", (string)null); - }); - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => { b.Property("Id") @@ -868,6 +722,46 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("SsoUser", (string)null); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.TwoFactorRememberToken", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeviceId") + .HasColumnType("char(36)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Stamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DeviceId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ExpirationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "DeviceId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("TwoFactorRememberToken", (string)null); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => { b.Property("Id") @@ -1591,6 +1485,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Id") .HasColumnType("char(36)"); + b.Property("AccessLeaseId") + .HasColumnType("char(36)"); + + b.Property("AccessRequestId") + .HasColumnType("char(36)"); + b.Property("ActingUserId") .HasColumnType("char(36)"); @@ -2915,6 +2815,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("TargetSystemId") .HasColumnType("char(36)"); + b.Property("WrappedKey") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + b.HasKey("Id"); b.HasIndex("CipherId") @@ -3011,6 +2915,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Status") .HasColumnType("tinyint unsigned"); + b.Property("WrappedKey") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + b.HasKey("Id"); b.HasIndex("OrganizationId"); @@ -3047,6 +2955,152 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("Installation", (string)null); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasColumnType("longtext"); + + b.Property("BillingPhone") + .HasColumnType("longtext"); + + b.Property("BusinessAddress1") + .HasColumnType("longtext"); + + b.Property("BusinessAddress2") + .HasColumnType("longtext"); + + b.Property("BusinessAddress3") + .HasColumnType("longtext"); + + b.Property("BusinessCountry") + .HasColumnType("longtext"); + + b.Property("BusinessName") + .HasColumnType("longtext"); + + b.Property("BusinessTaxNumber") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DiscountId") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Settings") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => { b.Property("Id") @@ -3634,42 +3688,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("Organization"); }); - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Provider"); - - b.Navigation("User"); - }); - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => { b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") @@ -3738,6 +3756,25 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("User"); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.TwoFactorRememberToken", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "Device") + .WithMany() + .HasForeignKey("DeviceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Device"); + + b.Navigation("User"); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => { b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") @@ -3789,7 +3826,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => { - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") .WithMany() .HasForeignKey("ProviderId") .OnDelete(DeleteBehavior.Cascade) @@ -3800,7 +3837,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => { - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") .WithMany() .HasForeignKey("ProviderId") .OnDelete(DeleteBehavior.Cascade) @@ -3996,7 +4033,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasForeignKey("OrganizationId") .OnDelete(DeleteBehavior.Cascade); - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") .WithMany() .HasForeignKey("ProviderId") .OnDelete(DeleteBehavior.Cascade); @@ -4034,7 +4071,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .WithMany("Transactions") .HasForeignKey("OrganizationId"); - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") .WithMany() .HasForeignKey("ProviderId"); @@ -4274,6 +4311,42 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("Organization"); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => { b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") diff --git a/util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.cs b/util/MySqlMigrations/Migrations/20261023152949_AddCipherReportExclusions.cs similarity index 100% rename from util/MySqlMigrations/Migrations/20261021171939_AddCipherReportExclusions.cs rename to util/MySqlMigrations/Migrations/20261023152949_AddCipherReportExclusions.cs diff --git a/util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.Designer.cs b/util/PostgresMigrations/Migrations/20261023152935_AddCipherReportExclusions.Designer.cs similarity index 98% rename from util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.Designer.cs rename to util/PostgresMigrations/Migrations/20261023152935_AddCipherReportExclusions.Designer.cs index 0edb37a0211e..ab20d4e873b9 100644 --- a/util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.Designer.cs +++ b/util/PostgresMigrations/Migrations/20261023152935_AddCipherReportExclusions.Designer.cs @@ -12,7 +12,7 @@ namespace Bit.PostgresMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20261021171844_AddCipherReportExclusions")] + [Migration("20261023152935_AddCipherReportExclusions")] partial class AddCipherReportExclusions { /// @@ -478,152 +478,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("Policy", (string)null); }); - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("BillingEmail") - .HasColumnType("text"); - - b.Property("BillingPhone") - .HasColumnType("text"); - - b.Property("BusinessAddress1") - .HasColumnType("text"); - - b.Property("BusinessAddress2") - .HasColumnType("text"); - - b.Property("BusinessAddress3") - .HasColumnType("text"); - - b.Property("BusinessCountry") - .HasColumnType("text"); - - b.Property("BusinessName") - .HasColumnType("text"); - - b.Property("BusinessTaxNumber") - .HasColumnType("text"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("DiscountId") - .HasColumnType("text"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Gateway") - .HasColumnType("smallint"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UseEvents") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("GatewayCustomerId"); - - b.HasIndex("GatewaySubscriptionId"); - - b.ToTable("Provider", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("ProviderId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Settings") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ProviderId"); - - b.ToTable("ProviderOrganization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Email") - .HasColumnType("text"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Permissions") - .HasColumnType("text"); - - b.Property("ProviderId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("ProviderId"); - - b.HasIndex("UserId"); - - b.ToTable("ProviderUser", (string)null); - }); - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => { b.Property("Id") @@ -873,6 +727,46 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("SsoUser", (string)null); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.TwoFactorRememberToken", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceId") + .HasColumnType("uuid"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Stamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DeviceId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ExpirationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "DeviceId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("TwoFactorRememberToken", (string)null); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => { b.Property("Id") @@ -1596,6 +1490,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Id") .HasColumnType("uuid"); + b.Property("AccessLeaseId") + .HasColumnType("uuid"); + + b.Property("AccessRequestId") + .HasColumnType("uuid"); + b.Property("ActingUserId") .HasColumnType("uuid"); @@ -2921,6 +2821,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("TargetSystemId") .HasColumnType("uuid"); + b.Property("WrappedKey") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + b.HasKey("Id"); b.HasIndex("CipherId") @@ -3017,6 +2921,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Status") .HasColumnType("smallint"); + b.Property("WrappedKey") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + b.HasKey("Id"); b.HasIndex("OrganizationId"); @@ -3053,6 +2961,152 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("Installation", (string)null); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasColumnType("text"); + + b.Property("BillingPhone") + .HasColumnType("text"); + + b.Property("BusinessAddress1") + .HasColumnType("text"); + + b.Property("BusinessAddress2") + .HasColumnType("text"); + + b.Property("BusinessAddress3") + .HasColumnType("text"); + + b.Property("BusinessCountry") + .HasColumnType("text"); + + b.Property("BusinessName") + .HasColumnType("text"); + + b.Property("BusinessTaxNumber") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DiscountId") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => { b.Property("Id") @@ -3640,42 +3694,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("Organization"); }); - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Provider"); - - b.Navigation("User"); - }); - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => { b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") @@ -3744,6 +3762,25 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("User"); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.TwoFactorRememberToken", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "Device") + .WithMany() + .HasForeignKey("DeviceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Device"); + + b.Navigation("User"); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => { b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") @@ -3795,7 +3832,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => { - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") .WithMany() .HasForeignKey("ProviderId") .OnDelete(DeleteBehavior.Cascade) @@ -3806,7 +3843,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => { - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") .WithMany() .HasForeignKey("ProviderId") .OnDelete(DeleteBehavior.Cascade) @@ -4002,7 +4039,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasForeignKey("OrganizationId") .OnDelete(DeleteBehavior.Cascade); - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") .WithMany() .HasForeignKey("ProviderId") .OnDelete(DeleteBehavior.Cascade); @@ -4040,7 +4077,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .WithMany("Transactions") .HasForeignKey("OrganizationId"); - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") .WithMany() .HasForeignKey("ProviderId"); @@ -4280,6 +4317,42 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("Organization"); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => { b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") diff --git a/util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.cs b/util/PostgresMigrations/Migrations/20261023152935_AddCipherReportExclusions.cs similarity index 100% rename from util/PostgresMigrations/Migrations/20261021171844_AddCipherReportExclusions.cs rename to util/PostgresMigrations/Migrations/20261023152935_AddCipherReportExclusions.cs diff --git a/util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.Designer.cs b/util/SqliteMigrations/Migrations/20261023152942_AddCipherReportExclusions.Designer.cs similarity index 98% rename from util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.Designer.cs rename to util/SqliteMigrations/Migrations/20261023152942_AddCipherReportExclusions.Designer.cs index 4f1613d3303a..9aa358e4160e 100644 --- a/util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.Designer.cs +++ b/util/SqliteMigrations/Migrations/20261023152942_AddCipherReportExclusions.Designer.cs @@ -11,7 +11,7 @@ namespace Bit.SqliteMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20261021171852_AddCipherReportExclusions")] + [Migration("20261023152942_AddCipherReportExclusions")] partial class AddCipherReportExclusions { /// @@ -470,152 +470,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("Policy", (string)null); }); - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("BillingEmail") - .HasColumnType("TEXT"); - - b.Property("BillingPhone") - .HasColumnType("TEXT"); - - b.Property("BusinessAddress1") - .HasColumnType("TEXT"); - - b.Property("BusinessAddress2") - .HasColumnType("TEXT"); - - b.Property("BusinessAddress3") - .HasColumnType("TEXT"); - - b.Property("BusinessCountry") - .HasColumnType("TEXT"); - - b.Property("BusinessName") - .HasColumnType("TEXT"); - - b.Property("BusinessTaxNumber") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("DiscountId") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("Gateway") - .HasColumnType("INTEGER"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UseEvents") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("GatewayCustomerId"); - - b.HasIndex("GatewaySubscriptionId"); - - b.ToTable("Provider", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("ProviderId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Settings") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ProviderId"); - - b.ToTable("ProviderOrganization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Email") - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("Permissions") - .HasColumnType("TEXT"); - - b.Property("ProviderId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ProviderId"); - - b.HasIndex("UserId"); - - b.ToTable("ProviderUser", (string)null); - }); - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => { b.Property("Id") @@ -857,6 +711,46 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("SsoUser", (string)null); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.TwoFactorRememberToken", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeviceId") + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Stamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DeviceId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ExpirationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "DeviceId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("TwoFactorRememberToken", (string)null); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => { b.Property("Id") @@ -1580,6 +1474,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Id") .HasColumnType("TEXT"); + b.Property("AccessLeaseId") + .HasColumnType("TEXT"); + + b.Property("AccessRequestId") + .HasColumnType("TEXT"); + b.Property("ActingUserId") .HasColumnType("TEXT"); @@ -2904,6 +2804,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("TargetSystemId") .HasColumnType("TEXT"); + b.Property("WrappedKey") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + b.HasKey("Id"); b.HasIndex("CipherId") @@ -3000,6 +2904,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Status") .HasColumnType("INTEGER"); + b.Property("WrappedKey") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + b.HasKey("Id"); b.HasIndex("OrganizationId"); @@ -3036,6 +2944,152 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("Installation", (string)null); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BillingEmail") + .HasColumnType("TEXT"); + + b.Property("BillingPhone") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DiscountId") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => { b.Property("Id") @@ -3623,42 +3677,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("Organization"); }); - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Provider"); - - b.Navigation("User"); - }); - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => { b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") @@ -3727,6 +3745,25 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("User"); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.TwoFactorRememberToken", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "Device") + .WithMany() + .HasForeignKey("DeviceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Device"); + + b.Navigation("User"); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => { b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") @@ -3778,7 +3815,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => { - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") .WithMany() .HasForeignKey("ProviderId") .OnDelete(DeleteBehavior.Cascade) @@ -3789,7 +3826,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => { - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") .WithMany() .HasForeignKey("ProviderId") .OnDelete(DeleteBehavior.Cascade) @@ -3985,7 +4022,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasForeignKey("OrganizationId") .OnDelete(DeleteBehavior.Cascade); - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") .WithMany() .HasForeignKey("ProviderId") .OnDelete(DeleteBehavior.Cascade); @@ -4023,7 +4060,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .WithMany("Transactions") .HasForeignKey("OrganizationId"); - b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") .WithMany() .HasForeignKey("ProviderId"); @@ -4263,6 +4300,42 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("Organization"); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Providers.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Providers.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => { b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") diff --git a/util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.cs b/util/SqliteMigrations/Migrations/20261023152942_AddCipherReportExclusions.cs similarity index 100% rename from util/SqliteMigrations/Migrations/20261021171852_AddCipherReportExclusions.cs rename to util/SqliteMigrations/Migrations/20261023152942_AddCipherReportExclusions.cs From 90f1645b0aefbd822176a4fcd185c19cf7de21e5 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 26 Oct 2026 15:06:15 -0400 Subject: [PATCH 12/20] [PM-41931] refactor(dirt): Rename report exclusions to browser health exclusions --- .../Models/Response/CipherResponseModel.cs | 4 +- ...CipherBrowserHealthExclusionRepository.cs} | 4 +- src/Core/Vault/Entities/Cipher.cs | 2 +- src/Core/Vault/Models/Data/CipherDetails.cs | 6 +- .../DapperServiceCollectionExtensions.cs | 2 +- ...CipherBrowserHealthExclusionRepository.cs} | 6 +- ...CipherBrowserHealthExclusionRepository.cs} | 10 +-- ...ityFrameworkServiceCollectionExtensions.cs | 2 +- .../Queries/UserCipherDetailsQuery.cs | 6 +- .../Vault/Repositories/CipherRepository.cs | 6 +- .../Queries/CipherDetailsQuery.cs | 2 +- ...ql => Cipher_ExcludeFromBrowserHealth.sql} | 6 +- src/Sql/dbo/Vault/Functions/CipherDetails.sql | 6 +- .../Cipher/CipherDetails_Create.sql | 4 +- .../CipherDetails_CreateWithCollections.sql | 4 +- .../Cipher/CipherDetails_ReadByIdUserId.sql | 4 +- .../Cipher/CipherDetails_Update.sql | 4 +- .../Cipher/Cipher_Create.sql | 2 +- .../Cipher/Cipher_CreateWithCollections.sql | 2 +- .../Cipher/Cipher_Update.sql | 2 +- .../Cipher/Cipher_UpdateWithCollections.sql | 2 +- src/Sql/dbo/Vault/Tables/Cipher.sql | 2 +- ...rBrowserHealthExclusionRepositoryTests.cs} | 62 +++++++++---------- ...6_00_AddCipherBrowserHealthExclusions.sql} | 40 ++++++------ ...CipherBrowserHealthExclusions.Designer.cs} | 10 +-- ...90333_AddCipherBrowserHealthExclusions.cs} | 6 +- .../DatabaseContextModelSnapshot.cs | 6 +- ...CipherBrowserHealthExclusions.Designer.cs} | 10 +-- ...90223_AddCipherBrowserHealthExclusions.cs} | 6 +- .../DatabaseContextModelSnapshot.cs | 6 +- ...CipherBrowserHealthExclusions.Designer.cs} | 10 +-- ...90237_AddCipherBrowserHealthExclusions.cs} | 6 +- .../DatabaseContextModelSnapshot.cs | 6 +- 33 files changed, 128 insertions(+), 128 deletions(-) rename src/Core/Dirt/Repositories/{ICipherReportExclusionRepository.cs => ICipherBrowserHealthExclusionRepository.cs} (59%) rename src/Infrastructure.Dapper/Dirt/{CipherReportExclusionRepository.cs => CipherBrowserHealthExclusionRepository.cs} (73%) rename src/Infrastructure.EntityFramework/Dirt/{CipherReportExclusionRepository.cs => CipherBrowserHealthExclusionRepository.cs} (74%) rename src/Sql/dbo/Dirt/Stored Procedures/{Cipher_ExcludeFromReports.sql => Cipher_ExcludeFromBrowserHealth.sql} (78%) rename test/Infrastructure.IntegrationTest/Dirt/Repositories/{CipherReportExclusionRepositoryTests.cs => CipherBrowserHealthExclusionRepositoryTests.cs} (75%) rename util/Migrator/DbScripts/{2026-10-23_00_AddCipherReportExclusions.sql => 2026-10-26_00_AddCipherBrowserHealthExclusions.sql} (92%) rename util/MySqlMigrations/Migrations/{20261023152949_AddCipherReportExclusions.Designer.cs => 20261026190333_AddCipherBrowserHealthExclusions.Designer.cs} (99%) rename util/MySqlMigrations/Migrations/{20261023152949_AddCipherReportExclusions.cs => 20261026190333_AddCipherBrowserHealthExclusions.cs} (79%) rename util/PostgresMigrations/Migrations/{20261023152935_AddCipherReportExclusions.Designer.cs => 20261026190223_AddCipherBrowserHealthExclusions.Designer.cs} (99%) rename util/PostgresMigrations/Migrations/{20261023152935_AddCipherReportExclusions.cs => 20261026190223_AddCipherBrowserHealthExclusions.cs} (77%) rename util/SqliteMigrations/Migrations/{20261023152942_AddCipherReportExclusions.Designer.cs => 20261026190237_AddCipherBrowserHealthExclusions.Designer.cs} (99%) rename util/SqliteMigrations/Migrations/{20261023152942_AddCipherReportExclusions.cs => 20261026190237_AddCipherBrowserHealthExclusions.cs} (77%) diff --git a/src/Api/Vault/Models/Response/CipherResponseModel.cs b/src/Api/Vault/Models/Response/CipherResponseModel.cs index d737759f0b11..4ad2acbacd1f 100644 --- a/src/Api/Vault/Models/Response/CipherResponseModel.cs +++ b/src/Api/Vault/Models/Response/CipherResponseModel.cs @@ -296,7 +296,7 @@ protected CipherResponseModel( Favorite = cipher.Favorite; Edit = cipher.Edit; ArchivedDate = cipher.ArchivedDate; - ExcludedFromReports = cipher.ExcludedFromReports; + ExcludedFromBrowserHealth = cipher.ExcludedFromBrowserHealth; ViewPassword = cipher.ViewPassword; Permissions = new CipherPermissionsResponseModel(user, cipher, organizationAbility); } @@ -313,7 +313,7 @@ public static CipherResponseModel From(FullCipherAccess access, CipherDetails ci public bool Edit { get; set; } public bool ViewPassword { get; set; } public DateTime? ArchivedDate { get; set; } - public bool ExcludedFromReports { get; set; } + public bool ExcludedFromBrowserHealth { get; set; } public CipherPermissionsResponseModel Permissions { get; set; } } diff --git a/src/Core/Dirt/Repositories/ICipherReportExclusionRepository.cs b/src/Core/Dirt/Repositories/ICipherBrowserHealthExclusionRepository.cs similarity index 59% rename from src/Core/Dirt/Repositories/ICipherReportExclusionRepository.cs rename to src/Core/Dirt/Repositories/ICipherBrowserHealthExclusionRepository.cs index ac17ede1b67b..16864beb7e4e 100644 --- a/src/Core/Dirt/Repositories/ICipherReportExclusionRepository.cs +++ b/src/Core/Dirt/Repositories/ICipherBrowserHealthExclusionRepository.cs @@ -1,7 +1,7 @@ namespace Bit.Core.Dirt.Repositories; -public interface ICipherReportExclusionRepository +public interface ICipherBrowserHealthExclusionRepository { - /// Excludes the user's personal, not-deleted logins from health reports. Returns how many were excluded. + /// Excludes the user's personal, not-deleted logins from browser health. Returns how many were excluded. Task ExcludeAsync(IEnumerable cipherIds, Guid userId); } diff --git a/src/Core/Vault/Entities/Cipher.cs b/src/Core/Vault/Entities/Cipher.cs index fdc68ede867e..59d4df6d5b7f 100644 --- a/src/Core/Vault/Entities/Cipher.cs +++ b/src/Core/Vault/Entities/Cipher.cs @@ -27,7 +27,7 @@ public class Cipher : ITableObject, ICloneable public Enums.CipherRepromptType? Reprompt { get; set; } public string Key { get; set; } public string Archives { get; set; } - public string ReportExclusions { get; set; } + public string BrowserHealthExclusions { get; set; } public void SetNewId() { diff --git a/src/Core/Vault/Models/Data/CipherDetails.cs b/src/Core/Vault/Models/Data/CipherDetails.cs index 124c9888549d..ffdf09e53186 100644 --- a/src/Core/Vault/Models/Data/CipherDetails.cs +++ b/src/Core/Vault/Models/Data/CipherDetails.cs @@ -11,8 +11,8 @@ public class CipherDetails : CipherOrganizationDetails public bool Manage { get; set; } // Per-user archived date from Archives JSON. public DateTime? ArchivedDate { get; set; } - // Per-user health report exclusion flag from ReportExclusions JSON. - public bool ExcludedFromReports { get; set; } + // Per-user browser health exclusion flag from BrowserHealthExclusions JSON. + public bool ExcludedFromBrowserHealth { get; set; } public CipherDetails() { } public CipherDetails(CipherOrganizationDetails cipher) @@ -55,7 +55,7 @@ public CipherDetailsWithCollections( Key = cipher.Key; FolderId = cipher.FolderId; ArchivedDate = cipher.ArchivedDate; - ExcludedFromReports = cipher.ExcludedFromReports; + ExcludedFromBrowserHealth = cipher.ExcludedFromBrowserHealth; Favorite = cipher.Favorite; Edit = cipher.Edit; ViewPassword = cipher.ViewPassword; diff --git a/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs b/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs index 9912bc6ca8b9..cd3e5786e2ef 100644 --- a/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs +++ b/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs @@ -96,7 +96,7 @@ public static void AddDapperRepositories(this IServiceCollection services, bool services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); if (selfHosted) { diff --git a/src/Infrastructure.Dapper/Dirt/CipherReportExclusionRepository.cs b/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs similarity index 73% rename from src/Infrastructure.Dapper/Dirt/CipherReportExclusionRepository.cs rename to src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs index 5214c0dbfa66..3856bedca8d2 100644 --- a/src/Infrastructure.Dapper/Dirt/CipherReportExclusionRepository.cs +++ b/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs @@ -7,9 +7,9 @@ namespace Bit.Infrastructure.Dapper.Dirt; -public class CipherReportExclusionRepository : BaseRepository, ICipherReportExclusionRepository +public class CipherBrowserHealthExclusionRepository : BaseRepository, ICipherBrowserHealthExclusionRepository { - public CipherReportExclusionRepository(GlobalSettings globalSettings) + public CipherBrowserHealthExclusionRepository(GlobalSettings globalSettings) : base(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString) { } @@ -18,7 +18,7 @@ public async Task ExcludeAsync(IEnumerable cipherIds, Guid userId) { await using var connection = new SqlConnection(ConnectionString); return await connection.ExecuteScalarAsync( - "[dbo].[Cipher_ExcludeFromReports]", + "[dbo].[Cipher_ExcludeFromBrowserHealth]", new { Ids = cipherIds.ToGuidIdArrayTVP(), UserId = userId }, commandType: CommandType.StoredProcedure); } diff --git a/src/Infrastructure.EntityFramework/Dirt/CipherReportExclusionRepository.cs b/src/Infrastructure.EntityFramework/Dirt/CipherBrowserHealthExclusionRepository.cs similarity index 74% rename from src/Infrastructure.EntityFramework/Dirt/CipherReportExclusionRepository.cs rename to src/Infrastructure.EntityFramework/Dirt/CipherBrowserHealthExclusionRepository.cs index bf3393fa92fd..7c4553c61033 100644 --- a/src/Infrastructure.EntityFramework/Dirt/CipherReportExclusionRepository.cs +++ b/src/Infrastructure.EntityFramework/Dirt/CipherBrowserHealthExclusionRepository.cs @@ -8,9 +8,9 @@ namespace Bit.Infrastructure.EntityFramework.Dirt; -public class CipherReportExclusionRepository : BaseEntityFrameworkRepository, ICipherReportExclusionRepository +public class CipherBrowserHealthExclusionRepository : BaseEntityFrameworkRepository, ICipherBrowserHealthExclusionRepository { - public CipherReportExclusionRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) + public CipherBrowserHealthExclusionRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) : base(serviceScopeFactory, mapper) { } @@ -27,12 +27,12 @@ public async Task ExcludeAsync(IEnumerable cipherIds, Guid userId) foreach (var cipher in ciphers) { - var exclusions = string.IsNullOrWhiteSpace(cipher.ReportExclusions) + var exclusions = string.IsNullOrWhiteSpace(cipher.BrowserHealthExclusions) ? new Dictionary() - : CoreHelpers.LoadClassFromJsonData>(cipher.ReportExclusions) + : CoreHelpers.LoadClassFromJsonData>(cipher.BrowserHealthExclusions) ?? new Dictionary(); exclusions[userId] = true; - cipher.ReportExclusions = CoreHelpers.ClassToJsonData(exclusions); + cipher.BrowserHealthExclusions = CoreHelpers.ClassToJsonData(exclusions); } if (ciphers.Count > 0) diff --git a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs index 2fc87c16cbca..72f1b1ae823e 100644 --- a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs +++ b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs @@ -132,7 +132,7 @@ public static void AddPasswordManagerEFRepositories(this IServiceCollection serv services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); if (selfHosted) diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs b/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs index ef9ed692da15..a5fa6b097470 100644 --- a/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs +++ b/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs @@ -72,7 +72,7 @@ from cg in cg_g.DefaultIfEmpty() c.Reprompt, c.Key, c.Archives, - c.ReportExclusions + c.BrowserHealthExclusions }; var query2 = from c in dbContext.Ciphers @@ -97,7 +97,7 @@ from cg in cg_g.DefaultIfEmpty() c.Reprompt, c.Key, c.Archives, - c.ReportExclusions + c.BrowserHealthExclusions }; var union = query.Union(query2).Select(c => new CipherDetails @@ -120,7 +120,7 @@ from cg in cg_g.DefaultIfEmpty() OrganizationUseTotp = c.OrganizationUseTotp, Key = c.Key, ArchivedDate = GetArchivedDate(_userId, new Cipher { Id = c.Id, Archives = c.Archives }), - ExcludedFromReports = _userId.HasValue && c.ReportExclusions != null && c.ReportExclusions.ToLowerInvariant().Contains($"\"{_userId}\":true") + ExcludedFromBrowserHealth = _userId.HasValue && c.BrowserHealthExclusions != null && c.BrowserHealthExclusions.ToLowerInvariant().Contains($"\"{_userId}\":true") }); return union; } diff --git a/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs b/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs index ced1770c5a21..3777cd917e50 100644 --- a/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs +++ b/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs @@ -502,7 +502,7 @@ public async Task> GetManyByUserIdAsync(Guid userId, OrganizationUseTotp = false, Key = c.Key, ArchivedDate = c.ArchivedDate, - ExcludedFromReports = c.ExcludedFromReports, + ExcludedFromBrowserHealth = c.ExcludedFromBrowserHealth, }; } @@ -572,7 +572,7 @@ public override async Task ReplaceAsync(Core.Vault.Entities.Cipher cipher) { var mappedEntity = Mapper.Map(cipher); dbContext.Entry(entity).CurrentValues.SetValues(mappedEntity); - dbContext.Entry(entity).Property(c => c.ReportExclusions).IsModified = false; + dbContext.Entry(entity).Property(c => c.BrowserHealthExclusions).IsModified = false; await dbContext.SaveChangesAsync(); } } @@ -651,7 +651,7 @@ public async Task ReplaceAsync(CipherDetails cipher) var mappedEntity = Mapper.Map(cipher); dbContext.Entry(entity).CurrentValues.SetValues(mappedEntity); - dbContext.Entry(entity).Property(c => c.ReportExclusions).IsModified = false; + dbContext.Entry(entity).Property(c => c.BrowserHealthExclusions).IsModified = false; if (cipher.OrganizationId.HasValue) { diff --git a/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs b/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs index 996cde102233..1d53ac43458b 100644 --- a/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs +++ b/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs @@ -37,7 +37,7 @@ public virtual IQueryable Run(DatabaseContext dbContext) ArchivedDate = !_userId.HasValue || c.Archives == null || !c.Archives.ToLowerInvariant().Contains(_userId.Value.ToString()) ? null : CoreHelpers.LoadClassFromJsonData>(c.Archives)[_userId.Value], - ExcludedFromReports = _userId.HasValue && c.ReportExclusions != null && c.ReportExclusions.ToLowerInvariant().Contains($"\"{_userId}\":true"), + ExcludedFromBrowserHealth = _userId.HasValue && c.BrowserHealthExclusions != null && c.BrowserHealthExclusions.ToLowerInvariant().Contains($"\"{_userId}\":true"), }; return query; } diff --git a/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql b/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromBrowserHealth.sql similarity index 78% rename from src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql rename to src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromBrowserHealth.sql index f2a3984d66c2..4b08d6a6b997 100644 --- a/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromReports.sql +++ b/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromBrowserHealth.sql @@ -1,4 +1,4 @@ -CREATE PROCEDURE [dbo].[Cipher_ExcludeFromReports] +CREATE PROCEDURE [dbo].[Cipher_ExcludeFromBrowserHealth] @Ids AS [dbo].[GuidIdArray] READONLY, @UserId AS UNIQUEIDENTIFIER AS @@ -8,8 +8,8 @@ BEGIN UPDATE C SET - [ReportExclusions] = JSON_MODIFY( - COALESCE(C.[ReportExclusions], N'{}'), + [BrowserHealthExclusions] = JSON_MODIFY( + COALESCE(C.[BrowserHealthExclusions], N'{}'), CONCAT('$."', @UserId, '"'), CAST(1 AS BIT) ) diff --git a/src/Sql/dbo/Vault/Functions/CipherDetails.sql b/src/Sql/dbo/Vault/Functions/CipherDetails.sql index d1a30fd2648c..a694b21c27f4 100644 --- a/src/Sql/dbo/Vault/Functions/CipherDetails.sql +++ b/src/Sql/dbo/Vault/Functions/CipherDetails.sql @@ -38,10 +38,10 @@ SELECT CASE WHEN @UserId IS NULL - OR C.[ReportExclusions] IS NULL - OR JSON_VALUE(C.[ReportExclusions], CONCAT('$."', @UserId, '"')) IS NULL + OR C.[BrowserHealthExclusions] IS NULL + OR JSON_VALUE(C.[BrowserHealthExclusions], CONCAT('$."', @UserId, '"')) IS NULL THEN 0 ELSE 1 - END [ExcludedFromReports] + END [ExcludedFromBrowserHealth] FROM [dbo].[Cipher] C; diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Create.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Create.sql index 8d3e2df0560d..da9378328376 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Create.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Create.sql @@ -20,8 +20,8 @@ @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used - @ExcludedFromReports BIT = NULL, -- not used - @ReportExclusions NVARCHAR(MAX) = NULL -- not used + @ExcludedFromBrowserHealth BIT = NULL, -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN SET NOCOUNT ON diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql index cd2d4c37d376..450d5cd152d4 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql @@ -20,8 +20,8 @@ @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used - @ExcludedFromReports BIT = NULL, -- not used - @ReportExclusions NVARCHAR(MAX) = NULL, -- not used + @ExcludedFromBrowserHealth BIT = NULL, -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL, -- not used @CollectionIds AS [dbo].[GuidIdArray] READONLY AS BEGIN diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_ReadByIdUserId.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_ReadByIdUserId.sql index 21b6e8c2add6..3d67c6a97f75 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_ReadByIdUserId.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_ReadByIdUserId.sql @@ -21,7 +21,7 @@ SELECT [Key], [OrganizationUseTotp], [ArchivedDate], - [ExcludedFromReports], + [ExcludedFromBrowserHealth], MAX ([Edit]) AS [Edit], MAX ([ViewPassword]) AS [ViewPassword], MAX ([Manage]) AS [Manage] @@ -45,5 +45,5 @@ SELECT [Key], [OrganizationUseTotp], [ArchivedDate], - [ExcludedFromReports] + [ExcludedFromBrowserHealth] END diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Update.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Update.sql index 0b4f4332a319..52ca63a90800 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Update.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/CipherDetails_Update.sql @@ -20,8 +20,8 @@ @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used - @ExcludedFromReports BIT = NULL, -- not used - @ReportExclusions NVARCHAR(MAX) = NULL -- not used + @ExcludedFromBrowserHealth BIT = NULL, -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN SET NOCOUNT ON diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Create.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Create.sql index ce3c00b3a9de..7d7c85b4babc 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Create.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Create.sql @@ -13,7 +13,7 @@ @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, @Archives NVARCHAR(MAX) = NULL, - @ReportExclusions NVARCHAR(MAX) = NULL -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN SET NOCOUNT ON diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql index 6b465649b2f1..79289d1afc32 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql @@ -13,7 +13,7 @@ @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, @Archives NVARCHAR(MAX) = NULL, - @ReportExclusions NVARCHAR(MAX) = NULL, -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL, -- not used @CollectionIds AS [dbo].[GuidIdArray] READONLY AS BEGIN diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Update.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Update.sql index b4764b11dd9d..90ace7ac0951 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Update.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_Update.sql @@ -13,7 +13,7 @@ @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, @Archives NVARCHAR(MAX) = NULL, - @ReportExclusions NVARCHAR(MAX) = NULL -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN SET NOCOUNT ON diff --git a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql index 972159674393..e710796e13c9 100644 --- a/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql +++ b/src/Sql/dbo/Vault/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql @@ -13,7 +13,7 @@ @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, @Archives NVARCHAR(MAX) = NULL, - @ReportExclusions NVARCHAR(MAX) = NULL, -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL, -- not used @CollectionIds AS [dbo].[GuidIdArray] READONLY AS BEGIN diff --git a/src/Sql/dbo/Vault/Tables/Cipher.sql b/src/Sql/dbo/Vault/Tables/Cipher.sql index f02257705438..42749bb241da 100644 --- a/src/Sql/dbo/Vault/Tables/Cipher.sql +++ b/src/Sql/dbo/Vault/Tables/Cipher.sql @@ -15,7 +15,7 @@ CREATE TABLE [dbo].[Cipher] ( [Key] VARCHAR(MAX) NULL, [ArchivedDate] DATETIME2 (7) NULL, [Archives] NVARCHAR(MAX) NULL, - [ReportExclusions] NVARCHAR(MAX) NULL, + [BrowserHealthExclusions] NVARCHAR(MAX) NULL, CONSTRAINT [PK_Cipher] PRIMARY KEY CLUSTERED ([Id] ASC), CONSTRAINT [FK_Cipher_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]), CONSTRAINT [FK_Cipher_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) diff --git a/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherBrowserHealthExclusionRepositoryTests.cs similarity index 75% rename from test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs rename to test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherBrowserHealthExclusionRepositoryTests.cs index e17fb5a38f00..8015856ddf32 100644 --- a/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherReportExclusionRepositoryTests.cs +++ b/test/Infrastructure.IntegrationTest/Dirt/Repositories/CipherBrowserHealthExclusionRepositoryTests.cs @@ -10,11 +10,11 @@ namespace Bit.Infrastructure.IntegrationTest.Dirt.Repositories; -public class CipherReportExclusionRepositoryTests +public class CipherBrowserHealthExclusionRepositoryTests { [DatabaseTheory, DatabaseData] - public async Task ExcludeAsync_PersonalLogin_ReturnsOneAndSetsUsersExcludedFromReports( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + public async Task ExcludeAsync_PersonalLogin_ReturnsOneAndSetsUsersExcludedFromBrowserHealth( + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); @@ -23,12 +23,12 @@ public async Task ExcludeAsync_PersonalLogin_ReturnsOneAndSetsUsersExcludedFromR Assert.Equal(1, excluded); var details = await cipherRepository.GetByIdAsync(cipher.Id, user.Id); - Assert.True(details.ExcludedFromReports); + Assert.True(details.ExcludedFromBrowserHealth); } [DatabaseTheory, DatabaseData] public async Task ExcludeAsync_OrganizationCipher_ReturnsZero( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository, + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository, IOrganizationRepository organizationRepository) { var user = await userRepository.CreateTestUserAsync(); @@ -46,7 +46,7 @@ public async Task ExcludeAsync_OrganizationCipher_ReturnsZero( [DatabaseTheory, DatabaseData] public async Task ExcludeAsync_NonLoginCipher_ReturnsZero( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id, CipherType.SecureNote); @@ -56,7 +56,7 @@ public async Task ExcludeAsync_NonLoginCipher_ReturnsZero( [DatabaseTheory, DatabaseData] public async Task ExcludeAsync_DeletedCipher_ReturnsZero( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); @@ -67,19 +67,19 @@ public async Task ExcludeAsync_DeletedCipher_ReturnsZero( [DatabaseTheory, DatabaseData] public async Task ExcludeAsync_OtherUsersCipher_ReturnsZero( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var owner = await userRepository.CreateTestUserAsync(); var otherUser = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, owner.Id); Assert.Equal(0, await sut.ExcludeAsync([cipher.Id], otherUser.Id)); - Assert.False((await cipherRepository.GetByIdAsync(cipher.Id, owner.Id)).ExcludedFromReports); + Assert.False((await cipherRepository.GetByIdAsync(cipher.Id, owner.Id)).ExcludedFromBrowserHealth); } [DatabaseTheory, DatabaseData] public async Task ExcludeAsync_AlreadyExcluded_ReturnsOne( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); @@ -90,7 +90,7 @@ public async Task ExcludeAsync_AlreadyExcluded_ReturnsOne( [DatabaseTheory, DatabaseData] public async Task ExcludeAsync_DoesNotChangeCipherRevisionDate( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); @@ -103,7 +103,7 @@ public async Task ExcludeAsync_DoesNotChangeCipherRevisionDate( [DatabaseTheory, DatabaseData] public async Task ExcludeAsync_BumpsAccountRevisionDate( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); @@ -115,8 +115,8 @@ public async Task ExcludeAsync_BumpsAccountRevisionDate( } [DatabaseTheory, DatabaseData] - public async Task GetManyByUserIdAsync_ReturnsExcludedFromReports( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + public async Task GetManyByUserIdAsync_ReturnsExcludedFromBrowserHealth( + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); var excludedCipher = await CreateCipherAsync(cipherRepository, user.Id); @@ -125,13 +125,13 @@ public async Task GetManyByUserIdAsync_ReturnsExcludedFromReports( await sut.ExcludeAsync([excludedCipher.Id], user.Id); var ciphers = await cipherRepository.GetManyByUserIdAsync(user.Id); - Assert.True(ciphers.Single(c => c.Id == excludedCipher.Id).ExcludedFromReports); - Assert.False(ciphers.Single(c => c.Id == otherCipher.Id).ExcludedFromReports); + Assert.True(ciphers.Single(c => c.Id == excludedCipher.Id).ExcludedFromBrowserHealth); + Assert.False(ciphers.Single(c => c.Id == otherCipher.Id).ExcludedFromBrowserHealth); } [DatabaseTheory, DatabaseData] - public async Task GetManyByUserIdAsync_WithoutOrganizations_ReturnsExcludedFromReports( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + public async Task GetManyByUserIdAsync_WithoutOrganizations_ReturnsExcludedFromBrowserHealth( + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); @@ -139,12 +139,12 @@ public async Task GetManyByUserIdAsync_WithoutOrganizations_ReturnsExcludedFromR await sut.ExcludeAsync([cipher.Id], user.Id); var ciphers = await cipherRepository.GetManyByUserIdAsync(user.Id, withOrganizations: false); - Assert.True(ciphers.Single(c => c.Id == cipher.Id).ExcludedFromReports); + Assert.True(ciphers.Single(c => c.Id == cipher.Id).ExcludedFromBrowserHealth); } [DatabaseTheory, DatabaseData] - public async Task ReplaceAsync_CipherDetails_PreservesReportExclusions( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + public async Task ReplaceAsync_CipherDetails_PreservesBrowserHealthExclusions( + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); @@ -154,12 +154,12 @@ public async Task ReplaceAsync_CipherDetails_PreservesReportExclusions( details.Data = "{\"updated\":true}"; await cipherRepository.ReplaceAsync(details); - Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ExcludedFromReports); + Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ExcludedFromBrowserHealth); } [DatabaseTheory, DatabaseData] - public async Task ReplaceAsync_Cipher_PreservesReportExclusions( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + public async Task ReplaceAsync_Cipher_PreservesBrowserHealthExclusions( + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); @@ -168,12 +168,12 @@ public async Task ReplaceAsync_Cipher_PreservesReportExclusions( cipher.Data = "{\"updated\":true}"; await cipherRepository.ReplaceAsync(cipher); - Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ExcludedFromReports); + Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ExcludedFromBrowserHealth); } [DatabaseTheory, DatabaseData] - public async Task SoftDeleteAsync_KeepsReportExclusion( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) + public async Task SoftDeleteAsync_KeepsBrowserHealthExclusion( + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository) { var user = await userRepository.CreateTestUserAsync(); var cipher = await CreateCipherAsync(cipherRepository, user.Id); @@ -181,12 +181,12 @@ public async Task SoftDeleteAsync_KeepsReportExclusion( await cipherRepository.SoftDeleteAsync([cipher.Id], user.Id); - Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ExcludedFromReports); + Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, user.Id)).ExcludedFromBrowserHealth); } [DatabaseTheory, DatabaseData] public async Task ReplaceAsync_ShareToOrg_KeepsOwnersExclusionOnly( - ICipherReportExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository, + ICipherBrowserHealthExclusionRepository sut, ICipherRepository cipherRepository, IUserRepository userRepository, IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, ICollectionRepository collectionRepository) { @@ -207,8 +207,8 @@ public async Task ReplaceAsync_ShareToOrg_KeepsOwnersExclusionOnly( cipher.OrganizationId = org.Id; await cipherRepository.ReplaceAsync(cipher, [collection.Id]); - Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, owner.Id)).ExcludedFromReports); - Assert.False((await cipherRepository.GetManyByUserIdAsync(member.Id)).Single(c => c.Id == cipher.Id).ExcludedFromReports); + Assert.True((await cipherRepository.GetByIdAsync(cipher.Id, owner.Id)).ExcludedFromBrowserHealth); + Assert.False((await cipherRepository.GetManyByUserIdAsync(member.Id)).Single(c => c.Id == cipher.Id).ExcludedFromBrowserHealth); } private static Task CreateCipherAsync(ICipherRepository cipherRepository, Guid userId, CipherType type = CipherType.Login) => diff --git a/util/Migrator/DbScripts/2026-10-23_00_AddCipherReportExclusions.sql b/util/Migrator/DbScripts/2026-10-26_00_AddCipherBrowserHealthExclusions.sql similarity index 92% rename from util/Migrator/DbScripts/2026-10-23_00_AddCipherReportExclusions.sql rename to util/Migrator/DbScripts/2026-10-26_00_AddCipherBrowserHealthExclusions.sql index 2f2c0af84cfa..4e5695d3cbdd 100644 --- a/util/Migrator/DbScripts/2026-10-23_00_AddCipherReportExclusions.sql +++ b/util/Migrator/DbScripts/2026-10-26_00_AddCipherBrowserHealthExclusions.sql @@ -1,7 +1,7 @@ -IF COL_LENGTH('[dbo].[Cipher]', 'ReportExclusions') IS NULL +IF COL_LENGTH('[dbo].[Cipher]', 'BrowserHealthExclusions') IS NULL BEGIN ALTER TABLE [dbo].[Cipher] - ADD [ReportExclusions] NVARCHAR(MAX) NULL; + ADD [BrowserHealthExclusions] NVARCHAR(MAX) NULL; END GO @@ -45,11 +45,11 @@ SELECT CASE WHEN @UserId IS NULL - OR C.[ReportExclusions] IS NULL - OR JSON_VALUE(C.[ReportExclusions], CONCAT('$."', @UserId, '"')) IS NULL + OR C.[BrowserHealthExclusions] IS NULL + OR JSON_VALUE(C.[BrowserHealthExclusions], CONCAT('$."', @UserId, '"')) IS NULL THEN 0 ELSE 1 - END [ExcludedFromReports] + END [ExcludedFromBrowserHealth] FROM [dbo].[Cipher] C; GO @@ -75,7 +75,7 @@ CREATE OR ALTER PROCEDURE [dbo].[Cipher_Create] @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, @Archives NVARCHAR(MAX) = NULL, - @ReportExclusions NVARCHAR(MAX) = NULL -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN SET NOCOUNT ON @@ -139,7 +139,7 @@ CREATE OR ALTER PROCEDURE [dbo].[Cipher_Update] @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, @Archives NVARCHAR(MAX) = NULL, - @ReportExclusions NVARCHAR(MAX) = NULL -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN SET NOCOUNT ON @@ -189,7 +189,7 @@ CREATE OR ALTER PROCEDURE [dbo].[Cipher_CreateWithCollections] @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, @Archives NVARCHAR(MAX) = NULL, - @ReportExclusions NVARCHAR(MAX) = NULL, -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL, -- not used @CollectionIds AS [dbo].[GuidIdArray] READONLY AS BEGIN @@ -224,7 +224,7 @@ CREATE OR ALTER PROCEDURE [dbo].[Cipher_UpdateWithCollections] @Reprompt TINYINT, @Key VARCHAR(MAX) = NULL, @Archives NVARCHAR(MAX) = NULL, - @ReportExclusions NVARCHAR(MAX) = NULL, -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL, -- not used @CollectionIds AS [dbo].[GuidIdArray] READONLY AS BEGIN @@ -296,8 +296,8 @@ CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_Create] @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used - @ExcludedFromReports BIT = NULL, -- not used - @ReportExclusions NVARCHAR(MAX) = NULL -- not used + @ExcludedFromBrowserHealth BIT = NULL, -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN SET NOCOUNT ON @@ -371,8 +371,8 @@ CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_CreateWithCollections] @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used - @ExcludedFromReports BIT = NULL, -- not used - @ReportExclusions NVARCHAR(MAX) = NULL, -- not used + @ExcludedFromBrowserHealth BIT = NULL, -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL, -- not used @CollectionIds AS [dbo].[GuidIdArray] READONLY AS BEGIN @@ -415,8 +415,8 @@ CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_Update] @Key VARCHAR(MAX) = NULL, @ArchivedDate DATETIME2(7) = NULL, @Archives NVARCHAR(MAX) = NULL, -- not used - @ExcludedFromReports BIT = NULL, -- not used - @ReportExclusions NVARCHAR(MAX) = NULL -- not used + @ExcludedFromBrowserHealth BIT = NULL, -- not used + @BrowserHealthExclusions NVARCHAR(MAX) = NULL -- not used AS BEGIN SET NOCOUNT ON @@ -501,7 +501,7 @@ SELECT [Key], [OrganizationUseTotp], [ArchivedDate], - [ExcludedFromReports], + [ExcludedFromBrowserHealth], MAX ([Edit]) AS [Edit], MAX ([ViewPassword]) AS [ViewPassword], MAX ([Manage]) AS [Manage] @@ -525,11 +525,11 @@ SELECT [Key], [OrganizationUseTotp], [ArchivedDate], - [ExcludedFromReports] + [ExcludedFromBrowserHealth] END GO -CREATE OR ALTER PROCEDURE [dbo].[Cipher_ExcludeFromReports] +CREATE OR ALTER PROCEDURE [dbo].[Cipher_ExcludeFromBrowserHealth] @Ids AS [dbo].[GuidIdArray] READONLY, @UserId AS UNIQUEIDENTIFIER AS @@ -539,8 +539,8 @@ BEGIN UPDATE C SET - [ReportExclusions] = JSON_MODIFY( - COALESCE(C.[ReportExclusions], N'{}'), + [BrowserHealthExclusions] = JSON_MODIFY( + COALESCE(C.[BrowserHealthExclusions], N'{}'), CONCAT('$."', @UserId, '"'), CAST(1 AS BIT) ) diff --git a/util/MySqlMigrations/Migrations/20261023152949_AddCipherReportExclusions.Designer.cs b/util/MySqlMigrations/Migrations/20261026190333_AddCipherBrowserHealthExclusions.Designer.cs similarity index 99% rename from util/MySqlMigrations/Migrations/20261023152949_AddCipherReportExclusions.Designer.cs rename to util/MySqlMigrations/Migrations/20261026190333_AddCipherBrowserHealthExclusions.Designer.cs index 446231c54b65..69f7eaee48c7 100644 --- a/util/MySqlMigrations/Migrations/20261023152949_AddCipherReportExclusions.Designer.cs +++ b/util/MySqlMigrations/Migrations/20261026190333_AddCipherBrowserHealthExclusions.Designer.cs @@ -12,8 +12,8 @@ namespace Bit.MySqlMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20261023152949_AddCipherReportExclusions")] - partial class AddCipherReportExclusions + [Migration("20261026190333_AddCipherBrowserHealthExclusions")] + partial class AddCipherBrowserHealthExclusions { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -3325,6 +3325,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Attachments") .HasColumnType("longtext"); + b.Property("BrowserHealthExclusions") + .HasColumnType("longtext"); + b.Property("CreationDate") .HasColumnType("datetime(6)"); @@ -3346,9 +3349,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("OrganizationId") .HasColumnType("char(36)"); - b.Property("ReportExclusions") - .HasColumnType("longtext"); - b.Property("Reprompt") .HasColumnType("tinyint unsigned"); diff --git a/util/MySqlMigrations/Migrations/20261023152949_AddCipherReportExclusions.cs b/util/MySqlMigrations/Migrations/20261026190333_AddCipherBrowserHealthExclusions.cs similarity index 79% rename from util/MySqlMigrations/Migrations/20261023152949_AddCipherReportExclusions.cs rename to util/MySqlMigrations/Migrations/20261026190333_AddCipherBrowserHealthExclusions.cs index db2b091004b7..c1cf1f0c095a 100644 --- a/util/MySqlMigrations/Migrations/20261023152949_AddCipherReportExclusions.cs +++ b/util/MySqlMigrations/Migrations/20261026190333_AddCipherBrowserHealthExclusions.cs @@ -5,13 +5,13 @@ namespace Bit.MySqlMigrations.Migrations; /// -public partial class AddCipherReportExclusions : Migration +public partial class AddCipherBrowserHealthExclusions : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn( - name: "ReportExclusions", + name: "BrowserHealthExclusions", table: "Cipher", type: "longtext", nullable: true) @@ -22,7 +22,7 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropColumn( - name: "ReportExclusions", + name: "BrowserHealthExclusions", table: "Cipher"); } } diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index 2bba2f0967aa..494983089eae 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -3328,6 +3328,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Attachments") .HasColumnType("longtext"); + b.Property("BrowserHealthExclusions") + .HasColumnType("longtext"); + b.Property("CreationDate") .HasColumnType("datetime(6)"); @@ -3349,9 +3352,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("OrganizationId") .HasColumnType("char(36)"); - b.Property("ReportExclusions") - .HasColumnType("longtext"); - b.Property("Reprompt") .HasColumnType("tinyint unsigned"); diff --git a/util/PostgresMigrations/Migrations/20261023152935_AddCipherReportExclusions.Designer.cs b/util/PostgresMigrations/Migrations/20261026190223_AddCipherBrowserHealthExclusions.Designer.cs similarity index 99% rename from util/PostgresMigrations/Migrations/20261023152935_AddCipherReportExclusions.Designer.cs rename to util/PostgresMigrations/Migrations/20261026190223_AddCipherBrowserHealthExclusions.Designer.cs index ab20d4e873b9..7f09b535bdd5 100644 --- a/util/PostgresMigrations/Migrations/20261023152935_AddCipherReportExclusions.Designer.cs +++ b/util/PostgresMigrations/Migrations/20261026190223_AddCipherBrowserHealthExclusions.Designer.cs @@ -12,8 +12,8 @@ namespace Bit.PostgresMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20261023152935_AddCipherReportExclusions")] - partial class AddCipherReportExclusions + [Migration("20261026190223_AddCipherBrowserHealthExclusions")] + partial class AddCipherBrowserHealthExclusions { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -3331,6 +3331,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Attachments") .HasColumnType("text"); + b.Property("BrowserHealthExclusions") + .HasColumnType("text"); + b.Property("CreationDate") .HasColumnType("timestamp with time zone"); @@ -3352,9 +3355,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("OrganizationId") .HasColumnType("uuid"); - b.Property("ReportExclusions") - .HasColumnType("text"); - b.Property("Reprompt") .HasColumnType("smallint"); diff --git a/util/PostgresMigrations/Migrations/20261023152935_AddCipherReportExclusions.cs b/util/PostgresMigrations/Migrations/20261026190223_AddCipherBrowserHealthExclusions.cs similarity index 77% rename from util/PostgresMigrations/Migrations/20261023152935_AddCipherReportExclusions.cs rename to util/PostgresMigrations/Migrations/20261026190223_AddCipherBrowserHealthExclusions.cs index 6b279b80e2ae..d82fc9e41fd3 100644 --- a/util/PostgresMigrations/Migrations/20261023152935_AddCipherReportExclusions.cs +++ b/util/PostgresMigrations/Migrations/20261026190223_AddCipherBrowserHealthExclusions.cs @@ -5,13 +5,13 @@ namespace Bit.PostgresMigrations.Migrations; /// -public partial class AddCipherReportExclusions : Migration +public partial class AddCipherBrowserHealthExclusions : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn( - name: "ReportExclusions", + name: "BrowserHealthExclusions", table: "Cipher", type: "text", nullable: true); @@ -21,7 +21,7 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropColumn( - name: "ReportExclusions", + name: "BrowserHealthExclusions", table: "Cipher"); } } diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index 4f333304b1df..7f0918973903 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -3334,6 +3334,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Attachments") .HasColumnType("text"); + b.Property("BrowserHealthExclusions") + .HasColumnType("text"); + b.Property("CreationDate") .HasColumnType("timestamp with time zone"); @@ -3355,9 +3358,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("OrganizationId") .HasColumnType("uuid"); - b.Property("ReportExclusions") - .HasColumnType("text"); - b.Property("Reprompt") .HasColumnType("smallint"); diff --git a/util/SqliteMigrations/Migrations/20261023152942_AddCipherReportExclusions.Designer.cs b/util/SqliteMigrations/Migrations/20261026190237_AddCipherBrowserHealthExclusions.Designer.cs similarity index 99% rename from util/SqliteMigrations/Migrations/20261023152942_AddCipherReportExclusions.Designer.cs rename to util/SqliteMigrations/Migrations/20261026190237_AddCipherBrowserHealthExclusions.Designer.cs index 9aa358e4160e..1ac946a01a3a 100644 --- a/util/SqliteMigrations/Migrations/20261023152942_AddCipherReportExclusions.Designer.cs +++ b/util/SqliteMigrations/Migrations/20261026190237_AddCipherBrowserHealthExclusions.Designer.cs @@ -11,8 +11,8 @@ namespace Bit.SqliteMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20261023152942_AddCipherReportExclusions")] - partial class AddCipherReportExclusions + [Migration("20261026190237_AddCipherBrowserHealthExclusions")] + partial class AddCipherBrowserHealthExclusions { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -3314,6 +3314,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Attachments") .HasColumnType("TEXT"); + b.Property("BrowserHealthExclusions") + .HasColumnType("TEXT"); + b.Property("CreationDate") .HasColumnType("TEXT"); @@ -3335,9 +3338,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("OrganizationId") .HasColumnType("TEXT"); - b.Property("ReportExclusions") - .HasColumnType("TEXT"); - b.Property("Reprompt") .HasColumnType("INTEGER"); diff --git a/util/SqliteMigrations/Migrations/20261023152942_AddCipherReportExclusions.cs b/util/SqliteMigrations/Migrations/20261026190237_AddCipherBrowserHealthExclusions.cs similarity index 77% rename from util/SqliteMigrations/Migrations/20261023152942_AddCipherReportExclusions.cs rename to util/SqliteMigrations/Migrations/20261026190237_AddCipherBrowserHealthExclusions.cs index 8b40abb161af..c1a6cf33fa01 100644 --- a/util/SqliteMigrations/Migrations/20261023152942_AddCipherReportExclusions.cs +++ b/util/SqliteMigrations/Migrations/20261026190237_AddCipherBrowserHealthExclusions.cs @@ -5,13 +5,13 @@ namespace Bit.SqliteMigrations.Migrations; /// -public partial class AddCipherReportExclusions : Migration +public partial class AddCipherBrowserHealthExclusions : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn( - name: "ReportExclusions", + name: "BrowserHealthExclusions", table: "Cipher", type: "TEXT", nullable: true); @@ -21,7 +21,7 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropColumn( - name: "ReportExclusions", + name: "BrowserHealthExclusions", table: "Cipher"); } } diff --git a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs index e878eafbe413..135b97a3e72b 100644 --- a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -3317,6 +3317,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Attachments") .HasColumnType("TEXT"); + b.Property("BrowserHealthExclusions") + .HasColumnType("TEXT"); + b.Property("CreationDate") .HasColumnType("TEXT"); @@ -3338,9 +3341,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("OrganizationId") .HasColumnType("TEXT"); - b.Property("ReportExclusions") - .HasColumnType("TEXT"); - b.Property("Reprompt") .HasColumnType("INTEGER"); From 8b53441ca7c567ee6bfad91ed01fc8379745dca2 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 26 Oct 2026 16:35:48 -0400 Subject: [PATCH 13/20] [PM-41931] refactor(dirt): Rename the exclusion procedure to UpdateMany Follow the SQL naming guide: Cipher_UpdateManyBrowserHealthExclusions. --- .../Dirt/CipherBrowserHealthExclusionRepository.cs | 2 +- ...rHealth.sql => Cipher_UpdateManyBrowserHealthExclusions.sql} | 2 +- .../2026-10-26_00_AddCipherBrowserHealthExclusions.sql | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/Sql/dbo/Dirt/Stored Procedures/{Cipher_ExcludeFromBrowserHealth.sql => Cipher_UpdateManyBrowserHealthExclusions.sql} (91%) diff --git a/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs b/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs index 3856bedca8d2..d2f442b74df3 100644 --- a/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs +++ b/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs @@ -18,7 +18,7 @@ public async Task ExcludeAsync(IEnumerable cipherIds, Guid userId) { await using var connection = new SqlConnection(ConnectionString); return await connection.ExecuteScalarAsync( - "[dbo].[Cipher_ExcludeFromBrowserHealth]", + "[dbo].[Cipher_UpdateManyBrowserHealthExclusions]", new { Ids = cipherIds.ToGuidIdArrayTVP(), UserId = userId }, commandType: CommandType.StoredProcedure); } diff --git a/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromBrowserHealth.sql b/src/Sql/dbo/Dirt/Stored Procedures/Cipher_UpdateManyBrowserHealthExclusions.sql similarity index 91% rename from src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromBrowserHealth.sql rename to src/Sql/dbo/Dirt/Stored Procedures/Cipher_UpdateManyBrowserHealthExclusions.sql index 4b08d6a6b997..0c32e3736432 100644 --- a/src/Sql/dbo/Dirt/Stored Procedures/Cipher_ExcludeFromBrowserHealth.sql +++ b/src/Sql/dbo/Dirt/Stored Procedures/Cipher_UpdateManyBrowserHealthExclusions.sql @@ -1,4 +1,4 @@ -CREATE PROCEDURE [dbo].[Cipher_ExcludeFromBrowserHealth] +CREATE PROCEDURE [dbo].[Cipher_UpdateManyBrowserHealthExclusions] @Ids AS [dbo].[GuidIdArray] READONLY, @UserId AS UNIQUEIDENTIFIER AS diff --git a/util/Migrator/DbScripts/2026-10-26_00_AddCipherBrowserHealthExclusions.sql b/util/Migrator/DbScripts/2026-10-26_00_AddCipherBrowserHealthExclusions.sql index 4e5695d3cbdd..dcc82c5335d3 100644 --- a/util/Migrator/DbScripts/2026-10-26_00_AddCipherBrowserHealthExclusions.sql +++ b/util/Migrator/DbScripts/2026-10-26_00_AddCipherBrowserHealthExclusions.sql @@ -529,7 +529,7 @@ SELECT END GO -CREATE OR ALTER PROCEDURE [dbo].[Cipher_ExcludeFromBrowserHealth] +CREATE OR ALTER PROCEDURE [dbo].[Cipher_UpdateManyBrowserHealthExclusions] @Ids AS [dbo].[GuidIdArray] READONLY, @UserId AS UNIQUEIDENTIFIER AS From e0ffca17b8bf76f1f8b76b7b5ee52ecac7311162 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 26 Oct 2026 16:58:25 -0400 Subject: [PATCH 14/20] [PM-41931] style(dirt): Align the column types in Cipher.sql --- src/Sql/dbo/Vault/Tables/Cipher.sql | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Sql/dbo/Vault/Tables/Cipher.sql b/src/Sql/dbo/Vault/Tables/Cipher.sql index 42749bb241da..37d42838379e 100644 --- a/src/Sql/dbo/Vault/Tables/Cipher.sql +++ b/src/Sql/dbo/Vault/Tables/Cipher.sql @@ -1,21 +1,21 @@  CREATE TABLE [dbo].[Cipher] ( - [Id] UNIQUEIDENTIFIER NOT NULL, - [UserId] UNIQUEIDENTIFIER NULL, - [OrganizationId] UNIQUEIDENTIFIER NULL, - [Type] TINYINT NOT NULL, - [Data] NVARCHAR (MAX) NOT NULL, - [Favorites] NVARCHAR (MAX) NULL, - [Folders] NVARCHAR (MAX) NULL, - [Attachments] NVARCHAR (MAX) NULL, - [CreationDate] DATETIME2 (7) NOT NULL, - [RevisionDate] DATETIME2 (7) NOT NULL, - [DeletedDate] DATETIME2 (7) NULL, - [Reprompt] TINYINT NULL, - [Key] VARCHAR(MAX) NULL, - [ArchivedDate] DATETIME2 (7) NULL, - [Archives] NVARCHAR(MAX) NULL, - [BrowserHealthExclusions] NVARCHAR(MAX) NULL, + [Id] UNIQUEIDENTIFIER NOT NULL, + [UserId] UNIQUEIDENTIFIER NULL, + [OrganizationId] UNIQUEIDENTIFIER NULL, + [Type] TINYINT NOT NULL, + [Data] NVARCHAR (MAX) NOT NULL, + [Favorites] NVARCHAR (MAX) NULL, + [Folders] NVARCHAR (MAX) NULL, + [Attachments] NVARCHAR (MAX) NULL, + [CreationDate] DATETIME2 (7) NOT NULL, + [RevisionDate] DATETIME2 (7) NOT NULL, + [DeletedDate] DATETIME2 (7) NULL, + [Reprompt] TINYINT NULL, + [Key] VARCHAR(MAX) NULL, + [ArchivedDate] DATETIME2 (7) NULL, + [Archives] NVARCHAR(MAX) NULL, + [BrowserHealthExclusions] NVARCHAR(MAX) NULL, CONSTRAINT [PK_Cipher] PRIMARY KEY CLUSTERED ([Id] ASC), CONSTRAINT [FK_Cipher_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]), CONSTRAINT [FK_Cipher_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) From d363d1660675bc71c8ef7d6c28df778e94110730 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 26 Oct 2026 17:03:29 -0400 Subject: [PATCH 15/20] [PM-41931] refactor(dirt): Return early when no cipher ids are passed --- .../Dirt/CipherBrowserHealthExclusionRepository.cs | 8 +++++++- .../Dirt/CipherBrowserHealthExclusionRepository.cs | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs b/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs index d2f442b74df3..4f16c768abce 100644 --- a/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs +++ b/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs @@ -16,10 +16,16 @@ public CipherBrowserHealthExclusionRepository(GlobalSettings globalSettings) public async Task ExcludeAsync(IEnumerable cipherIds, Guid userId) { + var ids = cipherIds.ToList(); + if (ids.Count == 0) + { + return 0; + } + await using var connection = new SqlConnection(ConnectionString); return await connection.ExecuteScalarAsync( "[dbo].[Cipher_UpdateManyBrowserHealthExclusions]", - new { Ids = cipherIds.ToGuidIdArrayTVP(), UserId = userId }, + new { Ids = ids.ToGuidIdArrayTVP(), UserId = userId }, commandType: CommandType.StoredProcedure); } } diff --git a/src/Infrastructure.EntityFramework/Dirt/CipherBrowserHealthExclusionRepository.cs b/src/Infrastructure.EntityFramework/Dirt/CipherBrowserHealthExclusionRepository.cs index 7c4553c61033..bc3e07ee9100 100644 --- a/src/Infrastructure.EntityFramework/Dirt/CipherBrowserHealthExclusionRepository.cs +++ b/src/Infrastructure.EntityFramework/Dirt/CipherBrowserHealthExclusionRepository.cs @@ -17,9 +17,14 @@ public CipherBrowserHealthExclusionRepository(IServiceScopeFactory serviceScopeF public async Task ExcludeAsync(IEnumerable cipherIds, Guid userId) { + var ids = cipherIds.ToList(); + if (ids.Count == 0) + { + return 0; + } + await using var scope = ServiceScopeFactory.CreateAsyncScope(); var dbContext = GetDatabaseContext(scope); - var ids = cipherIds.ToList(); var ciphers = await dbContext.Ciphers .Where(c => ids.Contains(c.Id) && c.UserId == userId && c.OrganizationId == null && c.Type == CipherType.Login && c.DeletedDate == null) From 4ca863861df72860cb8d5114015083005e6d1f13 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 26 Oct 2026 17:55:22 -0400 Subject: [PATCH 16/20] [PM-41931] chore(dirt): Regenerate EF migrations on current main --- ...26215410_AddCipherBrowserHealthExclusions.Designer.cs} | 8 +++++++- ...=> 20261026215410_AddCipherBrowserHealthExclusions.cs} | 0 ...26215355_AddCipherBrowserHealthExclusions.Designer.cs} | 8 +++++++- ...=> 20261026215355_AddCipherBrowserHealthExclusions.cs} | 0 ...26215402_AddCipherBrowserHealthExclusions.Designer.cs} | 8 +++++++- ...=> 20261026215402_AddCipherBrowserHealthExclusions.cs} | 0 6 files changed, 21 insertions(+), 3 deletions(-) rename util/MySqlMigrations/Migrations/{20261026190333_AddCipherBrowserHealthExclusions.Designer.cs => 20261026215410_AddCipherBrowserHealthExclusions.Designer.cs} (99%) rename util/MySqlMigrations/Migrations/{20261026190333_AddCipherBrowserHealthExclusions.cs => 20261026215410_AddCipherBrowserHealthExclusions.cs} (100%) rename util/PostgresMigrations/Migrations/{20261026190223_AddCipherBrowserHealthExclusions.Designer.cs => 20261026215355_AddCipherBrowserHealthExclusions.Designer.cs} (99%) rename util/PostgresMigrations/Migrations/{20261026190223_AddCipherBrowserHealthExclusions.cs => 20261026215355_AddCipherBrowserHealthExclusions.cs} (100%) rename util/SqliteMigrations/Migrations/{20261026190237_AddCipherBrowserHealthExclusions.Designer.cs => 20261026215402_AddCipherBrowserHealthExclusions.Designer.cs} (99%) rename util/SqliteMigrations/Migrations/{20261026190237_AddCipherBrowserHealthExclusions.cs => 20261026215402_AddCipherBrowserHealthExclusions.cs} (100%) diff --git a/util/MySqlMigrations/Migrations/20261026190333_AddCipherBrowserHealthExclusions.Designer.cs b/util/MySqlMigrations/Migrations/20261026215410_AddCipherBrowserHealthExclusions.Designer.cs similarity index 99% rename from util/MySqlMigrations/Migrations/20261026190333_AddCipherBrowserHealthExclusions.Designer.cs rename to util/MySqlMigrations/Migrations/20261026215410_AddCipherBrowserHealthExclusions.Designer.cs index 69f7eaee48c7..9f7c385f5654 100644 --- a/util/MySqlMigrations/Migrations/20261026190333_AddCipherBrowserHealthExclusions.Designer.cs +++ b/util/MySqlMigrations/Migrations/20261026215410_AddCipherBrowserHealthExclusions.Designer.cs @@ -12,7 +12,7 @@ namespace Bit.MySqlMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20261026190333_AddCipherBrowserHealthExclusions")] + [Migration("20261026215410_AddCipherBrowserHealthExclusions")] partial class AddCipherBrowserHealthExclusions { /// @@ -1788,6 +1788,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasMaxLength(300) .HasColumnType("varchar(300)"); + b.Property("InviteReminderDueDate") + .HasColumnType("datetime(6)"); + + b.Property("InviteRemindersSentCount") + .HasColumnType("tinyint unsigned"); + b.Property("Key") .HasColumnType("longtext"); diff --git a/util/MySqlMigrations/Migrations/20261026190333_AddCipherBrowserHealthExclusions.cs b/util/MySqlMigrations/Migrations/20261026215410_AddCipherBrowserHealthExclusions.cs similarity index 100% rename from util/MySqlMigrations/Migrations/20261026190333_AddCipherBrowserHealthExclusions.cs rename to util/MySqlMigrations/Migrations/20261026215410_AddCipherBrowserHealthExclusions.cs diff --git a/util/PostgresMigrations/Migrations/20261026190223_AddCipherBrowserHealthExclusions.Designer.cs b/util/PostgresMigrations/Migrations/20261026215355_AddCipherBrowserHealthExclusions.Designer.cs similarity index 99% rename from util/PostgresMigrations/Migrations/20261026190223_AddCipherBrowserHealthExclusions.Designer.cs rename to util/PostgresMigrations/Migrations/20261026215355_AddCipherBrowserHealthExclusions.Designer.cs index 7f09b535bdd5..33fddf3df88c 100644 --- a/util/PostgresMigrations/Migrations/20261026190223_AddCipherBrowserHealthExclusions.Designer.cs +++ b/util/PostgresMigrations/Migrations/20261026215355_AddCipherBrowserHealthExclusions.Designer.cs @@ -12,7 +12,7 @@ namespace Bit.PostgresMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20261026190223_AddCipherBrowserHealthExclusions")] + [Migration("20261026215355_AddCipherBrowserHealthExclusions")] partial class AddCipherBrowserHealthExclusions { /// @@ -1793,6 +1793,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasMaxLength(300) .HasColumnType("character varying(300)"); + b.Property("InviteReminderDueDate") + .HasColumnType("timestamp with time zone"); + + b.Property("InviteRemindersSentCount") + .HasColumnType("smallint"); + b.Property("Key") .HasColumnType("text"); diff --git a/util/PostgresMigrations/Migrations/20261026190223_AddCipherBrowserHealthExclusions.cs b/util/PostgresMigrations/Migrations/20261026215355_AddCipherBrowserHealthExclusions.cs similarity index 100% rename from util/PostgresMigrations/Migrations/20261026190223_AddCipherBrowserHealthExclusions.cs rename to util/PostgresMigrations/Migrations/20261026215355_AddCipherBrowserHealthExclusions.cs diff --git a/util/SqliteMigrations/Migrations/20261026190237_AddCipherBrowserHealthExclusions.Designer.cs b/util/SqliteMigrations/Migrations/20261026215402_AddCipherBrowserHealthExclusions.Designer.cs similarity index 99% rename from util/SqliteMigrations/Migrations/20261026190237_AddCipherBrowserHealthExclusions.Designer.cs rename to util/SqliteMigrations/Migrations/20261026215402_AddCipherBrowserHealthExclusions.Designer.cs index 1ac946a01a3a..7349d82f104c 100644 --- a/util/SqliteMigrations/Migrations/20261026190237_AddCipherBrowserHealthExclusions.Designer.cs +++ b/util/SqliteMigrations/Migrations/20261026215402_AddCipherBrowserHealthExclusions.Designer.cs @@ -11,7 +11,7 @@ namespace Bit.SqliteMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20261026190237_AddCipherBrowserHealthExclusions")] + [Migration("20261026215402_AddCipherBrowserHealthExclusions")] partial class AddCipherBrowserHealthExclusions { /// @@ -1777,6 +1777,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasMaxLength(300) .HasColumnType("TEXT"); + b.Property("InviteReminderDueDate") + .HasColumnType("TEXT"); + + b.Property("InviteRemindersSentCount") + .HasColumnType("INTEGER"); + b.Property("Key") .HasColumnType("TEXT"); diff --git a/util/SqliteMigrations/Migrations/20261026190237_AddCipherBrowserHealthExclusions.cs b/util/SqliteMigrations/Migrations/20261026215402_AddCipherBrowserHealthExclusions.cs similarity index 100% rename from util/SqliteMigrations/Migrations/20261026190237_AddCipherBrowserHealthExclusions.cs rename to util/SqliteMigrations/Migrations/20261026215402_AddCipherBrowserHealthExclusions.cs From f52fc32f013f55206c8f62f638ca456b9e74726d Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 27 Oct 2026 13:25:43 -0400 Subject: [PATCH 17/20] [PM-41931] chore(dirt): Re-date the SQL migration after main --- ...ons.sql => 2026-10-27_00_AddCipherBrowserHealthExclusions.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename util/Migrator/DbScripts/{2026-10-26_00_AddCipherBrowserHealthExclusions.sql => 2026-10-27_00_AddCipherBrowserHealthExclusions.sql} (100%) diff --git a/util/Migrator/DbScripts/2026-10-26_00_AddCipherBrowserHealthExclusions.sql b/util/Migrator/DbScripts/2026-10-27_00_AddCipherBrowserHealthExclusions.sql similarity index 100% rename from util/Migrator/DbScripts/2026-10-26_00_AddCipherBrowserHealthExclusions.sql rename to util/Migrator/DbScripts/2026-10-27_00_AddCipherBrowserHealthExclusions.sql From d406148fb1462dcf3bcea9a67218b4857eef2ab0 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Oct 2026 11:16:50 -0400 Subject: [PATCH 18/20] [PM-41931] feat(dirt): Add the exclude from reports endpoint PUT ciphers/{id}/exclude-from-reports, behind the health report exclusions flag. Requires premium, and pushes a cipher sync so the user's other devices pick up the exclusion. --- .../CipherReportExclusionsController.cs | 33 ++++++++++ .../ExcludeCipherFromReportsCommand.cs | 40 +++++++++++++ .../IExcludeCipherFromReportsCommand.cs | 7 +++ .../ReportingServiceCollectionExtensions.cs | 1 + .../CipherReportExclusionsControllerTests.cs | 26 ++++++++ .../ExcludeCipherFromReportsCommandTests.cs | 60 +++++++++++++++++++ 6 files changed, 167 insertions(+) create mode 100644 src/Api/Dirt/Controllers/CipherReportExclusionsController.cs create mode 100644 src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs create mode 100644 src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs create mode 100644 test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs create mode 100644 test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs diff --git a/src/Api/Dirt/Controllers/CipherReportExclusionsController.cs b/src/Api/Dirt/Controllers/CipherReportExclusionsController.cs new file mode 100644 index 000000000000..0a6bab2e3efb --- /dev/null +++ b/src/Api/Dirt/Controllers/CipherReportExclusionsController.cs @@ -0,0 +1,33 @@ +using Bit.Core; +using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; +using Bit.Core.Services; +using Bitwarden.Server.Sdk.Features; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Bit.Api.Dirt.Controllers; + +[Route("ciphers")] +[Authorize("Application")] +public class CipherReportExclusionsController : Controller +{ + private readonly IUserService _userService; + private readonly IExcludeCipherFromReportsCommand _excludeCipherFromReportsCommand; + + public CipherReportExclusionsController( + IUserService userService, + IExcludeCipherFromReportsCommand excludeCipherFromReportsCommand) + { + _userService = userService; + _excludeCipherFromReportsCommand = excludeCipherFromReportsCommand; + } + + /// Excludes the caller's personal login from their health reports. + [HttpPut("{id}/exclude-from-reports")] + [RequireFeature(FeatureFlagKeys.HealthReportExclusions)] + public async Task PutExcludeFromReportsAsync(Guid id) + { + var userId = _userService.GetProperUserId(User)!.Value; + await _excludeCipherFromReportsCommand.ExcludeAsync(id, userId); + } +} diff --git a/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs b/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs new file mode 100644 index 000000000000..891f0f1a1c01 --- /dev/null +++ b/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs @@ -0,0 +1,40 @@ +using Bit.Core.Billing.Premium.Queries; +using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; +using Bit.Core.Dirt.Repositories; +using Bit.Core.Exceptions; +using Bit.Core.Platform.Push; + +namespace Bit.Core.Dirt.Reports.ReportFeatures; + +public class ExcludeCipherFromReportsCommand : IExcludeCipherFromReportsCommand +{ + private readonly ICipherReportExclusionRepository _cipherReportExclusionRepository; + private readonly IHasPremiumAccessQuery _hasPremiumAccessQuery; + private readonly IPushNotificationService _pushService; + + public ExcludeCipherFromReportsCommand( + ICipherReportExclusionRepository cipherReportExclusionRepository, + IHasPremiumAccessQuery hasPremiumAccessQuery, + IPushNotificationService pushService) + { + _cipherReportExclusionRepository = cipherReportExclusionRepository; + _hasPremiumAccessQuery = hasPremiumAccessQuery; + _pushService = pushService; + } + + public async Task ExcludeAsync(Guid cipherId, Guid userId) + { + if (!await _hasPremiumAccessQuery.HasPremiumAccessAsync(userId)) + { + throw new BadRequestException("Premium required to exclude items from reports."); + } + + var excluded = await _cipherReportExclusionRepository.ExcludeAsync([cipherId], userId); + if (excluded == 0) + { + throw new NotFoundException(); + } + + await _pushService.PushSyncCiphersAsync(userId, true); + } +} diff --git a/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs b/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs new file mode 100644 index 000000000000..fb0d89921645 --- /dev/null +++ b/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; + +public interface IExcludeCipherFromReportsCommand +{ + /// Excludes the user's personal login from their health reports. Requires premium. + Task ExcludeAsync(Guid cipherId, Guid userId); +} diff --git a/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs b/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs index 6aca3160daca..f2a5e02caf75 100644 --- a/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs +++ b/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs @@ -28,6 +28,7 @@ public static void AddReportingServices(this IServiceCollection services, IGloba services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); // v2 file storage commands services.AddScoped(); diff --git a/test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs b/test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs new file mode 100644 index 000000000000..b7004e157ee6 --- /dev/null +++ b/test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs @@ -0,0 +1,26 @@ +using System.Security.Claims; +using Bit.Api.Dirt.Controllers; +using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; +using Bit.Core.Services; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Api.Test.Dirt; + +[ControllerCustomize(typeof(CipherReportExclusionsController))] +[SutProviderCustomize] +public class CipherReportExclusionsControllerTests +{ + [Theory, BitAutoData] + public async Task PutExcludeFromReports_CallsCommandWithCallingUser( + SutProvider sutProvider, Guid cipherId, Guid userId) + { + sutProvider.GetDependency().GetProperUserId(Arg.Any()).Returns(userId); + + await sutProvider.Sut.PutExcludeFromReportsAsync(cipherId); + + await sutProvider.GetDependency().Received(1).ExcludeAsync(cipherId, userId); + } +} diff --git a/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs b/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs new file mode 100644 index 000000000000..4caceac150f8 --- /dev/null +++ b/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs @@ -0,0 +1,60 @@ +using Bit.Core.Billing.Premium.Queries; +using Bit.Core.Dirt.Reports.ReportFeatures; +using Bit.Core.Dirt.Repositories; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.Models; +using Bit.Core.Platform.Push; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.Dirt.ReportFeatures; + +[SutProviderCustomize] +public class ExcludeCipherFromReportsCommandTests +{ + [Theory, BitAutoData] + public async Task ExcludeAsync_WithoutPremium_ThrowsBadRequest_AndDoesNotWrite( + SutProvider sutProvider, Guid cipherId, Guid userId) + { + sutProvider.GetDependency().HasPremiumAccessAsync(userId).Returns(false); + + await Assert.ThrowsAsync(() => sutProvider.Sut.ExcludeAsync(cipherId, userId)); + + await sutProvider.GetDependency() + .DidNotReceiveWithAnyArgs().ExcludeAsync(default!, default); + await sutProvider.GetDependency() + .DidNotReceiveWithAnyArgs().PushAsync(Arg.Any>()); + } + + [Theory, BitAutoData] + public async Task ExcludeAsync_NothingExcluded_ThrowsNotFound_AndDoesNotPush( + SutProvider sutProvider, Guid cipherId, Guid userId) + { + sutProvider.GetDependency().HasPremiumAccessAsync(userId).Returns(true); + sutProvider.GetDependency() + .ExcludeAsync(Arg.Any>(), userId).Returns(0); + + await Assert.ThrowsAsync(() => sutProvider.Sut.ExcludeAsync(cipherId, userId)); + + await sutProvider.GetDependency() + .DidNotReceiveWithAnyArgs().PushAsync(Arg.Any>()); + } + + [Theory, BitAutoData] + public async Task ExcludeAsync_Excluded_PushesCipherSyncExcludingCurrentContext( + SutProvider sutProvider, Guid cipherId, Guid userId) + { + sutProvider.GetDependency().HasPremiumAccessAsync(userId).Returns(true); + sutProvider.GetDependency() + .ExcludeAsync(Arg.Is>(ids => ids.Single() == cipherId), userId).Returns(1); + + await sutProvider.Sut.ExcludeAsync(cipherId, userId); + + await sutProvider.GetDependency().Received(1) + .PushAsync(Arg.Is>(n => + n.Type == PushType.SyncCiphers && n.TargetId == userId && n.ExcludeCurrentContext)); + } +} From 97b6f5d54ff4d023292898dafbfe7aade77b590a Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 26 Oct 2026 15:13:30 -0400 Subject: [PATCH 19/20] [PM-41931] refactor(dirt): Rename the exclude from reports endpoint to browser health --- ...CipherBrowserHealthExclusionsController.cs | 33 +++++++++++++++++++ .../CipherReportExclusionsController.cs | 33 ------------------- ... ExcludeCipherFromBrowserHealthCommand.cs} | 14 ++++---- .../IExcludeCipherFromBrowserHealthCommand.cs | 7 ++++ .../IExcludeCipherFromReportsCommand.cs | 7 ---- .../ReportingServiceCollectionExtensions.cs | 2 +- ...rBrowserHealthExclusionsControllerTests.cs | 26 +++++++++++++++ .../CipherReportExclusionsControllerTests.cs | 26 --------------- ...udeCipherFromBrowserHealthCommandTests.cs} | 14 ++++---- 9 files changed, 81 insertions(+), 81 deletions(-) create mode 100644 src/Api/Dirt/Controllers/CipherBrowserHealthExclusionsController.cs delete mode 100644 src/Api/Dirt/Controllers/CipherReportExclusionsController.cs rename src/Core/Dirt/Reports/ReportFeatures/{ExcludeCipherFromReportsCommand.cs => ExcludeCipherFromBrowserHealthCommand.cs} (63%) create mode 100644 src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromBrowserHealthCommand.cs delete mode 100644 src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs create mode 100644 test/Api.Test/Dirt/CipherBrowserHealthExclusionsControllerTests.cs delete mode 100644 test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs rename test/Core.Test/Dirt/ReportFeatures/{ExcludeCipherFromReportsCommandTests.cs => ExcludeCipherFromBrowserHealthCommandTests.cs} (78%) diff --git a/src/Api/Dirt/Controllers/CipherBrowserHealthExclusionsController.cs b/src/Api/Dirt/Controllers/CipherBrowserHealthExclusionsController.cs new file mode 100644 index 000000000000..c6d38907253d --- /dev/null +++ b/src/Api/Dirt/Controllers/CipherBrowserHealthExclusionsController.cs @@ -0,0 +1,33 @@ +using Bit.Core; +using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; +using Bit.Core.Services; +using Bitwarden.Server.Sdk.Features; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Bit.Api.Dirt.Controllers; + +[Route("ciphers")] +[Authorize("Application")] +public class CipherBrowserHealthExclusionsController : Controller +{ + private readonly IUserService _userService; + private readonly IExcludeCipherFromBrowserHealthCommand _excludeCipherFromBrowserHealthCommand; + + public CipherBrowserHealthExclusionsController( + IUserService userService, + IExcludeCipherFromBrowserHealthCommand excludeCipherFromBrowserHealthCommand) + { + _userService = userService; + _excludeCipherFromBrowserHealthCommand = excludeCipherFromBrowserHealthCommand; + } + + /// Excludes the caller's personal login from browser health. + [HttpPut("{id}/exclude-from-browser-health")] + [RequireFeature(FeatureFlagKeys.HealthReportExclusions)] + public async Task PutExcludeFromBrowserHealthAsync(Guid id) + { + var userId = _userService.GetProperUserId(User)!.Value; + await _excludeCipherFromBrowserHealthCommand.ExcludeAsync(id, userId); + } +} diff --git a/src/Api/Dirt/Controllers/CipherReportExclusionsController.cs b/src/Api/Dirt/Controllers/CipherReportExclusionsController.cs deleted file mode 100644 index 0a6bab2e3efb..000000000000 --- a/src/Api/Dirt/Controllers/CipherReportExclusionsController.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Bit.Core; -using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; -using Bit.Core.Services; -using Bitwarden.Server.Sdk.Features; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Bit.Api.Dirt.Controllers; - -[Route("ciphers")] -[Authorize("Application")] -public class CipherReportExclusionsController : Controller -{ - private readonly IUserService _userService; - private readonly IExcludeCipherFromReportsCommand _excludeCipherFromReportsCommand; - - public CipherReportExclusionsController( - IUserService userService, - IExcludeCipherFromReportsCommand excludeCipherFromReportsCommand) - { - _userService = userService; - _excludeCipherFromReportsCommand = excludeCipherFromReportsCommand; - } - - /// Excludes the caller's personal login from their health reports. - [HttpPut("{id}/exclude-from-reports")] - [RequireFeature(FeatureFlagKeys.HealthReportExclusions)] - public async Task PutExcludeFromReportsAsync(Guid id) - { - var userId = _userService.GetProperUserId(User)!.Value; - await _excludeCipherFromReportsCommand.ExcludeAsync(id, userId); - } -} diff --git a/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs b/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromBrowserHealthCommand.cs similarity index 63% rename from src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs rename to src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromBrowserHealthCommand.cs index 891f0f1a1c01..57fdf8bfce67 100644 --- a/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromReportsCommand.cs +++ b/src/Core/Dirt/Reports/ReportFeatures/ExcludeCipherFromBrowserHealthCommand.cs @@ -6,18 +6,18 @@ namespace Bit.Core.Dirt.Reports.ReportFeatures; -public class ExcludeCipherFromReportsCommand : IExcludeCipherFromReportsCommand +public class ExcludeCipherFromBrowserHealthCommand : IExcludeCipherFromBrowserHealthCommand { - private readonly ICipherReportExclusionRepository _cipherReportExclusionRepository; + private readonly ICipherBrowserHealthExclusionRepository _cipherBrowserHealthExclusionRepository; private readonly IHasPremiumAccessQuery _hasPremiumAccessQuery; private readonly IPushNotificationService _pushService; - public ExcludeCipherFromReportsCommand( - ICipherReportExclusionRepository cipherReportExclusionRepository, + public ExcludeCipherFromBrowserHealthCommand( + ICipherBrowserHealthExclusionRepository cipherBrowserHealthExclusionRepository, IHasPremiumAccessQuery hasPremiumAccessQuery, IPushNotificationService pushService) { - _cipherReportExclusionRepository = cipherReportExclusionRepository; + _cipherBrowserHealthExclusionRepository = cipherBrowserHealthExclusionRepository; _hasPremiumAccessQuery = hasPremiumAccessQuery; _pushService = pushService; } @@ -26,10 +26,10 @@ public async Task ExcludeAsync(Guid cipherId, Guid userId) { if (!await _hasPremiumAccessQuery.HasPremiumAccessAsync(userId)) { - throw new BadRequestException("Premium required to exclude items from reports."); + throw new BadRequestException("Premium required to exclude items from browser health."); } - var excluded = await _cipherReportExclusionRepository.ExcludeAsync([cipherId], userId); + var excluded = await _cipherBrowserHealthExclusionRepository.ExcludeAsync([cipherId], userId); if (excluded == 0) { throw new NotFoundException(); diff --git a/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromBrowserHealthCommand.cs b/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromBrowserHealthCommand.cs new file mode 100644 index 000000000000..f90ac3f1638c --- /dev/null +++ b/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromBrowserHealthCommand.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; + +public interface IExcludeCipherFromBrowserHealthCommand +{ + /// Excludes the user's personal login from browser health. Requires premium. + Task ExcludeAsync(Guid cipherId, Guid userId); +} diff --git a/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs b/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs deleted file mode 100644 index fb0d89921645..000000000000 --- a/src/Core/Dirt/Reports/ReportFeatures/Interfaces/IExcludeCipherFromReportsCommand.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; - -public interface IExcludeCipherFromReportsCommand -{ - /// Excludes the user's personal login from their health reports. Requires premium. - Task ExcludeAsync(Guid cipherId, Guid userId); -} diff --git a/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs b/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs index f2a5e02caf75..9c133f04789a 100644 --- a/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs +++ b/src/Core/Dirt/Reports/ReportFeatures/ReportingServiceCollectionExtensions.cs @@ -28,7 +28,7 @@ public static void AddReportingServices(this IServiceCollection services, IGloba services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); // v2 file storage commands services.AddScoped(); diff --git a/test/Api.Test/Dirt/CipherBrowserHealthExclusionsControllerTests.cs b/test/Api.Test/Dirt/CipherBrowserHealthExclusionsControllerTests.cs new file mode 100644 index 000000000000..abdf92441b43 --- /dev/null +++ b/test/Api.Test/Dirt/CipherBrowserHealthExclusionsControllerTests.cs @@ -0,0 +1,26 @@ +using System.Security.Claims; +using Bit.Api.Dirt.Controllers; +using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; +using Bit.Core.Services; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Api.Test.Dirt; + +[ControllerCustomize(typeof(CipherBrowserHealthExclusionsController))] +[SutProviderCustomize] +public class CipherBrowserHealthExclusionsControllerTests +{ + [Theory, BitAutoData] + public async Task PutExcludeFromBrowserHealth_CallsCommandWithCallingUser( + SutProvider sutProvider, Guid cipherId, Guid userId) + { + sutProvider.GetDependency().GetProperUserId(Arg.Any()).Returns(userId); + + await sutProvider.Sut.PutExcludeFromBrowserHealthAsync(cipherId); + + await sutProvider.GetDependency().Received(1).ExcludeAsync(cipherId, userId); + } +} diff --git a/test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs b/test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs deleted file mode 100644 index b7004e157ee6..000000000000 --- a/test/Api.Test/Dirt/CipherReportExclusionsControllerTests.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Security.Claims; -using Bit.Api.Dirt.Controllers; -using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; -using Bit.Core.Services; -using Bit.Test.Common.AutoFixture; -using Bit.Test.Common.AutoFixture.Attributes; -using NSubstitute; -using Xunit; - -namespace Bit.Api.Test.Dirt; - -[ControllerCustomize(typeof(CipherReportExclusionsController))] -[SutProviderCustomize] -public class CipherReportExclusionsControllerTests -{ - [Theory, BitAutoData] - public async Task PutExcludeFromReports_CallsCommandWithCallingUser( - SutProvider sutProvider, Guid cipherId, Guid userId) - { - sutProvider.GetDependency().GetProperUserId(Arg.Any()).Returns(userId); - - await sutProvider.Sut.PutExcludeFromReportsAsync(cipherId); - - await sutProvider.GetDependency().Received(1).ExcludeAsync(cipherId, userId); - } -} diff --git a/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs b/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromBrowserHealthCommandTests.cs similarity index 78% rename from test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs rename to test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromBrowserHealthCommandTests.cs index 4caceac150f8..e2e8cf904fca 100644 --- a/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromReportsCommandTests.cs +++ b/test/Core.Test/Dirt/ReportFeatures/ExcludeCipherFromBrowserHealthCommandTests.cs @@ -13,17 +13,17 @@ namespace Bit.Core.Test.Dirt.ReportFeatures; [SutProviderCustomize] -public class ExcludeCipherFromReportsCommandTests +public class ExcludeCipherFromBrowserHealthCommandTests { [Theory, BitAutoData] public async Task ExcludeAsync_WithoutPremium_ThrowsBadRequest_AndDoesNotWrite( - SutProvider sutProvider, Guid cipherId, Guid userId) + SutProvider sutProvider, Guid cipherId, Guid userId) { sutProvider.GetDependency().HasPremiumAccessAsync(userId).Returns(false); await Assert.ThrowsAsync(() => sutProvider.Sut.ExcludeAsync(cipherId, userId)); - await sutProvider.GetDependency() + await sutProvider.GetDependency() .DidNotReceiveWithAnyArgs().ExcludeAsync(default!, default); await sutProvider.GetDependency() .DidNotReceiveWithAnyArgs().PushAsync(Arg.Any>()); @@ -31,10 +31,10 @@ await sutProvider.GetDependency() [Theory, BitAutoData] public async Task ExcludeAsync_NothingExcluded_ThrowsNotFound_AndDoesNotPush( - SutProvider sutProvider, Guid cipherId, Guid userId) + SutProvider sutProvider, Guid cipherId, Guid userId) { sutProvider.GetDependency().HasPremiumAccessAsync(userId).Returns(true); - sutProvider.GetDependency() + sutProvider.GetDependency() .ExcludeAsync(Arg.Any>(), userId).Returns(0); await Assert.ThrowsAsync(() => sutProvider.Sut.ExcludeAsync(cipherId, userId)); @@ -45,10 +45,10 @@ await sutProvider.GetDependency() [Theory, BitAutoData] public async Task ExcludeAsync_Excluded_PushesCipherSyncExcludingCurrentContext( - SutProvider sutProvider, Guid cipherId, Guid userId) + SutProvider sutProvider, Guid cipherId, Guid userId) { sutProvider.GetDependency().HasPremiumAccessAsync(userId).Returns(true); - sutProvider.GetDependency() + sutProvider.GetDependency() .ExcludeAsync(Arg.Is>(ids => ids.Single() == cipherId), userId).Returns(1); await sutProvider.Sut.ExcludeAsync(cipherId, userId); From 5216750c24e74c7caaf7ccc6ce757d37553ad84e Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 27 Oct 2026 14:11:06 -0400 Subject: [PATCH 20/20] [PM-41931] feat(dirt): Add an Exclude parameter to the exclusion procedure Lets the restore feature reuse the procedure. Passing 0 removes the user's key, because the SQL Server read treats any stored value as excluded. --- .../Dirt/CipherBrowserHealthExclusionRepository.cs | 2 +- .../Cipher_UpdateManyBrowserHealthExclusions.sql | 5 +++-- .../2026-10-27_00_AddCipherBrowserHealthExclusions.sql | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs b/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs index 4f16c768abce..9f596d992a28 100644 --- a/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs +++ b/src/Infrastructure.Dapper/Dirt/CipherBrowserHealthExclusionRepository.cs @@ -25,7 +25,7 @@ public async Task ExcludeAsync(IEnumerable cipherIds, Guid userId) await using var connection = new SqlConnection(ConnectionString); return await connection.ExecuteScalarAsync( "[dbo].[Cipher_UpdateManyBrowserHealthExclusions]", - new { Ids = ids.ToGuidIdArrayTVP(), UserId = userId }, + new { Ids = ids.ToGuidIdArrayTVP(), UserId = userId, Exclude = true }, commandType: CommandType.StoredProcedure); } } diff --git a/src/Sql/dbo/Dirt/Stored Procedures/Cipher_UpdateManyBrowserHealthExclusions.sql b/src/Sql/dbo/Dirt/Stored Procedures/Cipher_UpdateManyBrowserHealthExclusions.sql index 0c32e3736432..f1c82e1dc728 100644 --- a/src/Sql/dbo/Dirt/Stored Procedures/Cipher_UpdateManyBrowserHealthExclusions.sql +++ b/src/Sql/dbo/Dirt/Stored Procedures/Cipher_UpdateManyBrowserHealthExclusions.sql @@ -1,6 +1,7 @@ CREATE PROCEDURE [dbo].[Cipher_UpdateManyBrowserHealthExclusions] @Ids AS [dbo].[GuidIdArray] READONLY, - @UserId AS UNIQUEIDENTIFIER + @UserId AS UNIQUEIDENTIFIER, + @Exclude AS BIT AS BEGIN SET NOCOUNT ON @@ -11,7 +12,7 @@ BEGIN [BrowserHealthExclusions] = JSON_MODIFY( COALESCE(C.[BrowserHealthExclusions], N'{}'), CONCAT('$."', @UserId, '"'), - CAST(1 AS BIT) + IIF(@Exclude = 1, CAST(1 AS BIT), NULL) -- NULL removes the user's key ) FROM [dbo].[Cipher] C diff --git a/util/Migrator/DbScripts/2026-10-27_00_AddCipherBrowserHealthExclusions.sql b/util/Migrator/DbScripts/2026-10-27_00_AddCipherBrowserHealthExclusions.sql index dcc82c5335d3..6f43676697ba 100644 --- a/util/Migrator/DbScripts/2026-10-27_00_AddCipherBrowserHealthExclusions.sql +++ b/util/Migrator/DbScripts/2026-10-27_00_AddCipherBrowserHealthExclusions.sql @@ -531,7 +531,8 @@ GO CREATE OR ALTER PROCEDURE [dbo].[Cipher_UpdateManyBrowserHealthExclusions] @Ids AS [dbo].[GuidIdArray] READONLY, - @UserId AS UNIQUEIDENTIFIER + @UserId AS UNIQUEIDENTIFIER, + @Exclude AS BIT AS BEGIN SET NOCOUNT ON @@ -542,7 +543,7 @@ BEGIN [BrowserHealthExclusions] = JSON_MODIFY( COALESCE(C.[BrowserHealthExclusions], N'{}'), CONCAT('$."', @UserId, '"'), - CAST(1 AS BIT) + IIF(@Exclude = 1, CAST(1 AS BIT), NULL) -- NULL removes the user's key ) FROM [dbo].[Cipher] C