diff --git a/src/Core/Dirt/Models/Data/MemberAccessReportDetail.cs b/src/Core/Dirt/Models/Data/MemberAccessReportDetail.cs
index 7b54822a1e..6a7234e396 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 a68e920e66..137747a319 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 83d074454d..599895dbbb 100644
--- a/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs
+++ b/src/Core/Dirt/Reports/ReportFeatures/MemberAccessReportQuery.cs
@@ -48,6 +48,7 @@ public class MemberAccessReportQuery(
b.UserGuid,
b.UserName,
b.Email,
+ b.AvatarColor,
b.TwoFactorProviders,
b.ResetPasswordKey,
b.UsesKeyConnector,
@@ -64,6 +65,7 @@ public class MemberAccessReportQuery(
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 @@ public class MemberAccessReportQuery(
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 e2a161aa9c..930e491c4b 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 458e72f996..bb5e97c508 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 123379da90..8b8a14c370 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 b748a26db2..b6367a44ac 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 0000000000..0df67437c4
--- /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 1aaf667f6a..0000000000
--- 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 0000000000..d1c96a0d9e
--- /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 0000000000..3a62b76a3f
--- /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 0000000000..cf5fb10afd
--- /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 0000000000..b5f58eeb23
--- /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 0000000000..0a835371ae
--- /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