From 3a69a686098a50e6a48fbd8f1b3866ff54f3f675 Mon Sep 17 00:00:00 2001 From: Graham Walker Date: Wed, 19 Nov 2025 22:24:09 -0600 Subject: [PATCH 1/9] PM-25576 made changes to member access query to use views and add avatar color. --- .../Models/Data/MemberAccessReportDetail.cs | 1 + .../Data/OrganizationMemberBaseDetail.cs | 5 +- .../ReportFeatures/MemberAccessReportQuery.cs | 4 +- ...IOrganizationMemberBaseDetailRepository.cs | 2 +- .../OrganizationMemberBaseDetailRepository.cs | 2 +- .../OrganizationMemberBaseDetailRepository.cs | 4 +- .../Repositories/DatabaseContext.cs | 2 +- ...berAccessCipherDetailsByOrganizationId.sql | 109 + ..._GetMemberAccessDetailByOrganizationId.sql | 92 - .../dbo/Views/CollectionCipherDetailsView.sql | 15 + .../Views/CollectionGroupPermissionsView.sql | 21 + .../Views/CollectionUserPermissionsView.sql | 15 + .../2025-11-19_00_UpdateMemberAccessQuery.sql | 166 + ...-19_00_UpdateMemberAccessQuery.Designer.cs | 3443 ++++++++++++++++ ...6_2025-11-19_00_UpdateMemberAccessQuery.cs | 50 + .../DatabaseContextModelSnapshot.cs | 7 +- ...-19_00_UpdateMemberAccessQuery.Designer.cs | 3449 +++++++++++++++++ ...6_2025-11-19_00_UpdateMemberAccessQuery.cs | 45 + .../DatabaseContextModelSnapshot.cs | 7 +- ...-19_00_UpdateMemberAccessQuery.Designer.cs | 3432 ++++++++++++++++ ...1_2025-11-19_00_UpdateMemberAccessQuery.cs | 45 + .../DatabaseContextModelSnapshot.cs | 7 +- 22 files changed, 10817 insertions(+), 106 deletions(-) rename src/Infrastructure.EntityFramework/Dirt/{ => Repositories}/OrganizationMemberBaseDetailRepository.cs (92%) create mode 100644 src/Sql/dbo/Dirt/Stored Procedures/MemberAccessDetail_GetMemberAccessCipherDetailsByOrganizationId.sql delete mode 100644 src/Sql/dbo/Dirt/Stored Procedures/MemberAccessDetail_GetMemberAccessDetailByOrganizationId.sql create mode 100644 src/Sql/dbo/Views/CollectionCipherDetailsView.sql create mode 100644 src/Sql/dbo/Views/CollectionGroupPermissionsView.sql create mode 100644 src/Sql/dbo/Views/CollectionUserPermissionsView.sql create mode 100644 util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql create mode 100644 util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs create mode 100644 util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.cs create mode 100644 util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs create mode 100644 util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.cs create mode 100644 util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs create mode 100644 util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.cs diff --git a/src/Core/Dirt/Models/Data/MemberAccessReportDetail.cs b/src/Core/Dirt/Models/Data/MemberAccessReportDetail.cs index 7b54822a1e9c..6a7234e3961e 100644 --- a/src/Core/Dirt/Models/Data/MemberAccessReportDetail.cs +++ b/src/Core/Dirt/Models/Data/MemberAccessReportDetail.cs @@ -8,6 +8,7 @@ public class MemberAccessReportDetail public Guid? UserGuid { get; set; } public string UserName { get; set; } public string Email { get; set; } + public string AvatarColor { get; set; } public bool TwoFactorEnabled { get; set; } public bool AccountRecoveryEnabled { get; set; } public bool UsesKeyConnector { get; set; } diff --git a/src/Core/Dirt/Models/Data/OrganizationMemberBaseDetail.cs b/src/Core/Dirt/Models/Data/OrganizationMemberBaseDetail.cs index a68e920e660c..137747a319e4 100644 --- a/src/Core/Dirt/Models/Data/OrganizationMemberBaseDetail.cs +++ b/src/Core/Dirt/Models/Data/OrganizationMemberBaseDetail.cs @@ -1,13 +1,14 @@ // FIXME: Update this file to be null safe and then delete the line below #nullable disable -namespace Bit.Core.Dirt.Reports.Models.Data; +namespace Bit.Core.Dirt.Models.Data; public class OrganizationMemberBaseDetail { public Guid? UserGuid { get; set; } public string UserName { get; set; } public string Email { get; set; } + public string AvatarColor { get; set; } public string TwoFactorProviders { get; set; } public bool UsesKeyConnector { get; set; } public string ResetPasswordKey { get; set; } @@ -18,5 +19,5 @@ public class OrganizationMemberBaseDetail public bool? ReadOnly { get; set; } public bool? HidePasswords { get; set; } public bool? Manage { get; set; } - public Guid CipherId { get; set; } + public Guid? CipherId { get; set; } } diff --git a/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs b/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs index 83d074454d89..599895dbbb9f 100644 --- a/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs +++ b/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs @@ -48,6 +48,7 @@ await organizationMemberBaseDetailRepository.GetOrganizationMemberBaseDetailsByO b.UserGuid, b.UserName, b.Email, + b.AvatarColor, b.TwoFactorProviders, b.ResetPasswordKey, b.UsesKeyConnector, @@ -64,6 +65,7 @@ await organizationMemberBaseDetailRepository.GetOrganizationMemberBaseDetailsByO UserGuid = g.Key.UserGuid, UserName = g.Key.UserName, Email = g.Key.Email, + AvatarColor = g.Key.AvatarColor, TwoFactorEnabled = orgUsersTwoFactorEnabled.FirstOrDefault(x => x.userId == g.Key.UserGuid).twoFactorIsEnabled, AccountRecoveryEnabled = !string.IsNullOrWhiteSpace(g.Key.ResetPasswordKey) && orgAbility.UseResetPassword, UsesKeyConnector = g.Key.UsesKeyConnector, @@ -74,7 +76,7 @@ await organizationMemberBaseDetailRepository.GetOrganizationMemberBaseDetailsByO ReadOnly = g.Key.ReadOnly, HidePasswords = g.Key.HidePasswords, Manage = g.Key.Manage, - CipherIds = g.Select(c => c.CipherId) + CipherIds = g.Select(c => c.CipherId).Where(id => id.HasValue).Select(id => id.Value) }); var accessDetailsCount = accessDetails.Count(); diff --git a/src/Core/Dirt/Repositories/IOrganizationMemberBaseDetailRepository.cs b/src/Core/Dirt/Repositories/IOrganizationMemberBaseDetailRepository.cs index e2a161aa9cd2..930e491c4b50 100644 --- a/src/Core/Dirt/Repositories/IOrganizationMemberBaseDetailRepository.cs +++ b/src/Core/Dirt/Repositories/IOrganizationMemberBaseDetailRepository.cs @@ -1,4 +1,4 @@ -using Bit.Core.Dirt.Reports.Models.Data; +using Bit.Core.Dirt.Models.Data; namespace Bit.Core.Dirt.Reports.Repositories; diff --git a/src/Infrastructure.Dapper/Dirt/OrganizationMemberBaseDetailRepository.cs b/src/Infrastructure.Dapper/Dirt/OrganizationMemberBaseDetailRepository.cs index 458e72f9969a..bb5e97c508a5 100644 --- a/src/Infrastructure.Dapper/Dirt/OrganizationMemberBaseDetailRepository.cs +++ b/src/Infrastructure.Dapper/Dirt/OrganizationMemberBaseDetailRepository.cs @@ -1,5 +1,5 @@ using System.Data; -using Bit.Core.Dirt.Reports.Models.Data; +using Bit.Core.Dirt.Models.Data; using Bit.Core.Dirt.Reports.Repositories; using Bit.Core.Settings; using Bit.Infrastructure.Dapper.Repositories; diff --git a/src/Infrastructure.EntityFramework/Dirt/OrganizationMemberBaseDetailRepository.cs b/src/Infrastructure.EntityFramework/Dirt/Repositories/OrganizationMemberBaseDetailRepository.cs similarity index 92% rename from src/Infrastructure.EntityFramework/Dirt/OrganizationMemberBaseDetailRepository.cs rename to src/Infrastructure.EntityFramework/Dirt/Repositories/OrganizationMemberBaseDetailRepository.cs index 123379da90ad..8b8a14c370a2 100644 --- a/src/Infrastructure.EntityFramework/Dirt/OrganizationMemberBaseDetailRepository.cs +++ b/src/Infrastructure.EntityFramework/Dirt/Repositories/OrganizationMemberBaseDetailRepository.cs @@ -1,12 +1,12 @@ using AutoMapper; -using Bit.Core.Dirt.Reports.Models.Data; +using Bit.Core.Dirt.Models.Data; using Bit.Core.Dirt.Reports.Repositories; using Bit.Infrastructure.EntityFramework.Repositories; using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -namespace Bit.Infrastructure.EntityFramework.Dirt; +namespace Bit.Infrastructure.EntityFramework.Dirt.Repositories; public class OrganizationMemberBaseDetailRepository : BaseEntityFrameworkRepository, IOrganizationMemberBaseDetailRepository { diff --git a/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs b/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs index b748a26db2df..b6367a44acff 100644 --- a/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs +++ b/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs @@ -1,5 +1,5 @@ using Bit.Core; -using Bit.Core.Dirt.Reports.Models.Data; +using Bit.Core.Dirt.Models.Data; using Bit.Infrastructure.EntityFramework.AdminConsole.Models; using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider; using Bit.Infrastructure.EntityFramework.Auth.Models; diff --git a/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessDetail_GetMemberAccessCipherDetailsByOrganizationId.sql b/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessDetail_GetMemberAccessCipherDetailsByOrganizationId.sql new file mode 100644 index 000000000000..0df67437c420 --- /dev/null +++ b/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessDetail_GetMemberAccessCipherDetailsByOrganizationId.sql @@ -0,0 +1,109 @@ +CREATE PROCEDURE dbo.MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId + @OrganizationId UNIQUEIDENTIFIER +AS + SET NOCOUNT ON; + +IF @OrganizationId IS NULL + THROW 50000, 'OrganizationId cannot be null', 1; + + -- Direct user-collection permissions + SELECT + OU.[Id] AS [UserGuid], + OU.[Name] AS [UserName], + OU.[Email], + OU.[AvatarColor], + OU.[TwoFactorProviders], + OU.[UsesKeyConnector], + OU.[ResetPasswordKey], + CUP.[CollectionId], + CUP.[CollectionName], + NULL AS [GroupId], + NULL AS [GroupName], + CUP.[ReadOnly], + CUP.[HidePasswords], + CUP.[Manage], + CCD.[CipherId] + FROM + [dbo].[OrganizationUserUserDetailsView] OU + INNER JOIN + [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] + INNER JOIN + [dbo].[CollectionUserPermissionsView] CUP ON CUP.[OrganizationUserId] = OU.[Id] + INNER JOIN + [dbo].[CollectionCipherDetailsView] CCD ON CCD.[CollectionId] = CUP.[CollectionId] + WHERE + O.[Id] = @OrganizationId + AND O.[Enabled] = 1 + AND CUP.[OrganizationId] = @OrganizationId + AND CCD.[CipherOrganizationId] = @OrganizationId + AND OU.[Status] IN (0, 1, 2) -- Invited, Accepted, Confirmed + AND CCD.[DeletedDate] IS NULL + + UNION ALL + + -- Group-based collection permissions + SELECT + OU.[Id] AS [UserGuid], + OU.[Name] AS [UserName], + OU.[Email], + OU.[AvatarColor], + OU.[TwoFactorProviders], + OU.[UsesKeyConnector], + OU.[ResetPasswordKey], + CGP.[CollectionId], + CGP.[CollectionName], + CGP.[GroupId], + CGP.[GroupName], + CGP.[ReadOnly], + CGP.[HidePasswords], + CGP.[Manage], + CCD.[CipherId] + FROM + [dbo].[OrganizationUserUserDetailsView] OU + INNER JOIN + [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] + INNER JOIN + [dbo].[CollectionGroupPermissionsView] CGP ON CGP.[OrganizationUserId] = OU.[Id] + INNER JOIN + [dbo].[CollectionCipherDetailsView] CCD ON CCD.[CollectionId] = CGP.[CollectionId] + WHERE + O.[Id] = @OrganizationId + AND O.[Enabled] = 1 + AND CGP.[OrganizationId] = @OrganizationId + AND CCD.[CipherOrganizationId] = @OrganizationId + AND OU.[Status] IN (0, 1, 2) -- Invited, Accepted, Confirmed + AND CCD.[DeletedDate] IS NULL + + UNION ALL + + -- Users without collection access + SELECT + OU.[Id] AS [UserGuid], + OU.[Name] AS [UserName], + OU.[Email], + OU.[AvatarColor], + OU.[TwoFactorProviders], + OU.[UsesKeyConnector], + OU.[ResetPasswordKey], + NULL AS [CollectionId], + NULL AS [CollectionName], + NULL AS [GroupId], + NULL AS [GroupName], + NULL AS [ReadOnly], + NULL AS [HidePasswords], + NULL AS [Manage], + NULL AS [CipherId] + FROM + [dbo].[OrganizationUserUserDetailsView] OU + INNER JOIN + [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] + WHERE + O.[Id] = @OrganizationId + AND O.[Enabled] = 1 + AND OU.[Status] IN (0, 1, 2) -- Invited, Accepted, Confirmed + AND NOT EXISTS ( + SELECT 1 + FROM [dbo].[CollectionUserPermissionsView] CUP + WHERE CUP.[OrganizationUserId] = OU.[Id] + AND CUP.[OrganizationId] = @OrganizationId + ); diff --git a/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessDetail_GetMemberAccessDetailByOrganizationId.sql b/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessDetail_GetMemberAccessDetailByOrganizationId.sql deleted file mode 100644 index 1aaf667f6aa4..000000000000 --- a/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessDetail_GetMemberAccessDetailByOrganizationId.sql +++ /dev/null @@ -1,92 +0,0 @@ -CREATE PROCEDURE dbo.MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId - @OrganizationId UNIQUEIDENTIFIER -AS - SET NOCOUNT ON; - -IF @OrganizationId IS NULL - THROW 50000, 'OrganizationId cannot be null', 1; - - SELECT - OU.Id AS UserGuid, - U.Name AS UserName, - ISNULL(U.Email, OU.Email) as 'Email', - U.TwoFactorProviders, - U.UsesKeyConnector, - OU.ResetPasswordKey, - CC.CollectionId, - C.Name AS CollectionName, - NULL AS GroupId, - NULL AS GroupName, - CU.ReadOnly, - CU.HidePasswords, - CU.Manage, - Cipher.Id AS CipherId - FROM dbo.OrganizationUser OU - LEFT JOIN dbo.[User] U ON U.Id = OU.UserId - INNER JOIN dbo.Organization O ON O.Id = OU.OrganizationId - AND O.Id = @OrganizationId - AND O.Enabled = 1 - INNER JOIN dbo.CollectionUser CU ON CU.OrganizationUserId = OU.Id - INNER JOIN dbo.Collection C ON C.Id = CU.CollectionId and C.OrganizationId = @OrganizationId - INNER JOIN dbo.CollectionCipher CC ON CC.CollectionId = C.Id - INNER JOIN dbo.Cipher Cipher ON Cipher.Id = CC.CipherId AND Cipher.OrganizationId = @OrganizationId - WHERE OU.Status IN (0,1,2) -- Invited, Accepted and Confirmed Users - AND Cipher.DeletedDate IS NULL -UNION ALL - -- Group-based collection permissions - SELECT - OU.Id AS UserGuid, - U.Name AS UserName, - ISNULL(U.Email, OU.Email) as 'Email', - U.TwoFactorProviders, - U.UsesKeyConnector, - OU.ResetPasswordKey, - CC.CollectionId, - C.Name AS CollectionName, - G.Id AS GroupId, - G.Name AS GroupName, - CG.ReadOnly, - CG.HidePasswords, - CG.Manage, - Cipher.Id AS CipherId - FROM dbo.OrganizationUser OU - LEFT JOIN dbo.[User] U ON U.Id = OU.UserId - INNER JOIN dbo.Organization O ON O.Id = OU.OrganizationId - AND O.Id = @OrganizationId - AND O.Enabled = 1 - INNER JOIN dbo.GroupUser GU ON GU.OrganizationUserId = OU.Id - INNER JOIN dbo.[Group] G ON G.Id = GU.GroupId - INNER JOIN dbo.CollectionGroup CG ON CG.GroupId = G.Id - INNER JOIN dbo.Collection C ON C.Id = CG.CollectionId AND C.OrganizationId = @OrganizationId - INNER JOIN dbo.CollectionCipher CC ON CC.CollectionId = C.Id - INNER JOIN dbo.Cipher Cipher ON Cipher.Id = CC.CipherId and Cipher.OrganizationId = @OrganizationId - WHERE OU.Status IN (0,1,2) -- Invited, Accepted and Confirmed Users - AND Cipher.DeletedDate IS NULL -UNION ALL - -- Users without collection access (invited users) - -- typically invited users who have not yet accepted the invitation - -- and not yet assigned to any collection - SELECT - OU.Id AS UserGuid, - U.Name AS UserName, - ISNULL(U.Email, OU.Email) as 'Email', - U.TwoFactorProviders, - U.UsesKeyConnector, - OU.ResetPasswordKey, - null as CollectionId, - null AS CollectionName, - NULL AS GroupId, - NULL AS GroupName, - null as [ReadOnly], - null as HidePasswords, - null as Manage, - null AS CipherId - FROM dbo.OrganizationUser OU - LEFT JOIN dbo.[User] U ON U.Id = OU.UserId - INNER JOIN dbo.Organization O ON O.Id = OU.OrganizationId AND O.Id = @OrganizationId AND O.Enabled = 1 - WHERE OU.Status IN (0,1,2) -- Invited, Accepted and Confirmed Users - AND OU.Id not in ( - select OU1.Id from dbo.OrganizationUser OU1 - inner join dbo.CollectionUser CU1 on CU1.OrganizationUserId = OU1.Id - WHERE OU1.OrganizationId = @organizationId - ) diff --git a/src/Sql/dbo/Views/CollectionCipherDetailsView.sql b/src/Sql/dbo/Views/CollectionCipherDetailsView.sql new file mode 100644 index 000000000000..d1c96a0d9ed4 --- /dev/null +++ b/src/Sql/dbo/Views/CollectionCipherDetailsView.sql @@ -0,0 +1,15 @@ +CREATE OR ALTER VIEW [dbo].[CollectionCipherDetailsView] +AS +SELECT + CC.[CollectionId], + C.[OrganizationId] AS [CollectionOrganizationId], + CC.[CipherId], + Ci.[OrganizationId] AS [CipherOrganizationId], + Ci.[DeletedDate] +FROM + [dbo].[CollectionCipher] CC + INNER JOIN + [dbo].[Collection] C ON C.[Id] = CC.[CollectionId] + INNER JOIN + [dbo].[Cipher] Ci ON Ci.[Id] = CC.[CipherId] +GO diff --git a/src/Sql/dbo/Views/CollectionGroupPermissionsView.sql b/src/Sql/dbo/Views/CollectionGroupPermissionsView.sql new file mode 100644 index 000000000000..3a62b76a3fe2 --- /dev/null +++ b/src/Sql/dbo/Views/CollectionGroupPermissionsView.sql @@ -0,0 +1,21 @@ +CREATE OR ALTER VIEW [dbo].[CollectionGroupPermissionsView] +AS +SELECT + GU.[OrganizationUserId], + G.[Id] AS [GroupId], + G.[Name] AS [GroupName], + G.[OrganizationId], + CG.[CollectionId], + C.[Name] AS [CollectionName], + CG.[ReadOnly], + CG.[HidePasswords], + CG.[Manage] +FROM + [dbo].[GroupUser] GU + INNER JOIN + [dbo].[Group] G ON G.[Id] = GU.[GroupId] + INNER JOIN + [dbo].[CollectionGroup] CG ON CG.[GroupId] = G.[Id] + INNER JOIN + [dbo].[Collection] C ON C.[Id] = CG.[CollectionId] +GO diff --git a/src/Sql/dbo/Views/CollectionUserPermissionsView.sql b/src/Sql/dbo/Views/CollectionUserPermissionsView.sql new file mode 100644 index 000000000000..cf5fb10afdec --- /dev/null +++ b/src/Sql/dbo/Views/CollectionUserPermissionsView.sql @@ -0,0 +1,15 @@ +CREATE OR ALTER VIEW [dbo].[CollectionUserPermissionsView] +AS +SELECT + CU.[OrganizationUserId], + CU.[CollectionId], + C.[OrganizationId], + C.[Name] AS [CollectionName], + CU.[ReadOnly], + CU.[HidePasswords], + CU.[Manage] +FROM + [dbo].[CollectionUser] CU + INNER JOIN + [dbo].[Collection] C ON C.[Id] = CU.[CollectionId] +GO diff --git a/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql b/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql new file mode 100644 index 000000000000..b5f58eeb239f --- /dev/null +++ b/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql @@ -0,0 +1,166 @@ +CREATE OR ALTER VIEW [dbo].[CollectionCipherDetailsView] +AS +SELECT + CC.[CollectionId], + C.[OrganizationId] AS [CollectionOrganizationId], + CC.[CipherId], + Ci.[OrganizationId] AS [CipherOrganizationId], + Ci.[DeletedDate] +FROM + [dbo].[CollectionCipher] CC +INNER JOIN + [dbo].[Collection] C ON C.[Id] = CC.[CollectionId] +INNER JOIN + [dbo].[Cipher] Ci ON Ci.[Id] = CC.[CipherId] +GO + +CREATE OR ALTER VIEW [dbo].[CollectionGroupPermissionsView] +AS +SELECT + GU.[OrganizationUserId], + G.[Id] AS [GroupId], + G.[Name] AS [GroupName], + G.[OrganizationId], + CG.[CollectionId], + C.[Name] AS [CollectionName], + CG.[ReadOnly], + CG.[HidePasswords], + CG.[Manage] +FROM + [dbo].[GroupUser] GU +INNER JOIN + [dbo].[Group] G ON G.[Id] = GU.[GroupId] +INNER JOIN + [dbo].[CollectionGroup] CG ON CG.[GroupId] = G.[Id] +INNER JOIN + [dbo].[Collection] C ON C.[Id] = CG.[CollectionId] +GO + +CREATE OR ALTER VIEW [dbo].[CollectionUserPermissionsView] +AS +SELECT + CU.[OrganizationUserId], + CU.[CollectionId], + C.[OrganizationId], + C.[Name] AS [CollectionName], + CU.[ReadOnly], + CU.[HidePasswords], + CU.[Manage] +FROM + [dbo].[CollectionUser] CU +INNER JOIN + [dbo].[Collection] C ON C.[Id] = CU.[CollectionId] +GO + +CREATE OR ALTER PROCEDURE [dbo].[MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + IF @OrganizationId IS NULL + THROW 50000, 'OrganizationId cannot be null', 1; + + -- Direct user-collection permissions + SELECT + OU.[Id] AS [UserGuid], + OU.[Name] AS [UserName], + OU.[Email], + OU.[AvatarColor], + OU.[TwoFactorProviders], + OU.[UsesKeyConnector], + OU.[ResetPasswordKey], + CUP.[CollectionId], + CUP.[CollectionName], + NULL AS [GroupId], + NULL AS [GroupName], + CUP.[ReadOnly], + CUP.[HidePasswords], + CUP.[Manage], + CCD.[CipherId] + FROM + [dbo].[OrganizationUserUserDetailsView] OU + INNER JOIN + [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] + INNER JOIN + [dbo].[CollectionUserPermissionsView] CUP ON CUP.[OrganizationUserId] = OU.[Id] + INNER JOIN + [dbo].[CollectionCipherDetailsView] CCD ON CCD.[CollectionId] = CUP.[CollectionId] + WHERE + O.[Id] = @OrganizationId + AND O.[Enabled] = 1 + AND CUP.[OrganizationId] = @OrganizationId + AND CCD.[CipherOrganizationId] = @OrganizationId + AND OU.[Status] IN (0, 1, 2) -- Invited, Accepted, Confirmed + AND CCD.[DeletedDate] IS NULL + + UNION ALL + + -- Group-based collection permissions + SELECT + OU.[Id] AS [UserGuid], + OU.[Name] AS [UserName], + OU.[Email], + OU.[AvatarColor], + OU.[TwoFactorProviders], + OU.[UsesKeyConnector], + OU.[ResetPasswordKey], + CGP.[CollectionId], + CGP.[CollectionName], + CGP.[GroupId], + CGP.[GroupName], + CGP.[ReadOnly], + CGP.[HidePasswords], + CGP.[Manage], + CCD.[CipherId] + FROM + [dbo].[OrganizationUserUserDetailsView] OU + INNER JOIN + [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] + INNER JOIN + [dbo].[CollectionGroupPermissionsView] CGP ON CGP.[OrganizationUserId] = OU.[Id] + INNER JOIN + [dbo].[CollectionCipherDetailsView] CCD ON CCD.[CollectionId] = CGP.[CollectionId] + WHERE + O.[Id] = @OrganizationId + AND O.[Enabled] = 1 + AND CGP.[OrganizationId] = @OrganizationId + AND CCD.[CipherOrganizationId] = @OrganizationId + AND OU.[Status] IN (0, 1, 2) -- Invited, Accepted, Confirmed + AND CCD.[DeletedDate] IS NULL + + UNION ALL + + -- Users without collection access + SELECT + OU.[Id] AS [UserGuid], + OU.[Name] AS [UserName], + OU.[Email], + OU.[AvatarColor], + OU.[TwoFactorProviders], + OU.[UsesKeyConnector], + OU.[ResetPasswordKey], + NULL AS [CollectionId], + NULL AS [CollectionName], + NULL AS [GroupId], + NULL AS [GroupName], + NULL AS [ReadOnly], + NULL AS [HidePasswords], + NULL AS [Manage], + NULL AS [CipherId] + FROM + [dbo].[OrganizationUserUserDetailsView] OU + INNER JOIN + [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] + WHERE + O.[Id] = @OrganizationId + AND O.[Enabled] = 1 + AND OU.[Status] IN (0, 1, 2) -- Invited, Accepted, Confirmed + AND NOT EXISTS ( + SELECT 1 + FROM [dbo].[CollectionUserPermissionsView] CUP + WHERE CUP.[OrganizationUserId] = OU.[Id] + AND CUP.[OrganizationId] = @OrganizationId + ) +END +GO \ No newline at end of file diff --git a/util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs b/util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs new file mode 100644 index 000000000000..0a835371ae9a --- /dev/null +++ b/util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs @@ -0,0 +1,3443 @@ +// +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("20251119211526_2025-11-19_00_UpdateMemberAccessQuery")] + partial class _20251119_00_UpdateMemberAccessQuery + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Bit.Core.Dirt.Models.Data.OrganizationMemberBaseDetail", b => + { + b.Property("AvatarColor") + .HasColumnType("longtext"); + + 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.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("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("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("OwnersNotifiedOfAutoscaling") + .HasColumnType("datetime(6)"); + + 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("UseEvents") + .HasColumnType("tinyint(1)"); + + b.Property("UseGroups") + .HasColumnType("tinyint(1)"); + + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("UseOrganizationDomains") + .HasColumnType("tinyint(1)"); + + b.Property("UsePasswordManager") + .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("Id", "Enabled") + .HasAnnotation("Npgsql:IndexInclude", new[] { "UseTotp" }); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.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.AdminConsole.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.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") + .HasColumnType("longtext"); + + b.Property("GatewaySubscriptionId") + .HasColumnType("longtext"); + + 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.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("int"); + + 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.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.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.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("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.Collection", b => + { + b.Property("Id") + .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("OrganizationId"); + + b.ToTable("Collection", (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.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.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.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Active") + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + 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("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("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("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("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("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + 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.Send", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletionDate") + .HasColumnType("datetime(6)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("Emails") + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("HideEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .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("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("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("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("VerifyDevices") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + 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.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("ArchivedDate") + .HasColumnType("datetime(6)"); + + 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("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.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.AdminConsole.Models.OrganizationIntegrationConfiguration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationIntegration", "OrganizationIntegration") + .WithMany() + .HasForeignKey("OrganizationIntegrationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationIntegration"); + }); + + 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.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.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.Collection", b => + { + 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.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.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.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.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.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.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.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.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.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.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + 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/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.cs new file mode 100644 index 000000000000..573adf03b8bf --- /dev/null +++ b/util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.cs @@ -0,0 +1,50 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +/// +public partial class _20251119_00_UpdateMemberAccessQuery : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "CipherId", + table: "OrganizationMemberBaseDetails", + type: "char(36)", + nullable: true, + collation: "ascii_general_ci", + oldClrType: typeof(Guid), + oldType: "char(36)") + .OldAnnotation("Relational:Collation", "ascii_general_ci"); + + migrationBuilder.AddColumn( + name: "AvatarColor", + table: "OrganizationMemberBaseDetails", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "AvatarColor", + table: "OrganizationMemberBaseDetails"); + + migrationBuilder.AlterColumn( + name: "CipherId", + table: "OrganizationMemberBaseDetails", + type: "char(36)", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + collation: "ascii_general_ci", + oldClrType: typeof(Guid), + oldType: "char(36)", + oldNullable: true) + .OldAnnotation("Relational:Collation", "ascii_general_ci"); + } +} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index 62d9d681eadc..b77df53df29e 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -22,9 +22,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - modelBuilder.Entity("Bit.Core.Dirt.Reports.Models.Data.OrganizationMemberBaseDetail", b => + modelBuilder.Entity("Bit.Core.Dirt.Models.Data.OrganizationMemberBaseDetail", b => { - b.Property("CipherId") + b.Property("AvatarColor") + .HasColumnType("longtext"); + + b.Property("CipherId") .HasColumnType("char(36)"); b.Property("CollectionId") diff --git a/util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs b/util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs new file mode 100644 index 000000000000..063d1dad9bdd --- /dev/null +++ b/util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs @@ -0,0 +1,3449 @@ +// +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("20251119211516_2025-11-19_00_UpdateMemberAccessQuery")] + partial class _20251119_00_UpdateMemberAccessQuery + { + /// + 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.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Bit.Core.Dirt.Models.Data.OrganizationMemberBaseDetail", b => + { + b.Property("AvatarColor") + .HasColumnType("text"); + + 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.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("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("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("OwnersNotifiedOfAutoscaling") + .HasColumnType("timestamp with time zone"); + + 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("UseEvents") + .HasColumnType("boolean"); + + b.Property("UseGroups") + .HasColumnType("boolean"); + + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + + b.Property("UseOrganizationDomains") + .HasColumnType("boolean"); + + b.Property("UsePasswordManager") + .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("Id", "Enabled"); + + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Id", "Enabled"), new[] { "UseTotp" }); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.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.AdminConsole.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.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") + .HasColumnType("text"); + + b.Property("GatewaySubscriptionId") + .HasColumnType("text"); + + 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.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("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"); + + 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.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.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.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("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.Collection", b => + { + b.Property("Id") + .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("OrganizationId"); + + b.ToTable("Collection", (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.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.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.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean") + .HasDefaultValue(true); + + 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("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("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("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("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("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + 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.Send", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCount") + .HasColumnType("integer"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("Emails") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HideEmail") + .HasColumnType("boolean"); + + b.Property("Key") + .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("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("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("UsesKeyConnector") + .HasColumnType("boolean"); + + b.Property("VerifyDevices") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + 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.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("ArchivedDate") + .HasColumnType("timestamp with time zone"); + + 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("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.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.AdminConsole.Models.OrganizationIntegrationConfiguration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationIntegration", "OrganizationIntegration") + .WithMany() + .HasForeignKey("OrganizationIntegrationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationIntegration"); + }); + + 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.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.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.Collection", b => + { + 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.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.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.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.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.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.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.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.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.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.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + 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/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.cs new file mode 100644 index 000000000000..98ea21789eb0 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +/// +public partial class _20251119_00_UpdateMemberAccessQuery : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "CipherId", + table: "OrganizationMemberBaseDetails", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AddColumn( + name: "AvatarColor", + table: "OrganizationMemberBaseDetails", + type: "text", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "AvatarColor", + table: "OrganizationMemberBaseDetails"); + + migrationBuilder.AlterColumn( + name: "CipherId", + table: "OrganizationMemberBaseDetails", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + } +} diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index c87b6513b072..b1de782d9fa2 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -23,9 +23,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("Bit.Core.Dirt.Reports.Models.Data.OrganizationMemberBaseDetail", b => + modelBuilder.Entity("Bit.Core.Dirt.Models.Data.OrganizationMemberBaseDetail", b => { - b.Property("CipherId") + b.Property("AvatarColor") + .HasColumnType("text"); + + b.Property("CipherId") .HasColumnType("uuid"); b.Property("CollectionId") diff --git a/util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs b/util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs new file mode 100644 index 000000000000..7ad27876981d --- /dev/null +++ b/util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs @@ -0,0 +1,3432 @@ +// +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("20251119211521_2025-11-19_00_UpdateMemberAccessQuery")] + partial class _20251119_00_UpdateMemberAccessQuery + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.8"); + + modelBuilder.Entity("Bit.Core.Dirt.Models.Data.OrganizationMemberBaseDetail", b => + { + b.Property("AvatarColor") + .HasColumnType("TEXT"); + + 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.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("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("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("OwnersNotifiedOfAutoscaling") + .HasColumnType("TEXT"); + + 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("UseEvents") + .HasColumnType("INTEGER"); + + b.Property("UseGroups") + .HasColumnType("INTEGER"); + + b.Property("UseKeyConnector") + .HasColumnType("INTEGER"); + + b.Property("UseOrganizationDomains") + .HasColumnType("INTEGER"); + + b.Property("UsePasswordManager") + .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("Id", "Enabled") + .HasAnnotation("Npgsql:IndexInclude", new[] { "UseTotp" }); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.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.AdminConsole.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.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") + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .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.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.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.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.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("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.Collection", b => + { + b.Property("Id") + .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("OrganizationId"); + + b.ToTable("Collection", (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.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.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.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Active") + .HasColumnType("INTEGER") + .HasDefaultValue(true); + + 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("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("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("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("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("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .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.Send", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCount") + .HasColumnType("INTEGER"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeletionDate") + .HasColumnType("TEXT"); + + b.Property("Disabled") + .HasColumnType("INTEGER"); + + b.Property("Emails") + .HasMaxLength(1024) + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("HideEmail") + .HasColumnType("INTEGER"); + + b.Property("Key") + .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("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("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("UsesKeyConnector") + .HasColumnType("INTEGER"); + + b.Property("VerifyDevices") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + 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.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("ArchivedDate") + .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("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.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.AdminConsole.Models.OrganizationIntegrationConfiguration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationIntegration", "OrganizationIntegration") + .WithMany() + .HasForeignKey("OrganizationIntegrationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationIntegration"); + }); + + 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.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.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.Collection", b => + { + 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.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.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.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.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.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.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.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.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.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.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + 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/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.cs new file mode 100644 index 000000000000..87c0a1c5d0c6 --- /dev/null +++ b/util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations; + +/// +public partial class _20251119_00_UpdateMemberAccessQuery : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "CipherId", + table: "OrganizationMemberBaseDetails", + type: "TEXT", + nullable: true, + oldClrType: typeof(Guid), + oldType: "TEXT"); + + migrationBuilder.AddColumn( + name: "AvatarColor", + table: "OrganizationMemberBaseDetails", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "AvatarColor", + table: "OrganizationMemberBaseDetails"); + + migrationBuilder.AlterColumn( + name: "CipherId", + table: "OrganizationMemberBaseDetails", + type: "TEXT", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "TEXT", + oldNullable: true); + } +} diff --git a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs index 17f9a067ed15..5219e66c3af4 100644 --- a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -17,9 +17,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "8.0.8"); - modelBuilder.Entity("Bit.Core.Dirt.Reports.Models.Data.OrganizationMemberBaseDetail", b => + modelBuilder.Entity("Bit.Core.Dirt.Models.Data.OrganizationMemberBaseDetail", b => { - b.Property("CipherId") + b.Property("AvatarColor") + .HasColumnType("TEXT"); + + b.Property("CipherId") .HasColumnType("TEXT"); b.Property("CollectionId") From 54ee5bfa0711ca734596e9fed637df6320325a45 Mon Sep 17 00:00:00 2001 From: Graham Walker Date: Wed, 19 Nov 2025 22:43:43 -0600 Subject: [PATCH 2/9] PM-25576 fixing view sql syntax --- src/Sql/dbo/Views/CollectionCipherDetailsView.sql | 2 +- src/Sql/dbo/Views/CollectionGroupPermissionsView.sql | 2 +- src/Sql/dbo/Views/CollectionUserPermissionsView.sql | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Sql/dbo/Views/CollectionCipherDetailsView.sql b/src/Sql/dbo/Views/CollectionCipherDetailsView.sql index d1c96a0d9ed4..d4b3c310a3cd 100644 --- a/src/Sql/dbo/Views/CollectionCipherDetailsView.sql +++ b/src/Sql/dbo/Views/CollectionCipherDetailsView.sql @@ -1,4 +1,4 @@ -CREATE OR ALTER VIEW [dbo].[CollectionCipherDetailsView] +CREATE VIEW [dbo].[CollectionCipherDetailsView] AS SELECT CC.[CollectionId], diff --git a/src/Sql/dbo/Views/CollectionGroupPermissionsView.sql b/src/Sql/dbo/Views/CollectionGroupPermissionsView.sql index 3a62b76a3fe2..123295675c18 100644 --- a/src/Sql/dbo/Views/CollectionGroupPermissionsView.sql +++ b/src/Sql/dbo/Views/CollectionGroupPermissionsView.sql @@ -1,4 +1,4 @@ -CREATE OR ALTER VIEW [dbo].[CollectionGroupPermissionsView] +CREATE VIEW [dbo].[CollectionGroupPermissionsView] AS SELECT GU.[OrganizationUserId], diff --git a/src/Sql/dbo/Views/CollectionUserPermissionsView.sql b/src/Sql/dbo/Views/CollectionUserPermissionsView.sql index cf5fb10afdec..2eec4e8f03f2 100644 --- a/src/Sql/dbo/Views/CollectionUserPermissionsView.sql +++ b/src/Sql/dbo/Views/CollectionUserPermissionsView.sql @@ -1,4 +1,4 @@ -CREATE OR ALTER VIEW [dbo].[CollectionUserPermissionsView] +CREATE VIEW [dbo].[CollectionUserPermissionsView] AS SELECT CU.[OrganizationUserId], From b20665edd7121eb460100f2431fd3a2717b90bb6 Mon Sep 17 00:00:00 2001 From: Graham Walker Date: Wed, 19 Nov 2025 23:44:35 -0600 Subject: [PATCH 3/9] PM-25576 adding unit tests and updated migration sql file --- .../ReportFeatures/MemberAccessReportQuery.cs | 2 +- ...erAccessCipherDetailsByOrganizationId.sql} | 6 + .../MemberAccessReportQueryTests.cs | 974 ++++++++++++++++++ .../2025-11-19_00_UpdateMemberAccessQuery.sql | 6 + ...6_2025-11-19_00_UpdateMemberAccessQuery.cs | 50 - ...19_00_UpdateMemberAccessQuery.Designer.cs} | 2 +- ...4_2025-11-19_00_UpdateMemberAccessQuery.cs | 21 + ...6_2025-11-19_00_UpdateMemberAccessQuery.cs | 45 - ...19_00_UpdateMemberAccessQuery.Designer.cs} | 2 +- ...5_2025-11-19_00_UpdateMemberAccessQuery.cs | 21 + ...1_2025-11-19_00_UpdateMemberAccessQuery.cs | 45 - ...19_00_UpdateMemberAccessQuery.Designer.cs} | 2 +- ...9_2025-11-19_00_UpdateMemberAccessQuery.cs | 21 + 13 files changed, 1053 insertions(+), 144 deletions(-) rename src/Sql/dbo/Dirt/Stored Procedures/{MemberAccessDetail_GetMemberAccessCipherDetailsByOrganizationId.sql => MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql} (94%) create mode 100644 test/Core.Test/Dirt/ReportFeatures/MemberAccessReportQueryTests.cs delete mode 100644 util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.cs rename util/MySqlMigrations/Migrations/{20251119211526_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs => 20251120054154_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs} (99%) create mode 100644 util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.cs delete mode 100644 util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.cs rename util/PostgresMigrations/Migrations/{20251119211516_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs => 20251120054145_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs} (99%) create mode 100644 util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.cs delete mode 100644 util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.cs rename util/SqliteMigrations/Migrations/{20251119211521_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs => 20251120054149_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs} (99%) create mode 100644 util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.cs diff --git a/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs b/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs index 599895dbbb9f..d2ac10cfbfd2 100644 --- a/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs +++ b/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs @@ -67,7 +67,7 @@ await organizationMemberBaseDetailRepository.GetOrganizationMemberBaseDetailsByO Email = g.Key.Email, AvatarColor = g.Key.AvatarColor, TwoFactorEnabled = orgUsersTwoFactorEnabled.FirstOrDefault(x => x.userId == g.Key.UserGuid).twoFactorIsEnabled, - AccountRecoveryEnabled = !string.IsNullOrWhiteSpace(g.Key.ResetPasswordKey) && orgAbility.UseResetPassword, + AccountRecoveryEnabled = !string.IsNullOrWhiteSpace(g.Key.ResetPasswordKey) && orgAbility?.UseResetPassword == true, UsesKeyConnector = g.Key.UsesKeyConnector, GroupId = g.Key.GroupId, GroupName = g.Key.GroupName, diff --git a/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessDetail_GetMemberAccessCipherDetailsByOrganizationId.sql b/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql similarity index 94% rename from src/Sql/dbo/Dirt/Stored Procedures/MemberAccessDetail_GetMemberAccessCipherDetailsByOrganizationId.sql rename to src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql index 0df67437c420..bf1df627fa9a 100644 --- a/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessDetail_GetMemberAccessCipherDetailsByOrganizationId.sql +++ b/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql @@ -106,4 +106,10 @@ IF @OrganizationId IS NULL FROM [dbo].[CollectionUserPermissionsView] CUP WHERE CUP.[OrganizationUserId] = OU.[Id] AND CUP.[OrganizationId] = @OrganizationId + ) + AND NOT EXISTS ( + SELECT 1 + FROM [dbo].[CollectionGroupPermissionsView] CGP + WHERE CGP.[OrganizationUserId] = OU.[Id] + AND CGP.[OrganizationId] = @OrganizationId ); diff --git a/test/Core.Test/Dirt/ReportFeatures/MemberAccessReportQueryTests.cs b/test/Core.Test/Dirt/ReportFeatures/MemberAccessReportQueryTests.cs new file mode 100644 index 000000000000..621b645324a3 --- /dev/null +++ b/test/Core.Test/Dirt/ReportFeatures/MemberAccessReportQueryTests.cs @@ -0,0 +1,974 @@ +using AutoFixture; +using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; +using Bit.Core.Dirt.Models.Data; +using Bit.Core.Dirt.Reports.ReportFeatures; +using Bit.Core.Dirt.Reports.ReportFeatures.Requests; +using Bit.Core.Dirt.Reports.Repositories; +using Bit.Core.Models.Data.Organizations; +using Bit.Core.Services; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.Dirt.ReportFeatures; + +[SutProviderCustomize] +public class MemberAccessReportQueryTests +{ + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_MapsAvatarColor_FromDatabaseToModel( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + var expectedAvatarColor = "#FF5733"; + + var baseDetails = new List + { + new OrganizationMemberBaseDetail + { + UserGuid = Guid.NewGuid(), + UserName = "Test User", + Email = "test@example.com", + AvatarColor = expectedAvatarColor, + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = Guid.NewGuid(), + GroupId = null, + GroupName = null, + CollectionName = "Test Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = Guid.NewGuid() + } + }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(baseDetails); + + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> + { + (baseDetails[0].UserGuid.Value, false) + }); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns(new OrganizationAbility { UseResetPassword = false }); + + // Act + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + var resultList = result.ToList(); + Assert.Single(resultList); + Assert.Equal(expectedAvatarColor, resultList[0].AvatarColor); + } + + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_ExcludesUsersWithGroupBasedAccess_FromNoAccessSection( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + var userWithGroupAccess = Guid.NewGuid(); + var userWithDirectAccess = Guid.NewGuid(); + var userWithNoAccess = Guid.NewGuid(); + + var baseDetails = new List + { + // User with group-based collection access + new OrganizationMemberBaseDetail + { + UserGuid = userWithGroupAccess, + UserName = "Group User", + Email = "group@example.com", + AvatarColor = "#FF0000", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = Guid.NewGuid(), + GroupId = Guid.NewGuid(), + GroupName = "Test Group", + CollectionName = "Test Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = Guid.NewGuid() + }, + // User with direct collection access + new OrganizationMemberBaseDetail + { + UserGuid = userWithDirectAccess, + UserName = "Direct User", + Email = "direct@example.com", + AvatarColor = "#00FF00", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = Guid.NewGuid(), + GroupId = null, + GroupName = null, + CollectionName = "Another Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = Guid.NewGuid() + } + }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(baseDetails); + + var userGuids = new[] { userWithGroupAccess, userWithDirectAccess }; + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(userGuids.Select(id => (id, false)).ToList()); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns(new OrganizationAbility { UseResetPassword = false }); + + // Act + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + var resultList = result.ToList(); + Assert.Equal(2, resultList.Count); + + // Verify both users with access are in results + Assert.Contains(resultList, r => r.UserGuid == userWithGroupAccess); + Assert.Contains(resultList, r => r.UserGuid == userWithDirectAccess); + + // Verify the user with group access has group information + var groupUser = resultList.First(r => r.UserGuid == userWithGroupAccess); + Assert.NotNull(groupUser.GroupId); + Assert.Equal("Test Group", groupUser.GroupName); + } + + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_HandlesNullCipherIds_CorrectlyFiltersToNonNullable( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + var userGuid = Guid.NewGuid(); + var collectionId = Guid.NewGuid(); + var cipherId1 = Guid.NewGuid(); + var cipherId2 = Guid.NewGuid(); + + var baseDetails = new List + { + // Same user, same collection, multiple ciphers (some null) + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = collectionId, + GroupId = null, + GroupName = null, + CollectionName = "Test Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = cipherId1 + }, + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = collectionId, + GroupId = null, + GroupName = null, + CollectionName = "Test Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = null // Null cipher + }, + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = collectionId, + GroupId = null, + GroupName = null, + CollectionName = "Test Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = cipherId2 + } + }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(baseDetails); + + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, false) }); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns(new OrganizationAbility { UseResetPassword = false }); + + // Act + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + var resultList = result.ToList(); + Assert.Single(resultList); + + var cipherIds = resultList[0].CipherIds.ToList(); + + // Should only contain non-null cipher IDs + Assert.Equal(2, cipherIds.Count); + Assert.Contains(cipherId1, cipherIds); + Assert.Contains(cipherId2, cipherIds); + Assert.DoesNotContain(Guid.Empty, cipherIds); + } + + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_GroupsByUserAndCollection_WithCorrectPermissions( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + var userGuid = Guid.NewGuid(); + var collectionId = Guid.NewGuid(); + + var baseDetails = new List + { + // Same user, same collection, different ciphers + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = collectionId, + GroupId = null, + GroupName = null, + CollectionName = "Test Collection", + ReadOnly = true, + HidePasswords = true, + Manage = false, + CipherId = Guid.NewGuid() + }, + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = collectionId, + GroupId = null, + GroupName = null, + CollectionName = "Test Collection", + ReadOnly = true, + HidePasswords = true, + Manage = false, + CipherId = Guid.NewGuid() + } + }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(baseDetails); + + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, true) }); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns(new OrganizationAbility { UseResetPassword = false }); + + // Act + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + var resultList = result.ToList(); + + // Should be grouped into one record since same user, collection, and permissions + Assert.Single(resultList); + + var record = resultList[0]; + Assert.Equal(userGuid, record.UserGuid); + Assert.Equal(collectionId, record.CollectionId); + Assert.True(record.ReadOnly); + Assert.True(record.HidePasswords); + Assert.False(record.Manage); + Assert.True(record.TwoFactorEnabled); + + // Should contain both cipher IDs + Assert.Equal(2, record.CipherIds.Count()); + } + + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_MapsAccountRecoveryEnabled_WhenResetPasswordKeyPresent( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + var userWithRecovery = Guid.NewGuid(); + var userWithoutRecovery = Guid.NewGuid(); + + var baseDetails = new List + { + new OrganizationMemberBaseDetail + { + UserGuid = userWithRecovery, + UserName = "User With Recovery", + Email = "recovery@example.com", + AvatarColor = "#FF0000", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = "some-reset-password-key", + CollectionId = Guid.NewGuid(), + GroupId = null, + GroupName = null, + CollectionName = "Test Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = Guid.NewGuid() + }, + new OrganizationMemberBaseDetail + { + UserGuid = userWithoutRecovery, + UserName = "User Without Recovery", + Email = "norecovery@example.com", + AvatarColor = "#00FF00", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = Guid.NewGuid(), + GroupId = null, + GroupName = null, + CollectionName = "Test Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = Guid.NewGuid() + } + }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(baseDetails); + + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> + { + (userWithRecovery, false), + (userWithoutRecovery, false) + }); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns(new OrganizationAbility { UseResetPassword = true }); + + // Act + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + var resultList = result.ToList(); + Assert.Equal(2, resultList.Count); + + var withRecovery = resultList.First(r => r.UserGuid == userWithRecovery); + var withoutRecovery = resultList.First(r => r.UserGuid == userWithoutRecovery); + + Assert.True(withRecovery.AccountRecoveryEnabled); + Assert.False(withoutRecovery.AccountRecoveryEnabled); + } + + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_MapsUsesKeyConnector_Correctly( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + var userWithKeyConnector = Guid.NewGuid(); + + var baseDetails = new List + { + new OrganizationMemberBaseDetail + { + UserGuid = userWithKeyConnector, + UserName = "Key Connector User", + Email = "keyconnector@example.com", + AvatarColor = "#0000FF", + TwoFactorProviders = "[]", + UsesKeyConnector = true, + ResetPasswordKey = null, + CollectionId = Guid.NewGuid(), + GroupId = null, + GroupName = null, + CollectionName = "Test Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = Guid.NewGuid() + } + }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(baseDetails); + + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> + { + (userWithKeyConnector, false) + }); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns(new OrganizationAbility { UseResetPassword = false }); + + // Act + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + var resultList = result.ToList(); + Assert.Single(resultList); + Assert.True(resultList[0].UsesKeyConnector); + } + + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_SeparatesDirectAndGroupAccess_ForSameUser( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + var userGuid = Guid.NewGuid(); + var directCollectionId = Guid.NewGuid(); + var groupCollectionId = Guid.NewGuid(); + + var baseDetails = new List + { + // Direct access + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = directCollectionId, + GroupId = null, + GroupName = null, + CollectionName = "Direct Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = Guid.NewGuid() + }, + // Group access + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = groupCollectionId, + GroupId = Guid.NewGuid(), + GroupName = "Test Group", + CollectionName = "Group Collection", + ReadOnly = true, + HidePasswords = false, + Manage = false, + CipherId = Guid.NewGuid() + } + }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(baseDetails); + + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, false) }); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns(new OrganizationAbility { UseResetPassword = false }); + + // Act + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + var resultList = result.ToList(); + Assert.Equal(2, resultList.Count); + + var directAccess = resultList.First(r => r.CollectionId == directCollectionId); + var groupAccess = resultList.First(r => r.CollectionId == groupCollectionId); + + // Direct access should not have group info + Assert.Null(directAccess.GroupId); + Assert.Null(directAccess.GroupName); + Assert.True(directAccess.Manage); + Assert.False(directAccess.ReadOnly); + + // Group access should have group info + Assert.NotNull(groupAccess.GroupId); + Assert.Equal("Test Group", groupAccess.GroupName); + Assert.False(groupAccess.Manage); + Assert.True(groupAccess.ReadOnly); + } + + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_WithNoMembers_ReturnsEmptyCollection( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(new List()); + + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)>()); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns(new OrganizationAbility { UseResetPassword = false }); + + // Act + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + Assert.Empty(result); + } + + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_WhenOrgAbilityIsNull_HandlesGracefully( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + var userGuid = Guid.NewGuid(); + + var baseDetails = new List + { + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = "some-key", + CollectionId = Guid.NewGuid(), + GroupId = null, + GroupName = null, + CollectionName = "Test Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = Guid.NewGuid() + } + }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(baseDetails); + + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, false) }); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns((OrganizationAbility)null); + + // Act & Assert - Should not throw + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + var resultList = result.ToList(); + Assert.Single(resultList); + // Should default to false when orgAbility is null + Assert.False(resultList[0].AccountRecoveryEnabled); + } + + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_WithCollectionWithoutCiphers_ReturnsEmptyCipherIds( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + var userGuid = Guid.NewGuid(); + var collectionId = Guid.NewGuid(); + + var baseDetails = new List + { + // Collection access with null cipher (collection has no ciphers) + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = collectionId, + GroupId = null, + GroupName = null, + CollectionName = "Empty Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = null + } + }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(baseDetails); + + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, false) }); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns(new OrganizationAbility { UseResetPassword = false }); + + // Act + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + var resultList = result.ToList(); + Assert.Single(resultList); + Assert.Empty(resultList[0].CipherIds); + } + + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_WithUserInMultipleGroupsSameCollection_AggregatesCipherIds( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + var userGuid = Guid.NewGuid(); + var collectionId = Guid.NewGuid(); + var group1Id = Guid.NewGuid(); + var group2Id = Guid.NewGuid(); + var cipher1 = Guid.NewGuid(); + var cipher2 = Guid.NewGuid(); + + var baseDetails = new List + { + // Same user, same collection, group 1 + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = collectionId, + GroupId = group1Id, + GroupName = "Group 1", + CollectionName = "Test Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = cipher1 + }, + // Same user, same collection, group 2 + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = collectionId, + GroupId = group2Id, + GroupName = "Group 2", + CollectionName = "Test Collection", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = cipher2 + } + }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(baseDetails); + + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, false) }); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns(new OrganizationAbility { UseResetPassword = false }); + + // Act + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + var resultList = result.ToList(); + + // Should create separate records for each group since GroupId is different + Assert.Equal(2, resultList.Count); + + var group1Access = resultList.First(r => r.GroupId == group1Id); + var group2Access = resultList.First(r => r.GroupId == group2Id); + + Assert.Equal("Group 1", group1Access.GroupName); + Assert.Equal("Group 2", group2Access.GroupName); + Assert.Single(group1Access.CipherIds); + Assert.Single(group2Access.CipherIds); + } + + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_WithMixedPermissions_GroupsCorrectly( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + var userGuid = Guid.NewGuid(); + var collection1Id = Guid.NewGuid(); + var collection2Id = Guid.NewGuid(); + + var baseDetails = new List + { + // Collection 1 - ReadOnly + HidePasswords + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = collection1Id, + GroupId = null, + GroupName = null, + CollectionName = "Collection 1", + ReadOnly = true, + HidePasswords = true, + Manage = false, + CipherId = Guid.NewGuid() + }, + // Collection 2 - Manage access + new OrganizationMemberBaseDetail + { + UserGuid = userGuid, + UserName = "Test User", + Email = "test@example.com", + AvatarColor = "#FF5733", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = collection2Id, + GroupId = null, + GroupName = null, + CollectionName = "Collection 2", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = Guid.NewGuid() + } + }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(baseDetails); + + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, false) }); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns(new OrganizationAbility { UseResetPassword = false }); + + // Act + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + var resultList = result.ToList(); + Assert.Equal(2, resultList.Count); + + var collection1 = resultList.First(r => r.CollectionId == collection1Id); + var collection2 = resultList.First(r => r.CollectionId == collection2Id); + + Assert.True(collection1.ReadOnly); + Assert.True(collection1.HidePasswords); + Assert.False(collection1.Manage); + + Assert.False(collection2.ReadOnly); + Assert.False(collection2.HidePasswords); + Assert.True(collection2.Manage); + } + + [Theory] + [BitAutoData] + public async Task GetMemberAccessReportsAsync_WhenTwoFactorQueryReturnsPartialResults_DefaultsToFalse( + SutProvider sutProvider) + { + // Arrange + var fixture = new Fixture(); + var organizationId = fixture.Create(); + var request = new MemberAccessReportRequest { OrganizationId = organizationId }; + var user1 = Guid.NewGuid(); + var user2 = Guid.NewGuid(); + + var baseDetails = new List + { + new OrganizationMemberBaseDetail + { + UserGuid = user1, + UserName = "User 1", + Email = "user1@example.com", + AvatarColor = "#FF0000", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = Guid.NewGuid(), + GroupId = null, + GroupName = null, + CollectionName = "Collection 1", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = Guid.NewGuid() + }, + new OrganizationMemberBaseDetail + { + UserGuid = user2, + UserName = "User 2", + Email = "user2@example.com", + AvatarColor = "#00FF00", + TwoFactorProviders = "[]", + UsesKeyConnector = false, + ResetPasswordKey = null, + CollectionId = Guid.NewGuid(), + GroupId = null, + GroupName = null, + CollectionName = "Collection 2", + ReadOnly = false, + HidePasswords = false, + Manage = true, + CipherId = Guid.NewGuid() + } + }; + + sutProvider.GetDependency() + .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) + .Returns(baseDetails); + + // Only return 2FA status for user1, not user2 + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(Arg.Any>()) + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> + { + (user1, true) + // user2 is missing from results + }); + + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organizationId) + .Returns(new OrganizationAbility { UseResetPassword = false }); + + // Act + var result = await sutProvider.Sut.GetMemberAccessReportsAsync(request); + + // Assert + var resultList = result.ToList(); + Assert.Equal(2, resultList.Count); + + var user1Result = resultList.First(r => r.UserGuid == user1); + var user2Result = resultList.First(r => r.UserGuid == user2); + + Assert.True(user1Result.TwoFactorEnabled); + // User2 should default to false when not in TwoFactorQuery results + Assert.False(user2Result.TwoFactorEnabled); + } +} diff --git a/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql b/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql index b5f58eeb239f..eceefb924a4a 100644 --- a/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql +++ b/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql @@ -162,5 +162,11 @@ BEGIN WHERE CUP.[OrganizationUserId] = OU.[Id] AND CUP.[OrganizationId] = @OrganizationId ) + AND NOT EXISTS ( + SELECT 1 + FROM [dbo].[CollectionGroupPermissionsView] CGP + WHERE CGP.[OrganizationUserId] = OU.[Id] + AND CGP.[OrganizationId] = @OrganizationId + ) END GO \ No newline at end of file diff --git a/util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.cs deleted file mode 100644 index 573adf03b8bf..000000000000 --- a/util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.MySqlMigrations.Migrations; - -/// -public partial class _20251119_00_UpdateMemberAccessQuery : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "CipherId", - table: "OrganizationMemberBaseDetails", - type: "char(36)", - nullable: true, - collation: "ascii_general_ci", - oldClrType: typeof(Guid), - oldType: "char(36)") - .OldAnnotation("Relational:Collation", "ascii_general_ci"); - - migrationBuilder.AddColumn( - name: "AvatarColor", - table: "OrganizationMemberBaseDetails", - type: "longtext", - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "AvatarColor", - table: "OrganizationMemberBaseDetails"); - - migrationBuilder.AlterColumn( - name: "CipherId", - table: "OrganizationMemberBaseDetails", - type: "char(36)", - nullable: false, - defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), - collation: "ascii_general_ci", - oldClrType: typeof(Guid), - oldType: "char(36)", - oldNullable: true) - .OldAnnotation("Relational:Collation", "ascii_general_ci"); - } -} diff --git a/util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs b/util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs similarity index 99% rename from util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs rename to util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs index 0a835371ae9a..783653574fc2 100644 --- a/util/MySqlMigrations/Migrations/20251119211526_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs +++ b/util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs @@ -12,7 +12,7 @@ namespace Bit.MySqlMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20251119211526_2025-11-19_00_UpdateMemberAccessQuery")] + [Migration("20251120054154_2025-11-19_00_UpdateMemberAccessQuery")] partial class _20251119_00_UpdateMemberAccessQuery { /// diff --git a/util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.cs new file mode 100644 index 000000000000..3f6afcb2bdf0 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +/// +public partial class _20251119_00_UpdateMemberAccessQuery : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } +} diff --git a/util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.cs deleted file mode 100644 index 98ea21789eb0..000000000000 --- a/util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.PostgresMigrations.Migrations; - -/// -public partial class _20251119_00_UpdateMemberAccessQuery : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "CipherId", - table: "OrganizationMemberBaseDetails", - type: "uuid", - nullable: true, - oldClrType: typeof(Guid), - oldType: "uuid"); - - migrationBuilder.AddColumn( - name: "AvatarColor", - table: "OrganizationMemberBaseDetails", - type: "text", - nullable: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "AvatarColor", - table: "OrganizationMemberBaseDetails"); - - migrationBuilder.AlterColumn( - name: "CipherId", - table: "OrganizationMemberBaseDetails", - type: "uuid", - nullable: false, - defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), - oldClrType: typeof(Guid), - oldType: "uuid", - oldNullable: true); - } -} diff --git a/util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs b/util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs similarity index 99% rename from util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs rename to util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs index 063d1dad9bdd..86f309bf6c03 100644 --- a/util/PostgresMigrations/Migrations/20251119211516_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs +++ b/util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs @@ -12,7 +12,7 @@ namespace Bit.PostgresMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20251119211516_2025-11-19_00_UpdateMemberAccessQuery")] + [Migration("20251120054145_2025-11-19_00_UpdateMemberAccessQuery")] partial class _20251119_00_UpdateMemberAccessQuery { /// diff --git a/util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.cs new file mode 100644 index 000000000000..c22cd2982f81 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +/// +public partial class _20251119_00_UpdateMemberAccessQuery : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } +} diff --git a/util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.cs deleted file mode 100644 index 87c0a1c5d0c6..000000000000 --- a/util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.SqliteMigrations.Migrations; - -/// -public partial class _20251119_00_UpdateMemberAccessQuery : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "CipherId", - table: "OrganizationMemberBaseDetails", - type: "TEXT", - nullable: true, - oldClrType: typeof(Guid), - oldType: "TEXT"); - - migrationBuilder.AddColumn( - name: "AvatarColor", - table: "OrganizationMemberBaseDetails", - type: "TEXT", - nullable: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "AvatarColor", - table: "OrganizationMemberBaseDetails"); - - migrationBuilder.AlterColumn( - name: "CipherId", - table: "OrganizationMemberBaseDetails", - type: "TEXT", - nullable: false, - defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), - oldClrType: typeof(Guid), - oldType: "TEXT", - oldNullable: true); - } -} diff --git a/util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs b/util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs similarity index 99% rename from util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs rename to util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs index 7ad27876981d..9791e6f3a5d5 100644 --- a/util/SqliteMigrations/Migrations/20251119211521_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs +++ b/util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs @@ -11,7 +11,7 @@ namespace Bit.SqliteMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20251119211521_2025-11-19_00_UpdateMemberAccessQuery")] + [Migration("20251120054149_2025-11-19_00_UpdateMemberAccessQuery")] partial class _20251119_00_UpdateMemberAccessQuery { /// diff --git a/util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.cs new file mode 100644 index 000000000000..5ca3c085753f --- /dev/null +++ b/util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations; + +/// +public partial class _20251119_00_UpdateMemberAccessQuery : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } +} From 113640828707dc0ca65de63a71c707e46e987312 Mon Sep 17 00:00:00 2001 From: Graham Walker Date: Wed, 19 Nov 2025 23:56:56 -0600 Subject: [PATCH 4/9] PM-25576 fixing sql formatting --- ...berAccessCipherDetailsByOrganizationId.sql | 71 ++++++++++--------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql b/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql index bf1df627fa9a..76955fc184f9 100644 --- a/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql +++ b/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql @@ -1,9 +1,10 @@ -CREATE PROCEDURE dbo.MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId +CREATE PROCEDURE [dbo].[MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId] @OrganizationId UNIQUEIDENTIFIER AS - SET NOCOUNT ON; +BEGIN + SET NOCOUNT ON -IF @OrganizationId IS NULL + IF @OrganizationId IS NULL THROW 50000, 'OrganizationId cannot be null', 1; -- Direct user-collection permissions @@ -25,19 +26,19 @@ IF @OrganizationId IS NULL CCD.[CipherId] FROM [dbo].[OrganizationUserUserDetailsView] OU - INNER JOIN + INNER JOIN [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] - INNER JOIN + INNER JOIN [dbo].[CollectionUserPermissionsView] CUP ON CUP.[OrganizationUserId] = OU.[Id] - INNER JOIN + INNER JOIN [dbo].[CollectionCipherDetailsView] CCD ON CCD.[CollectionId] = CUP.[CollectionId] WHERE O.[Id] = @OrganizationId - AND O.[Enabled] = 1 - AND CUP.[OrganizationId] = @OrganizationId - AND CCD.[CipherOrganizationId] = @OrganizationId - AND OU.[Status] IN (0, 1, 2) -- Invited, Accepted, Confirmed - AND CCD.[DeletedDate] IS NULL + AND O.[Enabled] = 1 + AND CUP.[OrganizationId] = @OrganizationId + AND CCD.[CipherOrganizationId] = @OrganizationId + AND OU.[Status] IN (0, 1, 2) -- Invited, Accepted, Confirmed + AND CCD.[DeletedDate] IS NULL UNION ALL @@ -60,19 +61,19 @@ IF @OrganizationId IS NULL CCD.[CipherId] FROM [dbo].[OrganizationUserUserDetailsView] OU - INNER JOIN + INNER JOIN [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] - INNER JOIN + INNER JOIN [dbo].[CollectionGroupPermissionsView] CGP ON CGP.[OrganizationUserId] = OU.[Id] - INNER JOIN + INNER JOIN [dbo].[CollectionCipherDetailsView] CCD ON CCD.[CollectionId] = CGP.[CollectionId] WHERE O.[Id] = @OrganizationId - AND O.[Enabled] = 1 - AND CGP.[OrganizationId] = @OrganizationId - AND CCD.[CipherOrganizationId] = @OrganizationId - AND OU.[Status] IN (0, 1, 2) -- Invited, Accepted, Confirmed - AND CCD.[DeletedDate] IS NULL + AND O.[Enabled] = 1 + AND CGP.[OrganizationId] = @OrganizationId + AND CCD.[CipherOrganizationId] = @OrganizationId + AND OU.[Status] IN (0, 1, 2) -- Invited, Accepted, Confirmed + AND CCD.[DeletedDate] IS NULL UNION ALL @@ -95,21 +96,23 @@ IF @OrganizationId IS NULL NULL AS [CipherId] FROM [dbo].[OrganizationUserUserDetailsView] OU - INNER JOIN + INNER JOIN [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] WHERE O.[Id] = @OrganizationId - AND O.[Enabled] = 1 - AND OU.[Status] IN (0, 1, 2) -- Invited, Accepted, Confirmed - AND NOT EXISTS ( - SELECT 1 - FROM [dbo].[CollectionUserPermissionsView] CUP - WHERE CUP.[OrganizationUserId] = OU.[Id] - AND CUP.[OrganizationId] = @OrganizationId - ) - AND NOT EXISTS ( - SELECT 1 - FROM [dbo].[CollectionGroupPermissionsView] CGP - WHERE CGP.[OrganizationUserId] = OU.[Id] - AND CGP.[OrganizationId] = @OrganizationId - ); + AND O.[Enabled] = 1 + AND OU.[Status] IN (0, 1, 2) -- Invited, Accepted, Confirmed + AND NOT EXISTS ( + SELECT 1 + FROM [dbo].[CollectionUserPermissionsView] CUP + WHERE CUP.[OrganizationUserId] = OU.[Id] + AND CUP.[OrganizationId] = @OrganizationId + ) + AND NOT EXISTS ( + SELECT 1 + FROM [dbo].[CollectionGroupPermissionsView] CGP + WHERE CGP.[OrganizationUserId] = OU.[Id] + AND CGP.[OrganizationId] = @OrganizationId + ) +END +GO From bfa167f053e52c8a86cf088da932b22432160395 Mon Sep 17 00:00:00 2001 From: Graham Walker Date: Thu, 20 Nov 2025 00:06:54 -0600 Subject: [PATCH 5/9] PM-25576 fixing linting issue --- .../EntityFrameworkServiceCollectionExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs index 3c35df2a82fb..226ff2e2cbbc 100644 --- a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs +++ b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs @@ -15,7 +15,6 @@ using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories; using Bit.Infrastructure.EntityFramework.Auth.Repositories; using Bit.Infrastructure.EntityFramework.Billing.Repositories; -using Bit.Infrastructure.EntityFramework.Dirt; using Bit.Infrastructure.EntityFramework.Dirt.Repositories; using Bit.Infrastructure.EntityFramework.KeyManagement.Repositories; using Bit.Infrastructure.EntityFramework.NotificationCenter.Repositories; From e880900689f1ac1a0ccdab601c019f8385f078ec Mon Sep 17 00:00:00 2001 From: Graham Walker Date: Thu, 20 Nov 2025 16:19:46 -0600 Subject: [PATCH 6/9] PM-25576 updating scripts with new fields for PM-26487 --- .../Data/OrganizationMemberBaseDetail.cs | 19 ++++----- ...berAccessCipherDetailsByOrganizationId.sql | 6 +++ .../2025-11-19_00_UpdateMemberAccessQuery.sql | 6 +++ ...4_2025-11-19_00_UpdateMemberAccessQuery.cs | 21 ---------- ...19_00_UpdateMemberAccessQuery.Designer.cs} | 8 +++- ...5_2025-11-19_00_UpdateMemberAccessQuery.cs | 39 +++++++++++++++++++ .../DatabaseContextModelSnapshot.cs | 6 +++ ...5_2025-11-19_00_UpdateMemberAccessQuery.cs | 21 ---------- ...19_00_UpdateMemberAccessQuery.Designer.cs} | 8 +++- ...1_2025-11-19_00_UpdateMemberAccessQuery.cs | 38 ++++++++++++++++++ .../DatabaseContextModelSnapshot.cs | 6 +++ ...9_2025-11-19_00_UpdateMemberAccessQuery.cs | 21 ---------- ...19_00_UpdateMemberAccessQuery.Designer.cs} | 8 +++- ...6_2025-11-19_00_UpdateMemberAccessQuery.cs | 38 ++++++++++++++++++ .../DatabaseContextModelSnapshot.cs | 6 +++ 15 files changed, 176 insertions(+), 75 deletions(-) delete mode 100644 util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.cs rename util/MySqlMigrations/Migrations/{20251120054154_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs => 20251120221525_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs} (99%) create mode 100644 util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.cs delete mode 100644 util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.cs rename util/PostgresMigrations/Migrations/{20251120054145_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs => 20251120221531_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs} (99%) create mode 100644 util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.cs delete mode 100644 util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.cs rename util/SqliteMigrations/Migrations/{20251120054149_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs => 20251120221536_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs} (99%) create mode 100644 util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.cs diff --git a/src/Core/Dirt/Models/Data/OrganizationMemberBaseDetail.cs b/src/Core/Dirt/Models/Data/OrganizationMemberBaseDetail.cs index 137747a319e4..69f6d1176886 100644 --- a/src/Core/Dirt/Models/Data/OrganizationMemberBaseDetail.cs +++ b/src/Core/Dirt/Models/Data/OrganizationMemberBaseDetail.cs @@ -1,21 +1,22 @@ -// FIXME: Update this file to be null safe and then delete the line below -#nullable disable +using Bit.Core.Enums; namespace Bit.Core.Dirt.Models.Data; public class OrganizationMemberBaseDetail { public Guid? UserGuid { get; set; } - public string UserName { get; set; } - public string Email { get; set; } - public string AvatarColor { get; set; } - public string TwoFactorProviders { get; set; } + public Guid? UserId { get; set; } + public string? UserName { get; set; } + public string? Email { get; set; } + public OrganizationUserStatusType Status { get; set; } + public string? AvatarColor { get; set; } + public string? TwoFactorProviders { get; set; } public bool UsesKeyConnector { get; set; } - public string ResetPasswordKey { get; set; } + public string? ResetPasswordKey { get; set; } public Guid? CollectionId { get; set; } public Guid? GroupId { get; set; } - public string GroupName { get; set; } - public string CollectionName { get; set; } + public string? GroupName { get; set; } + public string? CollectionName { get; set; } public bool? ReadOnly { get; set; } public bool? HidePasswords { get; set; } public bool? Manage { get; set; } diff --git a/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql b/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql index 76955fc184f9..c705b30d9c26 100644 --- a/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql +++ b/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql @@ -10,8 +10,10 @@ BEGIN -- Direct user-collection permissions SELECT OU.[Id] AS [UserGuid], + OU.[UserId], OU.[Name] AS [UserName], OU.[Email], + OU.[Status], OU.[AvatarColor], OU.[TwoFactorProviders], OU.[UsesKeyConnector], @@ -45,8 +47,10 @@ BEGIN -- Group-based collection permissions SELECT OU.[Id] AS [UserGuid], + OU.[UserId], OU.[Name] AS [UserName], OU.[Email], + OU.[Status], OU.[AvatarColor], OU.[TwoFactorProviders], OU.[UsesKeyConnector], @@ -80,8 +84,10 @@ BEGIN -- Users without collection access SELECT OU.[Id] AS [UserGuid], + OU.[UserId], OU.[Name] AS [UserName], OU.[Email], + OU.[Status], OU.[AvatarColor], OU.[TwoFactorProviders], OU.[UsesKeyConnector], diff --git a/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql b/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql index eceefb924a4a..0a46624e3904 100644 --- a/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql +++ b/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql @@ -64,8 +64,10 @@ BEGIN -- Direct user-collection permissions SELECT OU.[Id] AS [UserGuid], + OU.[UserId], OU.[Name] AS [UserName], OU.[Email], + OU.[Status], OU.[AvatarColor], OU.[TwoFactorProviders], OU.[UsesKeyConnector], @@ -99,8 +101,10 @@ BEGIN -- Group-based collection permissions SELECT OU.[Id] AS [UserGuid], + OU.[UserId], OU.[Name] AS [UserName], OU.[Email], + OU.[Status], OU.[AvatarColor], OU.[TwoFactorProviders], OU.[UsesKeyConnector], @@ -134,8 +138,10 @@ BEGIN -- Users without collection access SELECT OU.[Id] AS [UserGuid], + OU.[UserId], OU.[Name] AS [UserName], OU.[Email], + OU.[Status], OU.[AvatarColor], OU.[TwoFactorProviders], OU.[UsesKeyConnector], diff --git a/util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.cs deleted file mode 100644 index 3f6afcb2bdf0..000000000000 --- a/util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.MySqlMigrations.Migrations; - -/// -public partial class _20251119_00_UpdateMemberAccessQuery : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - - } -} diff --git a/util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs b/util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs similarity index 99% rename from util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs rename to util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs index 783653574fc2..295b72c9f2fa 100644 --- a/util/MySqlMigrations/Migrations/20251120054154_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs +++ b/util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs @@ -12,7 +12,7 @@ namespace Bit.MySqlMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20251120054154_2025-11-19_00_UpdateMemberAccessQuery")] + [Migration("20251120221525_2025-11-19_00_UpdateMemberAccessQuery")] partial class _20251119_00_UpdateMemberAccessQuery { /// @@ -60,12 +60,18 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("ResetPasswordKey") .HasColumnType("longtext"); + b.Property("Status") + .HasColumnType("smallint"); + b.Property("TwoFactorProviders") .HasColumnType("longtext"); b.Property("UserGuid") .HasColumnType("char(36)"); + b.Property("UserId") + .HasColumnType("char(36)"); + b.Property("UserName") .HasColumnType("longtext"); diff --git a/util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.cs new file mode 100644 index 000000000000..65ebad13639c --- /dev/null +++ b/util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +/// +public partial class _20251119_00_UpdateMemberAccessQuery : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Status", + table: "OrganizationMemberBaseDetails", + type: "smallint", + nullable: false, + defaultValue: (short)0); + + migrationBuilder.AddColumn( + name: "UserId", + table: "OrganizationMemberBaseDetails", + type: "char(36)", + nullable: true, + collation: "ascii_general_ci"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Status", + table: "OrganizationMemberBaseDetails"); + + migrationBuilder.DropColumn( + name: "UserId", + table: "OrganizationMemberBaseDetails"); + } +} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index b77df53df29e..e76b6fc35f86 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -57,12 +57,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ResetPasswordKey") .HasColumnType("longtext"); + b.Property("Status") + .HasColumnType("smallint"); + b.Property("TwoFactorProviders") .HasColumnType("longtext"); b.Property("UserGuid") .HasColumnType("char(36)"); + b.Property("UserId") + .HasColumnType("char(36)"); + b.Property("UserName") .HasColumnType("longtext"); diff --git a/util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.cs deleted file mode 100644 index c22cd2982f81..000000000000 --- a/util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.PostgresMigrations.Migrations; - -/// -public partial class _20251119_00_UpdateMemberAccessQuery : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - - } -} diff --git a/util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs b/util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs similarity index 99% rename from util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs rename to util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs index 86f309bf6c03..75f56af2280e 100644 --- a/util/PostgresMigrations/Migrations/20251120054145_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs +++ b/util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs @@ -12,7 +12,7 @@ namespace Bit.PostgresMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20251120054145_2025-11-19_00_UpdateMemberAccessQuery")] + [Migration("20251120221531_2025-11-19_00_UpdateMemberAccessQuery")] partial class _20251119_00_UpdateMemberAccessQuery { /// @@ -61,12 +61,18 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("ResetPasswordKey") .HasColumnType("text"); + b.Property("Status") + .HasColumnType("smallint"); + b.Property("TwoFactorProviders") .HasColumnType("text"); b.Property("UserGuid") .HasColumnType("uuid"); + b.Property("UserId") + .HasColumnType("uuid"); + b.Property("UserName") .HasColumnType("text"); diff --git a/util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.cs new file mode 100644 index 000000000000..4c01a8679ef5 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +/// +public partial class _20251119_00_UpdateMemberAccessQuery : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Status", + table: "OrganizationMemberBaseDetails", + type: "smallint", + nullable: false, + defaultValue: (short)0); + + migrationBuilder.AddColumn( + name: "UserId", + table: "OrganizationMemberBaseDetails", + type: "uuid", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Status", + table: "OrganizationMemberBaseDetails"); + + migrationBuilder.DropColumn( + name: "UserId", + table: "OrganizationMemberBaseDetails"); + } +} diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index b1de782d9fa2..b40e18fc6e1e 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -58,12 +58,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ResetPasswordKey") .HasColumnType("text"); + b.Property("Status") + .HasColumnType("smallint"); + b.Property("TwoFactorProviders") .HasColumnType("text"); b.Property("UserGuid") .HasColumnType("uuid"); + b.Property("UserId") + .HasColumnType("uuid"); + b.Property("UserName") .HasColumnType("text"); diff --git a/util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.cs deleted file mode 100644 index 5ca3c085753f..000000000000 --- a/util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.SqliteMigrations.Migrations; - -/// -public partial class _20251119_00_UpdateMemberAccessQuery : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - - } -} diff --git a/util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs b/util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs similarity index 99% rename from util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs rename to util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs index 9791e6f3a5d5..66f54bf0dd23 100644 --- a/util/SqliteMigrations/Migrations/20251120054149_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs +++ b/util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs @@ -11,7 +11,7 @@ namespace Bit.SqliteMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20251120054149_2025-11-19_00_UpdateMemberAccessQuery")] + [Migration("20251120221536_2025-11-19_00_UpdateMemberAccessQuery")] partial class _20251119_00_UpdateMemberAccessQuery { /// @@ -55,12 +55,18 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("ResetPasswordKey") .HasColumnType("TEXT"); + b.Property("Status") + .HasColumnType("INTEGER"); + b.Property("TwoFactorProviders") .HasColumnType("TEXT"); b.Property("UserGuid") .HasColumnType("TEXT"); + b.Property("UserId") + .HasColumnType("TEXT"); + b.Property("UserName") .HasColumnType("TEXT"); diff --git a/util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.cs new file mode 100644 index 000000000000..bc4b6848d38d --- /dev/null +++ b/util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations; + +/// +public partial class _20251119_00_UpdateMemberAccessQuery : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Status", + table: "OrganizationMemberBaseDetails", + type: "INTEGER", + nullable: false, + defaultValue: (short)0); + + migrationBuilder.AddColumn( + name: "UserId", + table: "OrganizationMemberBaseDetails", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Status", + table: "OrganizationMemberBaseDetails"); + + migrationBuilder.DropColumn( + name: "UserId", + table: "OrganizationMemberBaseDetails"); + } +} diff --git a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs index 5219e66c3af4..4985ab8dc173 100644 --- a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -52,12 +52,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ResetPasswordKey") .HasColumnType("TEXT"); + b.Property("Status") + .HasColumnType("INTEGER"); + b.Property("TwoFactorProviders") .HasColumnType("TEXT"); b.Property("UserGuid") .HasColumnType("TEXT"); + b.Property("UserId") + .HasColumnType("TEXT"); + b.Property("UserName") .HasColumnType("TEXT"); From 3aa2d5277f45754d08c0114aad4d58a1b04751d7 Mon Sep 17 00:00:00 2001 From: Graham Walker Date: Fri, 21 Nov 2025 09:14:35 -0600 Subject: [PATCH 7/9] PM-25576 changing UserGuid field to OrganizationUserId for clarity --- .../MemberAccessDetailReportResponseModel.cs | 23 +++-- .../Models/Data/MemberAccessReportDetail.cs | 21 +++-- .../Data/OrganizationMemberBaseDetail.cs | 2 +- .../ReportFeatures/MemberAccessReportQuery.cs | 28 +++--- .../Interfaces/IMemberAccessReportQuery.cs | 2 +- .../ReportFeatures/RiskInsightsReportQuery.cs | 6 +- ...IOrganizationMemberBaseDetailRepository.cs | 2 +- .../DapperServiceCollectionExtensions.cs | 1 - .../OrganizationMemberBaseDetailRepository.cs | 2 +- .../OrganizationMemberBaseDetailRepository.cs | 2 +- ...ityFrameworkServiceCollectionExtensions.cs | 1 - ...berAccessCipherDetailsByOrganizationId.sql | 6 +- .../MemberAccessReportQueryTests.cs | 94 +++++++++---------- .../2025-11-19_00_UpdateMemberAccessQuery.sql | 8 +- ...5_2025-11-19_00_UpdateMemberAccessQuery.cs | 39 -------- ...51248_UpdateMemberAccessQuery.Designer.cs} | 10 +- .../20251121151248_UpdateMemberAccessQuery.cs | 27 ++++++ .../DatabaseContextModelSnapshot.cs | 6 +- ...1_2025-11-19_00_UpdateMemberAccessQuery.cs | 38 -------- ...51253_UpdateMemberAccessQuery.Designer.cs} | 10 +- .../20251121151253_UpdateMemberAccessQuery.cs | 27 ++++++ .../DatabaseContextModelSnapshot.cs | 6 +- ...6_2025-11-19_00_UpdateMemberAccessQuery.cs | 38 -------- ...51244_UpdateMemberAccessQuery.Designer.cs} | 10 +- .../20251121151244_UpdateMemberAccessQuery.cs | 27 ++++++ .../DatabaseContextModelSnapshot.cs | 6 +- 26 files changed, 207 insertions(+), 235 deletions(-) delete mode 100644 util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.cs rename util/MySqlMigrations/Migrations/{20251120221525_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs => 20251121151248_UpdateMemberAccessQuery.Designer.cs} (99%) create mode 100644 util/MySqlMigrations/Migrations/20251121151248_UpdateMemberAccessQuery.cs delete mode 100644 util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.cs rename util/PostgresMigrations/Migrations/{20251120221531_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs => 20251121151253_UpdateMemberAccessQuery.Designer.cs} (99%) create mode 100644 util/PostgresMigrations/Migrations/20251121151253_UpdateMemberAccessQuery.cs delete mode 100644 util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.cs rename util/SqliteMigrations/Migrations/{20251120221536_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs => 20251121151244_UpdateMemberAccessQuery.Designer.cs} (99%) create mode 100644 util/SqliteMigrations/Migrations/20251121151244_UpdateMemberAccessQuery.cs diff --git a/src/Api/Dirt/Models/Response/MemberAccessDetailReportResponseModel.cs b/src/Api/Dirt/Models/Response/MemberAccessDetailReportResponseModel.cs index 2d5a7b155693..f81490008c3a 100644 --- a/src/Api/Dirt/Models/Response/MemberAccessDetailReportResponseModel.cs +++ b/src/Api/Dirt/Models/Response/MemberAccessDetailReportResponseModel.cs @@ -1,29 +1,34 @@ -using Bit.Core.Dirt.Reports.Models.Data; +using Bit.Core.Dirt.Models.Data; +using Bit.Core.Enums; -namespace Bit.Api.Tools.Models.Response; +namespace Bit.Api.Dirt.Models.Response; public class MemberAccessDetailReportResponseModel { - public Guid? UserGuid { get; set; } - public string UserName { get; set; } - public string Email { get; set; } + public Guid? OrganizationUserId { get; set; } + public Guid? UserId { get; set; } + public string? UserName { get; set; } + public string? Email { get; set; } + public OrganizationUserStatusType Status { get; set; } public bool TwoFactorEnabled { get; set; } public bool AccountRecoveryEnabled { get; set; } public bool UsesKeyConnector { get; set; } public Guid? CollectionId { get; set; } public Guid? GroupId { get; set; } - public string GroupName { get; set; } - public string CollectionName { get; set; } + public string? GroupName { get; set; } + public string? CollectionName { get; set; } public bool? ReadOnly { get; set; } public bool? HidePasswords { get; set; } public bool? Manage { get; set; } - public IEnumerable CipherIds { get; set; } + public IEnumerable? CipherIds { get; set; } public MemberAccessDetailReportResponseModel(MemberAccessReportDetail reportDetail) { - UserGuid = reportDetail.UserGuid; + OrganizationUserId = reportDetail.OrganizationUserId; + UserId = reportDetail.UserId; UserName = reportDetail.UserName; Email = reportDetail.Email; + Status = reportDetail.Status; TwoFactorEnabled = reportDetail.TwoFactorEnabled; AccountRecoveryEnabled = reportDetail.AccountRecoveryEnabled; UsesKeyConnector = reportDetail.UsesKeyConnector; diff --git a/src/Core/Dirt/Models/Data/MemberAccessReportDetail.cs b/src/Core/Dirt/Models/Data/MemberAccessReportDetail.cs index 6a7234e3961e..280e57eed77f 100644 --- a/src/Core/Dirt/Models/Data/MemberAccessReportDetail.cs +++ b/src/Core/Dirt/Models/Data/MemberAccessReportDetail.cs @@ -1,23 +1,24 @@ -// FIXME: Update this file to be null safe and then delete the line below -#nullable disable +using Bit.Core.Enums; -namespace Bit.Core.Dirt.Reports.Models.Data; +namespace Bit.Core.Dirt.Models.Data; public class MemberAccessReportDetail { - public Guid? UserGuid { get; set; } - public string UserName { get; set; } - public string Email { get; set; } - public string AvatarColor { get; set; } + public Guid? OrganizationUserId { get; set; } + public Guid? UserId { get; set; } + public string? UserName { get; set; } + public string? Email { get; set; } + public OrganizationUserStatusType Status { get; set; } + public string? AvatarColor { get; set; } public bool TwoFactorEnabled { get; set; } public bool AccountRecoveryEnabled { get; set; } public bool UsesKeyConnector { get; set; } public Guid? CollectionId { get; set; } public Guid? GroupId { get; set; } - public string GroupName { get; set; } - public string CollectionName { get; set; } + public string? GroupName { get; set; } + public string? CollectionName { get; set; } public bool? ReadOnly { get; set; } public bool? HidePasswords { get; set; } public bool? Manage { get; set; } - public IEnumerable CipherIds { get; set; } + public IEnumerable? CipherIds { get; set; } } diff --git a/src/Core/Dirt/Models/Data/OrganizationMemberBaseDetail.cs b/src/Core/Dirt/Models/Data/OrganizationMemberBaseDetail.cs index 69f6d1176886..fbf2112c0596 100644 --- a/src/Core/Dirt/Models/Data/OrganizationMemberBaseDetail.cs +++ b/src/Core/Dirt/Models/Data/OrganizationMemberBaseDetail.cs @@ -4,7 +4,7 @@ namespace Bit.Core.Dirt.Models.Data; public class OrganizationMemberBaseDetail { - public Guid? UserGuid { get; set; } + public Guid? OrganizationUserId { get; set; } public Guid? UserId { get; set; } public string? UserName { get; set; } public string? Email { get; set; } diff --git a/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs b/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs index d2ac10cfbfd2..7bdb7160da89 100644 --- a/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs +++ b/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs @@ -1,11 +1,8 @@ -// FIXME: Update this file to be null safe and then delete the line below -#nullable disable - -using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; -using Bit.Core.Dirt.Reports.Models.Data; +using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; +using Bit.Core.Dirt.Models.Data; using Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces; using Bit.Core.Dirt.Reports.ReportFeatures.Requests; -using Bit.Core.Dirt.Reports.Repositories; +using Bit.Core.Dirt.Repositories; using Bit.Core.Services; using Microsoft.Extensions.Logging; @@ -29,7 +26,7 @@ await organizationMemberBaseDetailRepository.GetOrganizationMemberBaseDetailsByO logger.LogInformation(Constants.BypassFiltersEventId, "Retrieved {BaseDetailsCount} base details for OrganizationId: {OrganizationId}", baseDetails.Count(), request.OrganizationId); - var orgUsers = baseDetails.Select(x => x.UserGuid.GetValueOrDefault()).Distinct(); + var orgUsers = baseDetails.Select(x => x.OrganizationUserId.GetValueOrDefault()).Distinct(); var orgUsersCount = orgUsers.Count(); logger.LogInformation(Constants.BypassFiltersEventId, "Found {UniqueUsersCount} unique users for OrganizationId: {OrganizationId}", orgUsersCount, request.OrganizationId); @@ -45,9 +42,11 @@ await organizationMemberBaseDetailRepository.GetOrganizationMemberBaseDetailsByO var accessDetails = baseDetails .GroupBy(b => new { - b.UserGuid, + b.OrganizationUserId, + b.UserId, b.UserName, b.Email, + b.Status, b.AvatarColor, b.TwoFactorProviders, b.ResetPasswordKey, @@ -62,11 +61,13 @@ await organizationMemberBaseDetailRepository.GetOrganizationMemberBaseDetailsByO }) .Select(g => new MemberAccessReportDetail { - UserGuid = g.Key.UserGuid, + OrganizationUserId = g.Key.OrganizationUserId, + UserId = g.Key.UserId, UserName = g.Key.UserName, Email = g.Key.Email, + Status = g.Key.Status, AvatarColor = g.Key.AvatarColor, - TwoFactorEnabled = orgUsersTwoFactorEnabled.FirstOrDefault(x => x.userId == g.Key.UserGuid).twoFactorIsEnabled, + TwoFactorEnabled = orgUsersTwoFactorEnabled.FirstOrDefault(x => x.userId == g.Key.OrganizationUserId).twoFactorIsEnabled, AccountRecoveryEnabled = !string.IsNullOrWhiteSpace(g.Key.ResetPasswordKey) && orgAbility?.UseResetPassword == true, UsesKeyConnector = g.Key.UsesKeyConnector, GroupId = g.Key.GroupId, @@ -76,10 +77,11 @@ await organizationMemberBaseDetailRepository.GetOrganizationMemberBaseDetailsByO ReadOnly = g.Key.ReadOnly, HidePasswords = g.Key.HidePasswords, Manage = g.Key.Manage, - CipherIds = g.Select(c => c.CipherId).Where(id => id.HasValue).Select(id => id.Value) - }); + CipherIds = g.Select(c => c.CipherId).OfType().ToList() + }) + .ToList(); - var accessDetailsCount = accessDetails.Count(); + var accessDetailsCount = accessDetails.Count; logger.LogInformation(Constants.BypassFiltersEventId, "Completed MemberAccessReport generation for OrganizationId: {OrganizationId}. Generated {AccessDetailsCount} access detail records", request.OrganizationId, accessDetailsCount); diff --git a/src/Core/Dirt/Reports/ReportFeatures/OrganizationReportMembers/Interfaces/IMemberAccessReportQuery.cs b/src/Core/Dirt/Reports/ReportFeatures/OrganizationReportMembers/Interfaces/IMemberAccessReportQuery.cs index 44bb4f33c55b..363459d86da4 100644 --- a/src/Core/Dirt/Reports/ReportFeatures/OrganizationReportMembers/Interfaces/IMemberAccessReportQuery.cs +++ b/src/Core/Dirt/Reports/ReportFeatures/OrganizationReportMembers/Interfaces/IMemberAccessReportQuery.cs @@ -1,4 +1,4 @@ -using Bit.Core.Dirt.Reports.Models.Data; +using Bit.Core.Dirt.Models.Data; using Bit.Core.Dirt.Reports.ReportFeatures.Requests; namespace Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces; diff --git a/src/Core/Dirt/Reports/ReportFeatures/RiskInsightsReportQuery.cs b/src/Core/Dirt/Reports/ReportFeatures/RiskInsightsReportQuery.cs index e686698c51af..c2bf77ce2fb5 100644 --- a/src/Core/Dirt/Reports/ReportFeatures/RiskInsightsReportQuery.cs +++ b/src/Core/Dirt/Reports/ReportFeatures/RiskInsightsReportQuery.cs @@ -1,7 +1,7 @@ using Bit.Core.Dirt.Reports.Models.Data; using Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces; using Bit.Core.Dirt.Reports.ReportFeatures.Requests; -using Bit.Core.Dirt.Reports.Repositories; +using Bit.Core.Dirt.Repositories; namespace Bit.Core.Dirt.Reports.ReportFeatures; @@ -22,10 +22,10 @@ await _organizationMemberBaseDetailRepository.GetOrganizationMemberBaseDetailsBy request.OrganizationId); var insightsDetails = baseDetails - .GroupBy(b => new { b.UserGuid, b.UserName, b.Email, b.UsesKeyConnector }) + .GroupBy(b => new { b.OrganizationUserId, b.UserName, b.Email, b.UsesKeyConnector }) .Select(g => new RiskInsightsReportDetail { - UserGuid = g.Key.UserGuid, + UserGuid = g.Key.OrganizationUserId, UserName = g.Key.UserName, Email = g.Key.Email, UsesKeyConnector = g.Key.UsesKeyConnector, diff --git a/src/Core/Dirt/Repositories/IOrganizationMemberBaseDetailRepository.cs b/src/Core/Dirt/Repositories/IOrganizationMemberBaseDetailRepository.cs index 930e491c4b50..9b0e998b81df 100644 --- a/src/Core/Dirt/Repositories/IOrganizationMemberBaseDetailRepository.cs +++ b/src/Core/Dirt/Repositories/IOrganizationMemberBaseDetailRepository.cs @@ -1,6 +1,6 @@ using Bit.Core.Dirt.Models.Data; -namespace Bit.Core.Dirt.Reports.Repositories; +namespace Bit.Core.Dirt.Repositories; public interface IOrganizationMemberBaseDetailRepository { diff --git a/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs b/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs index 445ff771091c..64b159707383 100644 --- a/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs +++ b/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs @@ -2,7 +2,6 @@ using Bit.Core.Auth.Repositories; using Bit.Core.Billing.Organizations.Repositories; using Bit.Core.Billing.Providers.Repositories; -using Bit.Core.Dirt.Reports.Repositories; using Bit.Core.Dirt.Repositories; using Bit.Core.KeyManagement.Repositories; using Bit.Core.NotificationCenter.Repositories; diff --git a/src/Infrastructure.Dapper/Dirt/OrganizationMemberBaseDetailRepository.cs b/src/Infrastructure.Dapper/Dirt/OrganizationMemberBaseDetailRepository.cs index bb5e97c508a5..6381f75aec13 100644 --- a/src/Infrastructure.Dapper/Dirt/OrganizationMemberBaseDetailRepository.cs +++ b/src/Infrastructure.Dapper/Dirt/OrganizationMemberBaseDetailRepository.cs @@ -1,6 +1,6 @@ using System.Data; using Bit.Core.Dirt.Models.Data; -using Bit.Core.Dirt.Reports.Repositories; +using Bit.Core.Dirt.Repositories; using Bit.Core.Settings; using Bit.Infrastructure.Dapper.Repositories; using Dapper; diff --git a/src/Infrastructure.EntityFramework/Dirt/Repositories/OrganizationMemberBaseDetailRepository.cs b/src/Infrastructure.EntityFramework/Dirt/Repositories/OrganizationMemberBaseDetailRepository.cs index 8b8a14c370a2..c6ded2f097e0 100644 --- a/src/Infrastructure.EntityFramework/Dirt/Repositories/OrganizationMemberBaseDetailRepository.cs +++ b/src/Infrastructure.EntityFramework/Dirt/Repositories/OrganizationMemberBaseDetailRepository.cs @@ -1,6 +1,6 @@ using AutoMapper; using Bit.Core.Dirt.Models.Data; -using Bit.Core.Dirt.Reports.Repositories; +using Bit.Core.Dirt.Repositories; using Bit.Infrastructure.EntityFramework.Repositories; using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; diff --git a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs index 226ff2e2cbbc..313daea547e9 100644 --- a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs +++ b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs @@ -2,7 +2,6 @@ using Bit.Core.Auth.Repositories; using Bit.Core.Billing.Organizations.Repositories; using Bit.Core.Billing.Providers.Repositories; -using Bit.Core.Dirt.Reports.Repositories; using Bit.Core.Dirt.Repositories; using Bit.Core.Enums; using Bit.Core.KeyManagement.Repositories; diff --git a/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql b/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql index c705b30d9c26..3d157362c75a 100644 --- a/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql +++ b/src/Sql/dbo/Dirt/Stored Procedures/MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId.sql @@ -9,7 +9,7 @@ BEGIN -- Direct user-collection permissions SELECT - OU.[Id] AS [UserGuid], + OU.[Id] AS [OrganizationUserId], OU.[UserId], OU.[Name] AS [UserName], OU.[Email], @@ -46,7 +46,7 @@ BEGIN -- Group-based collection permissions SELECT - OU.[Id] AS [UserGuid], + OU.[Id] AS [OrganizationUserId], OU.[UserId], OU.[Name] AS [UserName], OU.[Email], @@ -83,7 +83,7 @@ BEGIN -- Users without collection access SELECT - OU.[Id] AS [UserGuid], + OU.[Id] AS [OrganizationUserId], OU.[UserId], OU.[Name] AS [UserName], OU.[Email], diff --git a/test/Core.Test/Dirt/ReportFeatures/MemberAccessReportQueryTests.cs b/test/Core.Test/Dirt/ReportFeatures/MemberAccessReportQueryTests.cs index 621b645324a3..faa5140c87b1 100644 --- a/test/Core.Test/Dirt/ReportFeatures/MemberAccessReportQueryTests.cs +++ b/test/Core.Test/Dirt/ReportFeatures/MemberAccessReportQueryTests.cs @@ -3,7 +3,7 @@ using Bit.Core.Dirt.Models.Data; using Bit.Core.Dirt.Reports.ReportFeatures; using Bit.Core.Dirt.Reports.ReportFeatures.Requests; -using Bit.Core.Dirt.Reports.Repositories; +using Bit.Core.Dirt.Repositories; using Bit.Core.Models.Data.Organizations; using Bit.Core.Services; using Bit.Test.Common.AutoFixture; @@ -31,7 +31,7 @@ public async Task GetMemberAccessReportsAsync_MapsAvatarColor_FromDatabaseToMode { new OrganizationMemberBaseDetail { - UserGuid = Guid.NewGuid(), + OrganizationUserId = Guid.NewGuid(), UserName = "Test User", Email = "test@example.com", AvatarColor = expectedAvatarColor, @@ -57,7 +57,7 @@ public async Task GetMemberAccessReportsAsync_MapsAvatarColor_FromDatabaseToMode .TwoFactorIsEnabledAsync(Arg.Any>()) .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { - (baseDetails[0].UserGuid.Value, false) + (baseDetails[0].OrganizationUserId.Value, false) }); sutProvider.GetDependency() @@ -91,7 +91,7 @@ public async Task GetMemberAccessReportsAsync_ExcludesUsersWithGroupBasedAccess_ // User with group-based collection access new OrganizationMemberBaseDetail { - UserGuid = userWithGroupAccess, + OrganizationUserId = userWithGroupAccess, UserName = "Group User", Email = "group@example.com", AvatarColor = "#FF0000", @@ -110,7 +110,7 @@ public async Task GetMemberAccessReportsAsync_ExcludesUsersWithGroupBasedAccess_ // User with direct collection access new OrganizationMemberBaseDetail { - UserGuid = userWithDirectAccess, + OrganizationUserId = userWithDirectAccess, UserName = "Direct User", Email = "direct@example.com", AvatarColor = "#00FF00", @@ -132,10 +132,10 @@ public async Task GetMemberAccessReportsAsync_ExcludesUsersWithGroupBasedAccess_ .GetOrganizationMemberBaseDetailsByOrganizationId(organizationId) .Returns(baseDetails); - var userGuids = new[] { userWithGroupAccess, userWithDirectAccess }; + var organizationUserIds = new[] { userWithGroupAccess, userWithDirectAccess }; sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Any>()) - .Returns(userGuids.Select(id => (id, false)).ToList()); + .Returns(organizationUserIds.Select(id => (id, false)).ToList()); sutProvider.GetDependency() .GetOrganizationAbilityAsync(organizationId) @@ -149,11 +149,11 @@ public async Task GetMemberAccessReportsAsync_ExcludesUsersWithGroupBasedAccess_ Assert.Equal(2, resultList.Count); // Verify both users with access are in results - Assert.Contains(resultList, r => r.UserGuid == userWithGroupAccess); - Assert.Contains(resultList, r => r.UserGuid == userWithDirectAccess); + Assert.Contains(resultList, r => r.OrganizationUserId == userWithGroupAccess); + Assert.Contains(resultList, r => r.OrganizationUserId == userWithDirectAccess); // Verify the user with group access has group information - var groupUser = resultList.First(r => r.UserGuid == userWithGroupAccess); + var groupUser = resultList.First(r => r.OrganizationUserId == userWithGroupAccess); Assert.NotNull(groupUser.GroupId); Assert.Equal("Test Group", groupUser.GroupName); } @@ -167,7 +167,7 @@ public async Task GetMemberAccessReportsAsync_HandlesNullCipherIds_CorrectlyFilt var fixture = new Fixture(); var organizationId = fixture.Create(); var request = new MemberAccessReportRequest { OrganizationId = organizationId }; - var userGuid = Guid.NewGuid(); + var organizationUserId = Guid.NewGuid(); var collectionId = Guid.NewGuid(); var cipherId1 = Guid.NewGuid(); var cipherId2 = Guid.NewGuid(); @@ -177,7 +177,7 @@ public async Task GetMemberAccessReportsAsync_HandlesNullCipherIds_CorrectlyFilt // Same user, same collection, multiple ciphers (some null) new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -195,7 +195,7 @@ public async Task GetMemberAccessReportsAsync_HandlesNullCipherIds_CorrectlyFilt }, new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -213,7 +213,7 @@ public async Task GetMemberAccessReportsAsync_HandlesNullCipherIds_CorrectlyFilt }, new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -237,7 +237,7 @@ public async Task GetMemberAccessReportsAsync_HandlesNullCipherIds_CorrectlyFilt sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Any>()) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, false) }); + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUserId, false) }); sutProvider.GetDependency() .GetOrganizationAbilityAsync(organizationId) @@ -268,7 +268,7 @@ public async Task GetMemberAccessReportsAsync_GroupsByUserAndCollection_WithCorr var fixture = new Fixture(); var organizationId = fixture.Create(); var request = new MemberAccessReportRequest { OrganizationId = organizationId }; - var userGuid = Guid.NewGuid(); + var organizationUserId = Guid.NewGuid(); var collectionId = Guid.NewGuid(); var baseDetails = new List @@ -276,7 +276,7 @@ public async Task GetMemberAccessReportsAsync_GroupsByUserAndCollection_WithCorr // Same user, same collection, different ciphers new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -294,7 +294,7 @@ public async Task GetMemberAccessReportsAsync_GroupsByUserAndCollection_WithCorr }, new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -318,7 +318,7 @@ public async Task GetMemberAccessReportsAsync_GroupsByUserAndCollection_WithCorr sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Any>()) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, true) }); + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUserId, true) }); sutProvider.GetDependency() .GetOrganizationAbilityAsync(organizationId) @@ -334,7 +334,7 @@ public async Task GetMemberAccessReportsAsync_GroupsByUserAndCollection_WithCorr Assert.Single(resultList); var record = resultList[0]; - Assert.Equal(userGuid, record.UserGuid); + Assert.Equal(organizationUserId, record.OrganizationUserId); Assert.Equal(collectionId, record.CollectionId); Assert.True(record.ReadOnly); Assert.True(record.HidePasswords); @@ -361,7 +361,7 @@ public async Task GetMemberAccessReportsAsync_MapsAccountRecoveryEnabled_WhenRes { new OrganizationMemberBaseDetail { - UserGuid = userWithRecovery, + OrganizationUserId = userWithRecovery, UserName = "User With Recovery", Email = "recovery@example.com", AvatarColor = "#FF0000", @@ -379,7 +379,7 @@ public async Task GetMemberAccessReportsAsync_MapsAccountRecoveryEnabled_WhenRes }, new OrganizationMemberBaseDetail { - UserGuid = userWithoutRecovery, + OrganizationUserId = userWithoutRecovery, UserName = "User Without Recovery", Email = "norecovery@example.com", AvatarColor = "#00FF00", @@ -420,8 +420,8 @@ public async Task GetMemberAccessReportsAsync_MapsAccountRecoveryEnabled_WhenRes var resultList = result.ToList(); Assert.Equal(2, resultList.Count); - var withRecovery = resultList.First(r => r.UserGuid == userWithRecovery); - var withoutRecovery = resultList.First(r => r.UserGuid == userWithoutRecovery); + var withRecovery = resultList.First(r => r.OrganizationUserId == userWithRecovery); + var withoutRecovery = resultList.First(r => r.OrganizationUserId == userWithoutRecovery); Assert.True(withRecovery.AccountRecoveryEnabled); Assert.False(withoutRecovery.AccountRecoveryEnabled); @@ -442,7 +442,7 @@ public async Task GetMemberAccessReportsAsync_MapsUsesKeyConnector_Correctly( { new OrganizationMemberBaseDetail { - UserGuid = userWithKeyConnector, + OrganizationUserId = userWithKeyConnector, UserName = "Key Connector User", Email = "keyconnector@example.com", AvatarColor = "#0000FF", @@ -493,7 +493,7 @@ public async Task GetMemberAccessReportsAsync_SeparatesDirectAndGroupAccess_ForS var fixture = new Fixture(); var organizationId = fixture.Create(); var request = new MemberAccessReportRequest { OrganizationId = organizationId }; - var userGuid = Guid.NewGuid(); + var organizationUserId = Guid.NewGuid(); var directCollectionId = Guid.NewGuid(); var groupCollectionId = Guid.NewGuid(); @@ -502,7 +502,7 @@ public async Task GetMemberAccessReportsAsync_SeparatesDirectAndGroupAccess_ForS // Direct access new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -521,7 +521,7 @@ public async Task GetMemberAccessReportsAsync_SeparatesDirectAndGroupAccess_ForS // Group access new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -545,7 +545,7 @@ public async Task GetMemberAccessReportsAsync_SeparatesDirectAndGroupAccess_ForS sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Any>()) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, false) }); + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUserId, false) }); sutProvider.GetDependency() .GetOrganizationAbilityAsync(organizationId) @@ -612,13 +612,13 @@ public async Task GetMemberAccessReportsAsync_WhenOrgAbilityIsNull_HandlesGracef var fixture = new Fixture(); var organizationId = fixture.Create(); var request = new MemberAccessReportRequest { OrganizationId = organizationId }; - var userGuid = Guid.NewGuid(); + var organizationUserId = Guid.NewGuid(); var baseDetails = new List { new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -642,7 +642,7 @@ public async Task GetMemberAccessReportsAsync_WhenOrgAbilityIsNull_HandlesGracef sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Any>()) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, false) }); + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUserId, false) }); sutProvider.GetDependency() .GetOrganizationAbilityAsync(organizationId) @@ -667,7 +667,7 @@ public async Task GetMemberAccessReportsAsync_WithCollectionWithoutCiphers_Retur var fixture = new Fixture(); var organizationId = fixture.Create(); var request = new MemberAccessReportRequest { OrganizationId = organizationId }; - var userGuid = Guid.NewGuid(); + var organizationUserId = Guid.NewGuid(); var collectionId = Guid.NewGuid(); var baseDetails = new List @@ -675,7 +675,7 @@ public async Task GetMemberAccessReportsAsync_WithCollectionWithoutCiphers_Retur // Collection access with null cipher (collection has no ciphers) new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -699,7 +699,7 @@ public async Task GetMemberAccessReportsAsync_WithCollectionWithoutCiphers_Retur sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Any>()) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, false) }); + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUserId, false) }); sutProvider.GetDependency() .GetOrganizationAbilityAsync(organizationId) @@ -723,7 +723,7 @@ public async Task GetMemberAccessReportsAsync_WithUserInMultipleGroupsSameCollec var fixture = new Fixture(); var organizationId = fixture.Create(); var request = new MemberAccessReportRequest { OrganizationId = organizationId }; - var userGuid = Guid.NewGuid(); + var organizationUserId = Guid.NewGuid(); var collectionId = Guid.NewGuid(); var group1Id = Guid.NewGuid(); var group2Id = Guid.NewGuid(); @@ -735,7 +735,7 @@ public async Task GetMemberAccessReportsAsync_WithUserInMultipleGroupsSameCollec // Same user, same collection, group 1 new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -754,7 +754,7 @@ public async Task GetMemberAccessReportsAsync_WithUserInMultipleGroupsSameCollec // Same user, same collection, group 2 new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -778,7 +778,7 @@ public async Task GetMemberAccessReportsAsync_WithUserInMultipleGroupsSameCollec sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Any>()) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, false) }); + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUserId, false) }); sutProvider.GetDependency() .GetOrganizationAbilityAsync(organizationId) @@ -811,7 +811,7 @@ public async Task GetMemberAccessReportsAsync_WithMixedPermissions_GroupsCorrect var fixture = new Fixture(); var organizationId = fixture.Create(); var request = new MemberAccessReportRequest { OrganizationId = organizationId }; - var userGuid = Guid.NewGuid(); + var organizationUserId = Guid.NewGuid(); var collection1Id = Guid.NewGuid(); var collection2Id = Guid.NewGuid(); @@ -820,7 +820,7 @@ public async Task GetMemberAccessReportsAsync_WithMixedPermissions_GroupsCorrect // Collection 1 - ReadOnly + HidePasswords new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -839,7 +839,7 @@ public async Task GetMemberAccessReportsAsync_WithMixedPermissions_GroupsCorrect // Collection 2 - Manage access new OrganizationMemberBaseDetail { - UserGuid = userGuid, + OrganizationUserId = organizationUserId, UserName = "Test User", Email = "test@example.com", AvatarColor = "#FF5733", @@ -863,7 +863,7 @@ public async Task GetMemberAccessReportsAsync_WithMixedPermissions_GroupsCorrect sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Any>()) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (userGuid, false) }); + .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUserId, false) }); sutProvider.GetDependency() .GetOrganizationAbilityAsync(organizationId) @@ -904,7 +904,7 @@ public async Task GetMemberAccessReportsAsync_WhenTwoFactorQueryReturnsPartialRe { new OrganizationMemberBaseDetail { - UserGuid = user1, + OrganizationUserId = user1, UserName = "User 1", Email = "user1@example.com", AvatarColor = "#FF0000", @@ -922,7 +922,7 @@ public async Task GetMemberAccessReportsAsync_WhenTwoFactorQueryReturnsPartialRe }, new OrganizationMemberBaseDetail { - UserGuid = user2, + OrganizationUserId = user2, UserName = "User 2", Email = "user2@example.com", AvatarColor = "#00FF00", @@ -964,8 +964,8 @@ public async Task GetMemberAccessReportsAsync_WhenTwoFactorQueryReturnsPartialRe var resultList = result.ToList(); Assert.Equal(2, resultList.Count); - var user1Result = resultList.First(r => r.UserGuid == user1); - var user2Result = resultList.First(r => r.UserGuid == user2); + var user1Result = resultList.First(r => r.OrganizationUserId == user1); + var user2Result = resultList.First(r => r.OrganizationUserId == user2); Assert.True(user1Result.TwoFactorEnabled); // User2 should default to false when not in TwoFactorQuery results diff --git a/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql b/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql index 0a46624e3904..4a39a192875d 100644 --- a/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql +++ b/util/Migrator/DbScripts/2025-11-19_00_UpdateMemberAccessQuery.sql @@ -63,7 +63,7 @@ BEGIN -- Direct user-collection permissions SELECT - OU.[Id] AS [UserGuid], + OU.[Id] AS [OrganizationUserId], OU.[UserId], OU.[Name] AS [UserName], OU.[Email], @@ -100,7 +100,7 @@ BEGIN -- Group-based collection permissions SELECT - OU.[Id] AS [UserGuid], + OU.[Id] AS [OrganizationUserId], OU.[UserId], OU.[Name] AS [UserName], OU.[Email], @@ -137,7 +137,7 @@ BEGIN -- Users without collection access SELECT - OU.[Id] AS [UserGuid], + OU.[Id] AS [OrganizationUserId], OU.[UserId], OU.[Name] AS [UserName], OU.[Email], @@ -175,4 +175,4 @@ BEGIN AND CGP.[OrganizationId] = @OrganizationId ) END -GO \ No newline at end of file +GO diff --git a/util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.cs deleted file mode 100644 index 65ebad13639c..000000000000 --- a/util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.MySqlMigrations.Migrations; - -/// -public partial class _20251119_00_UpdateMemberAccessQuery : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "Status", - table: "OrganizationMemberBaseDetails", - type: "smallint", - nullable: false, - defaultValue: (short)0); - - migrationBuilder.AddColumn( - name: "UserId", - table: "OrganizationMemberBaseDetails", - type: "char(36)", - nullable: true, - collation: "ascii_general_ci"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Status", - table: "OrganizationMemberBaseDetails"); - - migrationBuilder.DropColumn( - name: "UserId", - table: "OrganizationMemberBaseDetails"); - } -} diff --git a/util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs b/util/MySqlMigrations/Migrations/20251121151248_UpdateMemberAccessQuery.Designer.cs similarity index 99% rename from util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs rename to util/MySqlMigrations/Migrations/20251121151248_UpdateMemberAccessQuery.Designer.cs index 295b72c9f2fa..de4ac9dcb21e 100644 --- a/util/MySqlMigrations/Migrations/20251120221525_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs +++ b/util/MySqlMigrations/Migrations/20251121151248_UpdateMemberAccessQuery.Designer.cs @@ -12,8 +12,8 @@ namespace Bit.MySqlMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20251120221525_2025-11-19_00_UpdateMemberAccessQuery")] - partial class _20251119_00_UpdateMemberAccessQuery + [Migration("20251121151248_UpdateMemberAccessQuery")] + partial class UpdateMemberAccessQuery { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -54,6 +54,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Manage") .HasColumnType("tinyint(1)"); + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + b.Property("ReadOnly") .HasColumnType("tinyint(1)"); @@ -66,9 +69,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("TwoFactorProviders") .HasColumnType("longtext"); - b.Property("UserGuid") - .HasColumnType("char(36)"); - b.Property("UserId") .HasColumnType("char(36)"); diff --git a/util/MySqlMigrations/Migrations/20251121151248_UpdateMemberAccessQuery.cs b/util/MySqlMigrations/Migrations/20251121151248_UpdateMemberAccessQuery.cs new file mode 100644 index 000000000000..f6cf8d23f702 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20251121151248_UpdateMemberAccessQuery.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +/// +public partial class UpdateMemberAccessQuery : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "UserGuid", + table: "OrganizationMemberBaseDetails", + newName: "OrganizationUserId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "OrganizationUserId", + table: "OrganizationMemberBaseDetails", + newName: "UserGuid"); + } +} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index e76b6fc35f86..b08f1057874d 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -51,6 +51,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Manage") .HasColumnType("tinyint(1)"); + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + b.Property("ReadOnly") .HasColumnType("tinyint(1)"); @@ -63,9 +66,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("TwoFactorProviders") .HasColumnType("longtext"); - b.Property("UserGuid") - .HasColumnType("char(36)"); - b.Property("UserId") .HasColumnType("char(36)"); diff --git a/util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.cs deleted file mode 100644 index 4c01a8679ef5..000000000000 --- a/util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.PostgresMigrations.Migrations; - -/// -public partial class _20251119_00_UpdateMemberAccessQuery : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "Status", - table: "OrganizationMemberBaseDetails", - type: "smallint", - nullable: false, - defaultValue: (short)0); - - migrationBuilder.AddColumn( - name: "UserId", - table: "OrganizationMemberBaseDetails", - type: "uuid", - nullable: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Status", - table: "OrganizationMemberBaseDetails"); - - migrationBuilder.DropColumn( - name: "UserId", - table: "OrganizationMemberBaseDetails"); - } -} diff --git a/util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs b/util/PostgresMigrations/Migrations/20251121151253_UpdateMemberAccessQuery.Designer.cs similarity index 99% rename from util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs rename to util/PostgresMigrations/Migrations/20251121151253_UpdateMemberAccessQuery.Designer.cs index 75f56af2280e..94890df77d04 100644 --- a/util/PostgresMigrations/Migrations/20251120221531_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs +++ b/util/PostgresMigrations/Migrations/20251121151253_UpdateMemberAccessQuery.Designer.cs @@ -12,8 +12,8 @@ namespace Bit.PostgresMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20251120221531_2025-11-19_00_UpdateMemberAccessQuery")] - partial class _20251119_00_UpdateMemberAccessQuery + [Migration("20251121151253_UpdateMemberAccessQuery")] + partial class UpdateMemberAccessQuery { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -55,6 +55,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Manage") .HasColumnType("boolean"); + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + b.Property("ReadOnly") .HasColumnType("boolean"); @@ -67,9 +70,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("TwoFactorProviders") .HasColumnType("text"); - b.Property("UserGuid") - .HasColumnType("uuid"); - b.Property("UserId") .HasColumnType("uuid"); diff --git a/util/PostgresMigrations/Migrations/20251121151253_UpdateMemberAccessQuery.cs b/util/PostgresMigrations/Migrations/20251121151253_UpdateMemberAccessQuery.cs new file mode 100644 index 000000000000..fd0748eabeac --- /dev/null +++ b/util/PostgresMigrations/Migrations/20251121151253_UpdateMemberAccessQuery.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +/// +public partial class UpdateMemberAccessQuery : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "UserGuid", + table: "OrganizationMemberBaseDetails", + newName: "OrganizationUserId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "OrganizationUserId", + table: "OrganizationMemberBaseDetails", + newName: "UserGuid"); + } +} diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index b40e18fc6e1e..420c56e5718d 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -52,6 +52,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Manage") .HasColumnType("boolean"); + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + b.Property("ReadOnly") .HasColumnType("boolean"); @@ -64,9 +67,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("TwoFactorProviders") .HasColumnType("text"); - b.Property("UserGuid") - .HasColumnType("uuid"); - b.Property("UserId") .HasColumnType("uuid"); diff --git a/util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.cs b/util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.cs deleted file mode 100644 index bc4b6848d38d..000000000000 --- a/util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.SqliteMigrations.Migrations; - -/// -public partial class _20251119_00_UpdateMemberAccessQuery : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "Status", - table: "OrganizationMemberBaseDetails", - type: "INTEGER", - nullable: false, - defaultValue: (short)0); - - migrationBuilder.AddColumn( - name: "UserId", - table: "OrganizationMemberBaseDetails", - type: "TEXT", - nullable: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Status", - table: "OrganizationMemberBaseDetails"); - - migrationBuilder.DropColumn( - name: "UserId", - table: "OrganizationMemberBaseDetails"); - } -} diff --git a/util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs b/util/SqliteMigrations/Migrations/20251121151244_UpdateMemberAccessQuery.Designer.cs similarity index 99% rename from util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs rename to util/SqliteMigrations/Migrations/20251121151244_UpdateMemberAccessQuery.Designer.cs index 66f54bf0dd23..5b53da5ae5f3 100644 --- a/util/SqliteMigrations/Migrations/20251120221536_2025-11-19_00_UpdateMemberAccessQuery.Designer.cs +++ b/util/SqliteMigrations/Migrations/20251121151244_UpdateMemberAccessQuery.Designer.cs @@ -11,8 +11,8 @@ namespace Bit.SqliteMigrations.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20251120221536_2025-11-19_00_UpdateMemberAccessQuery")] - partial class _20251119_00_UpdateMemberAccessQuery + [Migration("20251121151244_UpdateMemberAccessQuery")] + partial class UpdateMemberAccessQuery { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -49,6 +49,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Manage") .HasColumnType("INTEGER"); + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + b.Property("ReadOnly") .HasColumnType("INTEGER"); @@ -61,9 +64,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("TwoFactorProviders") .HasColumnType("TEXT"); - b.Property("UserGuid") - .HasColumnType("TEXT"); - b.Property("UserId") .HasColumnType("TEXT"); diff --git a/util/SqliteMigrations/Migrations/20251121151244_UpdateMemberAccessQuery.cs b/util/SqliteMigrations/Migrations/20251121151244_UpdateMemberAccessQuery.cs new file mode 100644 index 000000000000..61b1411a887c --- /dev/null +++ b/util/SqliteMigrations/Migrations/20251121151244_UpdateMemberAccessQuery.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations; + +/// +public partial class UpdateMemberAccessQuery : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "UserGuid", + table: "OrganizationMemberBaseDetails", + newName: "OrganizationUserId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "OrganizationUserId", + table: "OrganizationMemberBaseDetails", + newName: "UserGuid"); + } +} diff --git a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs index 4985ab8dc173..0711ff66d037 100644 --- a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -46,6 +46,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Manage") .HasColumnType("INTEGER"); + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + b.Property("ReadOnly") .HasColumnType("INTEGER"); @@ -58,9 +61,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("TwoFactorProviders") .HasColumnType("TEXT"); - b.Property("UserGuid") - .HasColumnType("TEXT"); - b.Property("UserId") .HasColumnType("TEXT"); From 81dc1abf954a7ab559ebf1d6ac234d6fe129db9d Mon Sep 17 00:00:00 2001 From: Graham Walker Date: Fri, 21 Nov 2025 11:24:34 -0600 Subject: [PATCH 8/9] PM-25576 fixing linting issue --- src/Api/Dirt/Controllers/ReportsController.cs | 1 - .../Models/Response/MemberAccessDetailReportResponseModel.cs | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Api/Dirt/Controllers/ReportsController.cs b/src/Api/Dirt/Controllers/ReportsController.cs index 3e9f2f0e0d6a..aa5fba02e62e 100644 --- a/src/Api/Dirt/Controllers/ReportsController.cs +++ b/src/Api/Dirt/Controllers/ReportsController.cs @@ -1,6 +1,5 @@ using Bit.Api.Dirt.Models; using Bit.Api.Dirt.Models.Response; -using Bit.Api.Tools.Models.Response; using Bit.Core; using Bit.Core.Context; using Bit.Core.Dirt.Entities; diff --git a/src/Api/Dirt/Models/Response/MemberAccessDetailReportResponseModel.cs b/src/Api/Dirt/Models/Response/MemberAccessDetailReportResponseModel.cs index f81490008c3a..b39d0505b329 100644 --- a/src/Api/Dirt/Models/Response/MemberAccessDetailReportResponseModel.cs +++ b/src/Api/Dirt/Models/Response/MemberAccessDetailReportResponseModel.cs @@ -10,6 +10,7 @@ public class MemberAccessDetailReportResponseModel public string? UserName { get; set; } public string? Email { get; set; } public OrganizationUserStatusType Status { get; set; } + public string? AvatarColor { get; set; } public bool TwoFactorEnabled { get; set; } public bool AccountRecoveryEnabled { get; set; } public bool UsesKeyConnector { get; set; } @@ -29,6 +30,7 @@ public MemberAccessDetailReportResponseModel(MemberAccessReportDetail reportDeta UserName = reportDetail.UserName; Email = reportDetail.Email; Status = reportDetail.Status; + AvatarColor = reportDetail.AvatarColor; TwoFactorEnabled = reportDetail.TwoFactorEnabled; AccountRecoveryEnabled = reportDetail.AccountRecoveryEnabled; UsesKeyConnector = reportDetail.UsesKeyConnector; From 7432a62144aedb210a9e2fbd0141a15ce4ba0b7c Mon Sep 17 00:00:00 2001 From: Graham Walker Date: Fri, 21 Nov 2025 12:11:44 -0600 Subject: [PATCH 9/9] PM-25576 fixing formatting issues --- src/Sql/dbo/Views/CollectionCipherDetailsView.sql | 4 ++-- src/Sql/dbo/Views/CollectionGroupPermissionsView.sql | 6 +++--- src/Sql/dbo/Views/CollectionUserPermissionsView.sql | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Sql/dbo/Views/CollectionCipherDetailsView.sql b/src/Sql/dbo/Views/CollectionCipherDetailsView.sql index d4b3c310a3cd..54c9bf6b0a8f 100644 --- a/src/Sql/dbo/Views/CollectionCipherDetailsView.sql +++ b/src/Sql/dbo/Views/CollectionCipherDetailsView.sql @@ -8,8 +8,8 @@ SELECT Ci.[DeletedDate] FROM [dbo].[CollectionCipher] CC - INNER JOIN +INNER JOIN [dbo].[Collection] C ON C.[Id] = CC.[CollectionId] - INNER JOIN +INNER JOIN [dbo].[Cipher] Ci ON Ci.[Id] = CC.[CipherId] GO diff --git a/src/Sql/dbo/Views/CollectionGroupPermissionsView.sql b/src/Sql/dbo/Views/CollectionGroupPermissionsView.sql index 123295675c18..4fd3af0c517b 100644 --- a/src/Sql/dbo/Views/CollectionGroupPermissionsView.sql +++ b/src/Sql/dbo/Views/CollectionGroupPermissionsView.sql @@ -12,10 +12,10 @@ SELECT CG.[Manage] FROM [dbo].[GroupUser] GU - INNER JOIN +INNER JOIN [dbo].[Group] G ON G.[Id] = GU.[GroupId] - INNER JOIN +INNER JOIN [dbo].[CollectionGroup] CG ON CG.[GroupId] = G.[Id] - INNER JOIN +INNER JOIN [dbo].[Collection] C ON C.[Id] = CG.[CollectionId] GO diff --git a/src/Sql/dbo/Views/CollectionUserPermissionsView.sql b/src/Sql/dbo/Views/CollectionUserPermissionsView.sql index 2eec4e8f03f2..7741cc9663a4 100644 --- a/src/Sql/dbo/Views/CollectionUserPermissionsView.sql +++ b/src/Sql/dbo/Views/CollectionUserPermissionsView.sql @@ -10,6 +10,6 @@ SELECT CU.[Manage] FROM [dbo].[CollectionUser] CU - INNER JOIN +INNER JOIN [dbo].[Collection] C ON C.[Id] = CU.[CollectionId] GO