diff --git a/src/Api/AdminConsole/Models/Response/BaseProfileOrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/BaseProfileOrganizationResponseModel.cs
new file mode 100644
index 0000000000..c172c45e94
--- /dev/null
+++ b/src/Api/AdminConsole/Models/Response/BaseProfileOrganizationResponseModel.cs
@@ -0,0 +1,127 @@
+using System.Text.Json.Serialization;
+using Bit.Core.AdminConsole.Enums.Provider;
+using Bit.Core.AdminConsole.Models.Data;
+using Bit.Core.Auth.Enums;
+using Bit.Core.Auth.Models.Data;
+using Bit.Core.Billing.Enums;
+using Bit.Core.Billing.Extensions;
+using Bit.Core.Enums;
+using Bit.Core.Models.Api;
+using Bit.Core.Models.Data;
+using Bit.Core.Utilities;
+
+namespace Bit.Api.AdminConsole.Models.Response;
+
+///
+/// Contains organization properties for both OrganizationUsers and ProviderUsers.
+/// Any organization properties in sync data should be added to this class so they are populated for both
+/// members and providers.
+///
+public abstract class BaseProfileOrganizationResponseModel : ResponseModel
+{
+ protected BaseProfileOrganizationResponseModel(
+ string type, IProfileOrganizationDetails organizationDetails) : base(type)
+ {
+ Id = organizationDetails.OrganizationId;
+ UserId = organizationDetails.UserId;
+ Name = organizationDetails.Name;
+ Enabled = organizationDetails.Enabled;
+ Identifier = organizationDetails.Identifier;
+ ProductTierType = organizationDetails.PlanType.GetProductTier();
+ UsePolicies = organizationDetails.UsePolicies;
+ UseSso = organizationDetails.UseSso;
+ UseKeyConnector = organizationDetails.UseKeyConnector;
+ UseScim = organizationDetails.UseScim;
+ UseGroups = organizationDetails.UseGroups;
+ UseDirectory = organizationDetails.UseDirectory;
+ UseEvents = organizationDetails.UseEvents;
+ UseTotp = organizationDetails.UseTotp;
+ Use2fa = organizationDetails.Use2fa;
+ UseApi = organizationDetails.UseApi;
+ UseResetPassword = organizationDetails.UseResetPassword;
+ UsersGetPremium = organizationDetails.UsersGetPremium;
+ UseCustomPermissions = organizationDetails.UseCustomPermissions;
+ UseActivateAutofillPolicy = organizationDetails.PlanType.GetProductTier() == ProductTierType.Enterprise;
+ UseRiskInsights = organizationDetails.UseRiskInsights;
+ UseOrganizationDomains = organizationDetails.UseOrganizationDomains;
+ UseAdminSponsoredFamilies = organizationDetails.UseAdminSponsoredFamilies;
+ UseAutomaticUserConfirmation = organizationDetails.UseAutomaticUserConfirmation;
+ UseSecretsManager = organizationDetails.UseSecretsManager;
+ UsePasswordManager = organizationDetails.UsePasswordManager;
+ SelfHost = organizationDetails.SelfHost;
+ Seats = organizationDetails.Seats;
+ MaxCollections = organizationDetails.MaxCollections;
+ MaxStorageGb = organizationDetails.MaxStorageGb;
+ Key = organizationDetails.Key;
+ HasPublicAndPrivateKeys = organizationDetails.PublicKey != null && organizationDetails.PrivateKey != null;
+ SsoBound = !string.IsNullOrWhiteSpace(organizationDetails.SsoExternalId);
+ ResetPasswordEnrolled = !string.IsNullOrWhiteSpace(organizationDetails.ResetPasswordKey);
+ ProviderId = organizationDetails.ProviderId;
+ ProviderName = organizationDetails.ProviderName;
+ ProviderType = organizationDetails.ProviderType;
+ LimitCollectionCreation = organizationDetails.LimitCollectionCreation;
+ LimitCollectionDeletion = organizationDetails.LimitCollectionDeletion;
+ LimitItemDeletion = organizationDetails.LimitItemDeletion;
+ AllowAdminAccessToAllCollectionItems = organizationDetails.AllowAdminAccessToAllCollectionItems;
+ SsoEnabled = organizationDetails.SsoEnabled ?? false;
+ if (organizationDetails.SsoConfig != null)
+ {
+ var ssoConfigData = SsoConfigurationData.Deserialize(organizationDetails.SsoConfig);
+ KeyConnectorEnabled = ssoConfigData.MemberDecryptionType == MemberDecryptionType.KeyConnector && !string.IsNullOrEmpty(ssoConfigData.KeyConnectorUrl);
+ KeyConnectorUrl = ssoConfigData.KeyConnectorUrl;
+ SsoMemberDecryptionType = ssoConfigData.MemberDecryptionType;
+ }
+ }
+
+ public Guid Id { get; set; }
+ [JsonConverter(typeof(HtmlEncodingStringConverter))]
+ public string Name { get; set; } = null!;
+ public bool Enabled { get; set; }
+ public string? Identifier { get; set; }
+ public ProductTierType ProductTierType { get; set; }
+ public bool UsePolicies { get; set; }
+ public bool UseSso { get; set; }
+ public bool UseKeyConnector { get; set; }
+ public bool UseScim { get; set; }
+ public bool UseGroups { get; set; }
+ public bool UseDirectory { get; set; }
+ public bool UseEvents { get; set; }
+ public bool UseTotp { get; set; }
+ public bool Use2fa { get; set; }
+ public bool UseApi { get; set; }
+ public bool UseResetPassword { get; set; }
+ public bool UseSecretsManager { get; set; }
+ public bool UsePasswordManager { get; set; }
+ public bool UsersGetPremium { get; set; }
+ public bool UseCustomPermissions { get; set; }
+ public bool UseActivateAutofillPolicy { get; set; }
+ public bool UseRiskInsights { get; set; }
+ public bool UseOrganizationDomains { get; set; }
+ public bool UseAdminSponsoredFamilies { get; set; }
+ public bool UseAutomaticUserConfirmation { get; set; }
+ public bool SelfHost { get; set; }
+ public int? Seats { get; set; }
+ public short? MaxCollections { get; set; }
+ public short? MaxStorageGb { get; set; }
+ public string? Key { get; set; }
+ public bool HasPublicAndPrivateKeys { get; set; }
+ public bool SsoBound { get; set; }
+ public bool ResetPasswordEnrolled { get; set; }
+ public bool LimitCollectionCreation { get; set; }
+ public bool LimitCollectionDeletion { get; set; }
+ public bool LimitItemDeletion { get; set; }
+ public bool AllowAdminAccessToAllCollectionItems { get; set; }
+ public Guid? ProviderId { get; set; }
+ [JsonConverter(typeof(HtmlEncodingStringConverter))]
+ public string? ProviderName { get; set; }
+ public ProviderType? ProviderType { get; set; }
+ public bool SsoEnabled { get; set; }
+ public bool KeyConnectorEnabled { get; set; }
+ public string? KeyConnectorUrl { get; set; }
+ public MemberDecryptionType? SsoMemberDecryptionType { get; set; }
+ public bool AccessSecretsManager { get; set; }
+ public Guid? UserId { get; set; }
+ public OrganizationUserStatusType Status { get; set; }
+ public OrganizationUserType Type { get; set; }
+ public Permissions? Permissions { get; set; }
+}
diff --git a/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs
index 5a8669bb52..97a58d038a 100644
--- a/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs
+++ b/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs
@@ -1,150 +1,47 @@
-// FIXME: Update this file to be null safe and then delete the line below
-#nullable disable
-
-using System.Text.Json.Serialization;
-using Bit.Core.AdminConsole.Enums.Provider;
-using Bit.Core.Auth.Enums;
-using Bit.Core.Auth.Models.Data;
-using Bit.Core.Billing.Enums;
-using Bit.Core.Billing.Extensions;
-using Bit.Core.Enums;
-using Bit.Core.Models.Api;
+using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Utilities;
namespace Bit.Api.AdminConsole.Models.Response;
-public class ProfileOrganizationResponseModel : ResponseModel
+///
+/// Sync data for organization members and their organization.
+/// Note: see for organization sync data received by provider users.
+///
+public class ProfileOrganizationResponseModel : BaseProfileOrganizationResponseModel
{
- public ProfileOrganizationResponseModel(string str) : base(str) { }
-
public ProfileOrganizationResponseModel(
- OrganizationUserOrganizationDetails organization,
+ OrganizationUserOrganizationDetails organizationDetails,
IEnumerable organizationIdsClaimingUser)
- : this("profileOrganization")
+ : base("profileOrganization", organizationDetails)
{
- Id = organization.OrganizationId;
- Name = organization.Name;
- UsePolicies = organization.UsePolicies;
- UseSso = organization.UseSso;
- UseKeyConnector = organization.UseKeyConnector;
- UseScim = organization.UseScim;
- UseGroups = organization.UseGroups;
- UseDirectory = organization.UseDirectory;
- UseEvents = organization.UseEvents;
- UseTotp = organization.UseTotp;
- Use2fa = organization.Use2fa;
- UseApi = organization.UseApi;
- UseResetPassword = organization.UseResetPassword;
- UseSecretsManager = organization.UseSecretsManager;
- UsePasswordManager = organization.UsePasswordManager;
- UsersGetPremium = organization.UsersGetPremium;
- UseCustomPermissions = organization.UseCustomPermissions;
- UseActivateAutofillPolicy = organization.PlanType.GetProductTier() == ProductTierType.Enterprise;
- SelfHost = organization.SelfHost;
- Seats = organization.Seats;
- MaxCollections = organization.MaxCollections;
- MaxStorageGb = organization.MaxStorageGb;
- Key = organization.Key;
- HasPublicAndPrivateKeys = organization.PublicKey != null && organization.PrivateKey != null;
- Status = organization.Status;
- Type = organization.Type;
- Enabled = organization.Enabled;
- SsoBound = !string.IsNullOrWhiteSpace(organization.SsoExternalId);
- Identifier = organization.Identifier;
- Permissions = CoreHelpers.LoadClassFromJsonData(organization.Permissions);
- ResetPasswordEnrolled = !string.IsNullOrWhiteSpace(organization.ResetPasswordKey);
- UserId = organization.UserId;
- OrganizationUserId = organization.OrganizationUserId;
- ProviderId = organization.ProviderId;
- ProviderName = organization.ProviderName;
- ProviderType = organization.ProviderType;
- FamilySponsorshipFriendlyName = organization.FamilySponsorshipFriendlyName;
- IsAdminInitiated = organization.IsAdminInitiated ?? false;
- FamilySponsorshipAvailable = (FamilySponsorshipFriendlyName == null || IsAdminInitiated) &&
+ Status = organizationDetails.Status;
+ Type = organizationDetails.Type;
+ OrganizationUserId = organizationDetails.OrganizationUserId;
+ UserIsClaimedByOrganization = organizationIdsClaimingUser.Contains(organizationDetails.OrganizationId);
+ Permissions = CoreHelpers.LoadClassFromJsonData(organizationDetails.Permissions);
+ IsAdminInitiated = organizationDetails.IsAdminInitiated ?? false;
+ FamilySponsorshipFriendlyName = organizationDetails.FamilySponsorshipFriendlyName;
+ FamilySponsorshipLastSyncDate = organizationDetails.FamilySponsorshipLastSyncDate;
+ FamilySponsorshipToDelete = organizationDetails.FamilySponsorshipToDelete;
+ FamilySponsorshipValidUntil = organizationDetails.FamilySponsorshipValidUntil;
+ FamilySponsorshipAvailable = (organizationDetails.FamilySponsorshipFriendlyName == null || IsAdminInitiated) &&
StaticStore.GetSponsoredPlan(PlanSponsorshipType.FamiliesForEnterprise)
- .UsersCanSponsor(organization);
- ProductTierType = organization.PlanType.GetProductTier();
- FamilySponsorshipLastSyncDate = organization.FamilySponsorshipLastSyncDate;
- FamilySponsorshipToDelete = organization.FamilySponsorshipToDelete;
- FamilySponsorshipValidUntil = organization.FamilySponsorshipValidUntil;
- AccessSecretsManager = organization.AccessSecretsManager;
- LimitCollectionCreation = organization.LimitCollectionCreation;
- LimitCollectionDeletion = organization.LimitCollectionDeletion;
- LimitItemDeletion = organization.LimitItemDeletion;
- AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
- UserIsClaimedByOrganization = organizationIdsClaimingUser.Contains(organization.OrganizationId);
- UseRiskInsights = organization.UseRiskInsights;
- UseOrganizationDomains = organization.UseOrganizationDomains;
- UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
- SsoEnabled = organization.SsoEnabled ?? false;
-
- if (organization.SsoConfig != null)
- {
- var ssoConfigData = SsoConfigurationData.Deserialize(organization.SsoConfig);
- KeyConnectorEnabled = ssoConfigData.MemberDecryptionType == MemberDecryptionType.KeyConnector && !string.IsNullOrEmpty(ssoConfigData.KeyConnectorUrl);
- KeyConnectorUrl = ssoConfigData.KeyConnectorUrl;
- SsoMemberDecryptionType = ssoConfigData.MemberDecryptionType;
- }
-
- UseAutomaticUserConfirmation = organization.UseAutomaticUserConfirmation;
+ .UsersCanSponsor(organizationDetails);
+ AccessSecretsManager = organizationDetails.AccessSecretsManager;
}
- public Guid Id { get; set; }
- [JsonConverter(typeof(HtmlEncodingStringConverter))]
- public string Name { get; set; }
- public bool UsePolicies { get; set; }
- public bool UseSso { get; set; }
- public bool UseKeyConnector { get; set; }
- public bool UseScim { get; set; }
- public bool UseGroups { get; set; }
- public bool UseDirectory { get; set; }
- public bool UseEvents { get; set; }
- public bool UseTotp { get; set; }
- public bool Use2fa { get; set; }
- public bool UseApi { get; set; }
- public bool UseResetPassword { get; set; }
- public bool UseSecretsManager { get; set; }
- public bool UsePasswordManager { get; set; }
- public bool UsersGetPremium { get; set; }
- public bool UseCustomPermissions { get; set; }
- public bool UseActivateAutofillPolicy { get; set; }
- public bool SelfHost { get; set; }
- public int? Seats { get; set; }
- public short? MaxCollections { get; set; }
- public short? MaxStorageGb { get; set; }
- public string Key { get; set; }
- public OrganizationUserStatusType Status { get; set; }
- public OrganizationUserType Type { get; set; }
- public bool Enabled { get; set; }
- public bool SsoBound { get; set; }
- public string Identifier { get; set; }
- public Permissions Permissions { get; set; }
- public bool ResetPasswordEnrolled { get; set; }
- public Guid? UserId { get; set; }
public Guid OrganizationUserId { get; set; }
- public bool HasPublicAndPrivateKeys { get; set; }
- public Guid? ProviderId { get; set; }
- [JsonConverter(typeof(HtmlEncodingStringConverter))]
- public string ProviderName { get; set; }
- public ProviderType? ProviderType { get; set; }
- public string FamilySponsorshipFriendlyName { get; set; }
+ public bool UserIsClaimedByOrganization { get; set; }
+ public string? FamilySponsorshipFriendlyName { get; set; }
public bool FamilySponsorshipAvailable { get; set; }
- public ProductTierType ProductTierType { get; set; }
- public bool KeyConnectorEnabled { get; set; }
- public string KeyConnectorUrl { get; set; }
public DateTime? FamilySponsorshipLastSyncDate { get; set; }
public DateTime? FamilySponsorshipValidUntil { get; set; }
public bool? FamilySponsorshipToDelete { get; set; }
- public bool AccessSecretsManager { get; set; }
- public bool LimitCollectionCreation { get; set; }
- public bool LimitCollectionDeletion { get; set; }
- public bool LimitItemDeletion { get; set; }
- public bool AllowAdminAccessToAllCollectionItems { get; set; }
+ public bool IsAdminInitiated { get; set; }
///
- /// Obsolete.
- /// See
+ /// Obsolete property for backward compatibility
///
[Obsolete("Please use UserIsClaimedByOrganization instead. This property will be removed in a future version.")]
public bool UserIsManagedByOrganization
@@ -152,19 +49,4 @@ public class ProfileOrganizationResponseModel : ResponseModel
get => UserIsClaimedByOrganization;
set => UserIsClaimedByOrganization = value;
}
- ///
- /// Indicates if the user is claimed by the organization.
- ///
- ///
- /// A user is claimed by an organization if the user's email domain is verified by the organization and the user is a member.
- /// The organization must be enabled and able to have verified domains.
- ///
- public bool UserIsClaimedByOrganization { get; set; }
- public bool UseRiskInsights { get; set; }
- public bool UseOrganizationDomains { get; set; }
- public bool UseAdminSponsoredFamilies { get; set; }
- public bool IsAdminInitiated { get; set; }
- public bool SsoEnabled { get; set; }
- public MemberDecryptionType? SsoMemberDecryptionType { get; set; }
- public bool UseAutomaticUserConfirmation { get; set; }
}
diff --git a/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs
index fcbb949757..fe31b8cb55 100644
--- a/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs
+++ b/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs
@@ -1,57 +1,24 @@
using Bit.Core.AdminConsole.Models.Data.Provider;
-using Bit.Core.Billing.Enums;
-using Bit.Core.Billing.Extensions;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
namespace Bit.Api.AdminConsole.Models.Response;
-public class ProfileProviderOrganizationResponseModel : ProfileOrganizationResponseModel
+///
+/// Sync data for provider users and their managed organizations.
+/// Note: see for organization sync data received by organization members.
+///
+public class ProfileProviderOrganizationResponseModel : BaseProfileOrganizationResponseModel
{
- public ProfileProviderOrganizationResponseModel(ProviderUserOrganizationDetails organization)
- : base("profileProviderOrganization")
+ public ProfileProviderOrganizationResponseModel(ProviderUserOrganizationDetails organizationDetails)
+ : base("profileProviderOrganization", organizationDetails)
{
- Id = organization.OrganizationId;
- Name = organization.Name;
- UsePolicies = organization.UsePolicies;
- UseSso = organization.UseSso;
- UseKeyConnector = organization.UseKeyConnector;
- UseScim = organization.UseScim;
- UseGroups = organization.UseGroups;
- UseDirectory = organization.UseDirectory;
- UseEvents = organization.UseEvents;
- UseTotp = organization.UseTotp;
- Use2fa = organization.Use2fa;
- UseApi = organization.UseApi;
- UseResetPassword = organization.UseResetPassword;
- UsersGetPremium = organization.UsersGetPremium;
- UseCustomPermissions = organization.UseCustomPermissions;
- UseActivateAutofillPolicy = organization.PlanType.GetProductTier() == ProductTierType.Enterprise;
- SelfHost = organization.SelfHost;
- Seats = organization.Seats;
- MaxCollections = organization.MaxCollections;
- MaxStorageGb = organization.MaxStorageGb;
- Key = organization.Key;
- HasPublicAndPrivateKeys = organization.PublicKey != null && organization.PrivateKey != null;
Status = OrganizationUserStatusType.Confirmed; // Provider users are always confirmed
Type = OrganizationUserType.Owner; // Provider users behave like Owners
- Enabled = organization.Enabled;
- SsoBound = false;
- Identifier = organization.Identifier;
+ ProviderId = organizationDetails.ProviderId;
+ ProviderName = organizationDetails.ProviderName;
+ ProviderType = organizationDetails.ProviderType;
Permissions = new Permissions();
- ResetPasswordEnrolled = false;
- UserId = organization.UserId;
- ProviderId = organization.ProviderId;
- ProviderName = organization.ProviderName;
- ProviderType = organization.ProviderType;
- ProductTierType = organization.PlanType.GetProductTier();
- LimitCollectionCreation = organization.LimitCollectionCreation;
- LimitCollectionDeletion = organization.LimitCollectionDeletion;
- LimitItemDeletion = organization.LimitItemDeletion;
- AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
- UseRiskInsights = organization.UseRiskInsights;
- UseOrganizationDomains = organization.UseOrganizationDomains;
- UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
- UseAutomaticUserConfirmation = organization.UseAutomaticUserConfirmation;
+ AccessSecretsManager = false; // Provider users cannot access Secrets Manager
}
}
diff --git a/src/Core/AdminConsole/Models/Data/IProfileOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/IProfileOrganizationDetails.cs
new file mode 100644
index 0000000000..820b65dbfd
--- /dev/null
+++ b/src/Core/AdminConsole/Models/Data/IProfileOrganizationDetails.cs
@@ -0,0 +1,56 @@
+using Bit.Core.AdminConsole.Enums.Provider;
+using Bit.Core.Billing.Enums;
+
+namespace Bit.Core.AdminConsole.Models.Data;
+
+///
+/// Interface defining common organization details properties shared between
+/// regular organization users and provider organization users for profile endpoints.
+///
+public interface IProfileOrganizationDetails
+{
+ Guid? UserId { get; set; }
+ Guid OrganizationId { get; set; }
+ string Name { get; set; }
+ bool Enabled { get; set; }
+ PlanType PlanType { get; set; }
+ bool UsePolicies { get; set; }
+ bool UseSso { get; set; }
+ bool UseKeyConnector { get; set; }
+ bool UseScim { get; set; }
+ bool UseGroups { get; set; }
+ bool UseDirectory { get; set; }
+ bool UseEvents { get; set; }
+ bool UseTotp { get; set; }
+ bool Use2fa { get; set; }
+ bool UseApi { get; set; }
+ bool UseResetPassword { get; set; }
+ bool SelfHost { get; set; }
+ bool UsersGetPremium { get; set; }
+ bool UseCustomPermissions { get; set; }
+ bool UseSecretsManager { get; set; }
+ int? Seats { get; set; }
+ short? MaxCollections { get; set; }
+ short? MaxStorageGb { get; set; }
+ string? Identifier { get; set; }
+ string? Key { get; set; }
+ string? ResetPasswordKey { get; set; }
+ string? PublicKey { get; set; }
+ string? PrivateKey { get; set; }
+ string? SsoExternalId { get; set; }
+ string? Permissions { get; set; }
+ Guid? ProviderId { get; set; }
+ string? ProviderName { get; set; }
+ ProviderType? ProviderType { get; set; }
+ bool? SsoEnabled { get; set; }
+ string? SsoConfig { get; set; }
+ bool UsePasswordManager { get; set; }
+ bool LimitCollectionCreation { get; set; }
+ bool LimitCollectionDeletion { get; set; }
+ bool AllowAdminAccessToAllCollectionItems { get; set; }
+ bool UseRiskInsights { get; set; }
+ bool LimitItemDeletion { get; set; }
+ bool UseAdminSponsoredFamilies { get; set; }
+ bool UseOrganizationDomains { get; set; }
+ bool UseAutomaticUserConfirmation { get; set; }
+}
diff --git a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs
index 04e481d340..8d30bfc250 100644
--- a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs
+++ b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs
@@ -1,20 +1,18 @@
-// FIXME: Update this file to be null safe and then delete the line below
-#nullable disable
-
-using System.Text.Json.Serialization;
+using System.Text.Json.Serialization;
using Bit.Core.AdminConsole.Enums.Provider;
+using Bit.Core.AdminConsole.Models.Data;
using Bit.Core.Billing.Enums;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Data.Organizations.OrganizationUsers;
-public class OrganizationUserOrganizationDetails
+public class OrganizationUserOrganizationDetails : IProfileOrganizationDetails
{
public Guid OrganizationId { get; set; }
public Guid? UserId { get; set; }
public Guid OrganizationUserId { get; set; }
[JsonConverter(typeof(HtmlEncodingStringConverter))]
- public string Name { get; set; }
+ public string Name { get; set; } = null!;
public bool UsePolicies { get; set; }
public bool UseSso { get; set; }
public bool UseKeyConnector { get; set; }
@@ -33,24 +31,24 @@ public class OrganizationUserOrganizationDetails
public int? Seats { get; set; }
public short? MaxCollections { get; set; }
public short? MaxStorageGb { get; set; }
- public string Key { get; set; }
+ public string? Key { get; set; }
public Enums.OrganizationUserStatusType Status { get; set; }
public Enums.OrganizationUserType Type { get; set; }
public bool Enabled { get; set; }
public PlanType PlanType { get; set; }
- public string SsoExternalId { get; set; }
- public string Identifier { get; set; }
- public string Permissions { get; set; }
- public string ResetPasswordKey { get; set; }
- public string PublicKey { get; set; }
- public string PrivateKey { get; set; }
+ public string? SsoExternalId { get; set; }
+ public string? Identifier { get; set; }
+ public string? Permissions { get; set; }
+ public string? ResetPasswordKey { get; set; }
+ public string? PublicKey { get; set; }
+ public string? PrivateKey { get; set; }
public Guid? ProviderId { get; set; }
[JsonConverter(typeof(HtmlEncodingStringConverter))]
- public string ProviderName { get; set; }
+ public string? ProviderName { get; set; }
public ProviderType? ProviderType { get; set; }
- public string FamilySponsorshipFriendlyName { get; set; }
+ public string? FamilySponsorshipFriendlyName { get; set; }
public bool? SsoEnabled { get; set; }
- public string SsoConfig { get; set; }
+ public string? SsoConfig { get; set; }
public DateTime? FamilySponsorshipLastSyncDate { get; set; }
public DateTime? FamilySponsorshipValidUntil { get; set; }
public bool? FamilySponsorshipToDelete { get; set; }
diff --git a/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs
index 7d68f685b8..0d48f5cfa9 100644
--- a/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs
+++ b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs
@@ -1,19 +1,16 @@
-// FIXME: Update this file to be null safe and then delete the line below
-#nullable disable
-
-using System.Text.Json.Serialization;
+using System.Text.Json.Serialization;
using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.Billing.Enums;
using Bit.Core.Utilities;
namespace Bit.Core.AdminConsole.Models.Data.Provider;
-public class ProviderUserOrganizationDetails
+public class ProviderUserOrganizationDetails : IProfileOrganizationDetails
{
public Guid OrganizationId { get; set; }
public Guid? UserId { get; set; }
[JsonConverter(typeof(HtmlEncodingStringConverter))]
- public string Name { get; set; }
+ public string Name { get; set; } = null!;
public bool UsePolicies { get; set; }
public bool UseSso { get; set; }
public bool UseKeyConnector { get; set; }
@@ -28,20 +25,22 @@ public class ProviderUserOrganizationDetails
public bool SelfHost { get; set; }
public bool UsersGetPremium { get; set; }
public bool UseCustomPermissions { get; set; }
+ public bool UseSecretsManager { get; set; }
+ public bool UsePasswordManager { get; set; }
public int? Seats { get; set; }
public short? MaxCollections { get; set; }
public short? MaxStorageGb { get; set; }
- public string Key { get; set; }
+ public string? Key { get; set; }
public ProviderUserStatusType Status { get; set; }
public ProviderUserType Type { get; set; }
public bool Enabled { get; set; }
- public string Identifier { get; set; }
- public string PublicKey { get; set; }
- public string PrivateKey { get; set; }
+ public string? Identifier { get; set; }
+ public string? PublicKey { get; set; }
+ public string? PrivateKey { get; set; }
public Guid? ProviderId { get; set; }
public Guid? ProviderUserId { get; set; }
[JsonConverter(typeof(HtmlEncodingStringConverter))]
- public string ProviderName { get; set; }
+ public string? ProviderName { get; set; }
public PlanType PlanType { get; set; }
public bool LimitCollectionCreation { get; set; }
public bool LimitCollectionDeletion { get; set; }
@@ -50,6 +49,11 @@ public class ProviderUserOrganizationDetails
public bool UseRiskInsights { get; set; }
public bool UseOrganizationDomains { get; set; }
public bool UseAdminSponsoredFamilies { get; set; }
- public ProviderType ProviderType { get; set; }
+ public ProviderType? ProviderType { get; set; }
public bool UseAutomaticUserConfirmation { get; set; }
+ public bool? SsoEnabled { get; set; }
+ public string? SsoConfig { get; set; }
+ public string? SsoExternalId { get; set; }
+ public string? Permissions { get; set; }
+ public string? ResetPasswordKey { get; set; }
}
diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserOrganizationDetailsViewQuery.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserOrganizationDetailsViewQuery.cs
index 26d3a128fc..504a75c9f2 100644
--- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserOrganizationDetailsViewQuery.cs
+++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserOrganizationDetailsViewQuery.cs
@@ -73,7 +73,8 @@ public class OrganizationUserOrganizationDetailsViewQuery : IQuery new ProviderUserOrganizationDetails
{
OrganizationId = x.po.OrganizationId,
@@ -29,6 +31,9 @@ public class ProviderUserOrganizationDetailsViewQuery : IQuery CreateTestOrganizationAsync(this IOrganizationRepository organizationRepository,
int? seatCount = null,
string identifier = "test")
- => organizationRepository.CreateAsync(new Organization
+ {
+ var id = Guid.NewGuid();
+ return organizationRepository.CreateAsync(new Organization
{
- Name = $"{identifier}-{Guid.NewGuid()}",
- BillingEmail = "billing@example.com", // TODO: EF does not enforce this being NOT NULL
- Plan = "Enterprise (Annually)", // TODO: EF does not enforce this being NOT NULl
+ Name = $"{identifier}-{id}",
+ BillingEmail = $"billing-{id}@example.com",
+ Plan = "Enterprise (Annually)",
PlanType = PlanType.EnterpriseAnnually,
- Seats = seatCount
+ Identifier = $"{identifier}-{id}",
+ BusinessName = $"Test Business {id}",
+ BusinessAddress1 = "123 Test Street",
+ BusinessAddress2 = "Suite 100",
+ BusinessAddress3 = "Building A",
+ BusinessCountry = "US",
+ BusinessTaxNumber = "123456789",
+ Seats = seatCount,
+ MaxCollections = 50,
+ UsePolicies = true,
+ UseSso = true,
+ UseKeyConnector = true,
+ UseScim = true,
+ UseGroups = true,
+ UseDirectory = true,
+ UseEvents = true,
+ UseTotp = true,
+ Use2fa = true,
+ UseApi = true,
+ UseResetPassword = true,
+ UseSecretsManager = true,
+ UsePasswordManager = true,
+ SelfHost = false,
+ UsersGetPremium = true,
+ UseCustomPermissions = true,
+ Storage = 1073741824, // 1 GB in bytes
+ MaxStorageGb = 10,
+ Gateway = GatewayType.Stripe,
+ GatewayCustomerId = $"cus_{id}",
+ GatewaySubscriptionId = $"sub_{id}",
+ ReferenceData = "{\"test\":\"data\"}",
+ Enabled = true,
+ LicenseKey = $"license-{id}",
+ PublicKey = "test-public-key",
+ PrivateKey = "test-private-key",
+ TwoFactorProviders = null,
+ ExpirationDate = DateTime.UtcNow.AddYears(1),
+ MaxAutoscaleSeats = 200,
+ OwnersNotifiedOfAutoscaling = null,
+ Status = OrganizationStatusType.Managed,
+ SmSeats = 50,
+ SmServiceAccounts = 25,
+ MaxAutoscaleSmSeats = 100,
+ MaxAutoscaleSmServiceAccounts = 50,
+ LimitCollectionCreation = true,
+ LimitCollectionDeletion = true,
+ LimitItemDeletion = true,
+ AllowAdminAccessToAllCollectionItems = true,
+ UseRiskInsights = true,
+ UseOrganizationDomains = true,
+ UseAdminSponsoredFamilies = true,
+ SyncSeats = false,
+ UseAutomaticUserConfirmation = true
});
+ }
///
/// Creates a confirmed Owner for the specified organization and user.
diff --git a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/OrganizationUserRepository/OrganizationUserRepositoryTests.cs b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/OrganizationUserRepository/OrganizationUserRepositoryTests.cs
index a60a8e046c..798571df17 100644
--- a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/OrganizationUserRepository/OrganizationUserRepositoryTests.cs
+++ b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/OrganizationUserRepository/OrganizationUserRepositoryTests.cs
@@ -461,13 +461,7 @@ public class OrganizationUserRepositoryTests
KdfParallelism = 3
});
- var organization = await organizationRepository.CreateAsync(new Organization
- {
- Name = "Test Org",
- BillingEmail = user1.Email, // TODO: EF does not enforce this being NOT NULL
- Plan = "Test", // TODO: EF does not enforce this being NOT NULL
- PrivateKey = "privatekey",
- });
+ var organization = await organizationRepository.CreateTestOrganizationAsync();
var orgUser1 = await organizationUserRepository.CreateAsync(new OrganizationUser
{
@@ -536,9 +530,72 @@ public class OrganizationUserRepositoryTests
Assert.Equal(organization.SmServiceAccounts, result.SmServiceAccounts);
Assert.Equal(organization.LimitCollectionCreation, result.LimitCollectionCreation);
Assert.Equal(organization.LimitCollectionDeletion, result.LimitCollectionDeletion);
+ Assert.Equal(organization.LimitItemDeletion, result.LimitItemDeletion);
Assert.Equal(organization.AllowAdminAccessToAllCollectionItems, result.AllowAdminAccessToAllCollectionItems);
Assert.Equal(organization.UseRiskInsights, result.UseRiskInsights);
+ Assert.Equal(organization.UseOrganizationDomains, result.UseOrganizationDomains);
Assert.Equal(organization.UseAdminSponsoredFamilies, result.UseAdminSponsoredFamilies);
+ Assert.Equal(organization.UseAutomaticUserConfirmation, result.UseAutomaticUserConfirmation);
+ }
+
+ [Theory, DatabaseData]
+ public async Task GetManyDetailsByUserAsync_ShouldPopulateSsoPropertiesCorrectly(
+ IUserRepository userRepository,
+ IOrganizationRepository organizationRepository,
+ IOrganizationUserRepository organizationUserRepository,
+ ISsoConfigRepository ssoConfigRepository)
+ {
+ var user = await userRepository.CreateTestUserAsync();
+ var organizationWithSso = await organizationRepository.CreateTestOrganizationAsync();
+ var organizationWithoutSso = await organizationRepository.CreateTestOrganizationAsync();
+
+ var orgUserWithSso = await organizationUserRepository.CreateAsync(new OrganizationUser
+ {
+ OrganizationId = organizationWithSso.Id,
+ UserId = user.Id,
+ Status = OrganizationUserStatusType.Confirmed,
+ Type = OrganizationUserType.Owner,
+ Email = user.Email
+ });
+
+ var orgUserWithoutSso = await organizationUserRepository.CreateAsync(new OrganizationUser
+ {
+ OrganizationId = organizationWithoutSso.Id,
+ UserId = user.Id,
+ Status = OrganizationUserStatusType.Confirmed,
+ Type = OrganizationUserType.User,
+ Email = user.Email
+ });
+
+ // Create SSO configuration for first organization only
+ var serializedSsoConfigData = new SsoConfigurationData
+ {
+ MemberDecryptionType = MemberDecryptionType.KeyConnector,
+ KeyConnectorUrl = "https://keyconnector.example.com"
+ }.Serialize();
+
+ var ssoConfig = await ssoConfigRepository.CreateAsync(new SsoConfig
+ {
+ OrganizationId = organizationWithSso.Id,
+ Enabled = true,
+ Data = serializedSsoConfigData
+ });
+
+ var results = (await organizationUserRepository.GetManyDetailsByUserAsync(user.Id)).ToList();
+
+ Assert.Equal(2, results.Count);
+
+ var orgWithSsoDetails = results.Single(r => r.OrganizationId == organizationWithSso.Id);
+ var orgWithoutSsoDetails = results.Single(r => r.OrganizationId == organizationWithoutSso.Id);
+
+ // Organization with SSO should have SSO properties populated
+ Assert.True(orgWithSsoDetails.SsoEnabled);
+ Assert.NotNull(orgWithSsoDetails.SsoConfig);
+ Assert.Equal(serializedSsoConfigData, orgWithSsoDetails.SsoConfig);
+
+ // Organization without SSO should have null SSO properties
+ Assert.Null(orgWithoutSsoDetails.SsoEnabled);
+ Assert.Null(orgWithoutSsoDetails.SsoConfig);
}
[DatabaseTheory, DatabaseData]
diff --git a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/ProviderUserRepositoryTests.cs b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/ProviderUserRepositoryTests.cs
new file mode 100644
index 0000000000..0d1d28f33d
--- /dev/null
+++ b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/ProviderUserRepositoryTests.cs
@@ -0,0 +1,142 @@
+using Bit.Core.AdminConsole.Entities;
+using Bit.Core.AdminConsole.Entities.Provider;
+using Bit.Core.AdminConsole.Enums.Provider;
+using Bit.Core.AdminConsole.Models.Data.Provider;
+using Bit.Core.AdminConsole.Repositories;
+using Bit.Core.Auth.Entities;
+using Bit.Core.Auth.Enums;
+using Bit.Core.Auth.Models.Data;
+using Bit.Core.Auth.Repositories;
+using Bit.Core.Entities;
+using Bit.Core.Repositories;
+using Xunit;
+
+namespace Bit.Infrastructure.IntegrationTest.AdminConsole.Repositories;
+
+public class ProviderUserRepositoryTests
+{
+ [Theory, DatabaseData]
+ public async Task GetManyOrganizationDetailsByUserAsync_ShouldPopulatePropertiesCorrectly(
+ IUserRepository userRepository,
+ IOrganizationRepository organizationRepository,
+ IProviderRepository providerRepository,
+ IProviderUserRepository providerUserRepository,
+ IProviderOrganizationRepository providerOrganizationRepository,
+ ISsoConfigRepository ssoConfigRepository)
+ {
+ var user = await userRepository.CreateTestUserAsync();
+ var organizationWithSso = await organizationRepository.CreateTestOrganizationAsync();
+ var organizationWithoutSso = await organizationRepository.CreateTestOrganizationAsync();
+
+ var provider = await providerRepository.CreateAsync(new Provider
+ {
+ Name = "Test Provider",
+ Enabled = true,
+ Type = ProviderType.Msp
+ });
+
+ var providerUser = await providerUserRepository.CreateAsync(new ProviderUser
+ {
+ ProviderId = provider.Id,
+ UserId = user.Id,
+ Status = ProviderUserStatusType.Confirmed,
+ Type = ProviderUserType.ProviderAdmin
+ });
+
+ var providerOrganizationWithSso = await providerOrganizationRepository.CreateAsync(new ProviderOrganization
+ {
+ ProviderId = provider.Id,
+ OrganizationId = organizationWithSso.Id
+ });
+
+ var providerOrganizationWithoutSso = await providerOrganizationRepository.CreateAsync(new ProviderOrganization
+ {
+ ProviderId = provider.Id,
+ OrganizationId = organizationWithoutSso.Id
+ });
+
+ // Create SSO configuration for first organization only
+ var serializedSsoConfigData = new SsoConfigurationData
+ {
+ MemberDecryptionType = MemberDecryptionType.KeyConnector,
+ KeyConnectorUrl = "https://keyconnector.example.com"
+ }.Serialize();
+
+ var ssoConfig = await ssoConfigRepository.CreateAsync(new SsoConfig
+ {
+ OrganizationId = organizationWithSso.Id,
+ Enabled = true,
+ Data = serializedSsoConfigData
+ });
+ var results = (await providerUserRepository.GetManyOrganizationDetailsByUserAsync(user.Id, ProviderUserStatusType.Confirmed)).ToList();
+
+ Assert.Equal(2, results.Count);
+
+ var orgWithSsoDetails = results.Single(r => r.OrganizationId == organizationWithSso.Id);
+ var orgWithoutSsoDetails = results.Single(r => r.OrganizationId == organizationWithoutSso.Id);
+
+ // Verify all properties for both organizations
+ AssertProviderOrganizationDetails(orgWithSsoDetails, organizationWithSso, user, provider, providerUser);
+ AssertProviderOrganizationDetails(orgWithoutSsoDetails, organizationWithoutSso, user, provider, providerUser);
+
+ // Organization without SSO should have null SSO properties
+ Assert.Null(orgWithoutSsoDetails.SsoEnabled);
+ Assert.Null(orgWithoutSsoDetails.SsoConfig);
+
+ // Organization with SSO should have SSO properties populated
+ Assert.True(orgWithSsoDetails.SsoEnabled);
+ Assert.NotNull(orgWithSsoDetails.SsoConfig);
+ Assert.Equal(serializedSsoConfigData, orgWithSsoDetails.SsoConfig);
+ }
+
+ private static void AssertProviderOrganizationDetails(
+ ProviderUserOrganizationDetails actual,
+ Organization expectedOrganization,
+ User expectedUser,
+ Provider expectedProvider,
+ ProviderUser expectedProviderUser)
+ {
+ // Organization properties
+ Assert.Equal(expectedOrganization.Id, actual.OrganizationId);
+ Assert.Equal(expectedUser.Id, actual.UserId);
+ Assert.Equal(expectedOrganization.Name, actual.Name);
+ Assert.Equal(expectedOrganization.UsePolicies, actual.UsePolicies);
+ Assert.Equal(expectedOrganization.UseSso, actual.UseSso);
+ Assert.Equal(expectedOrganization.UseKeyConnector, actual.UseKeyConnector);
+ Assert.Equal(expectedOrganization.UseScim, actual.UseScim);
+ Assert.Equal(expectedOrganization.UseGroups, actual.UseGroups);
+ Assert.Equal(expectedOrganization.UseDirectory, actual.UseDirectory);
+ Assert.Equal(expectedOrganization.UseEvents, actual.UseEvents);
+ Assert.Equal(expectedOrganization.UseTotp, actual.UseTotp);
+ Assert.Equal(expectedOrganization.Use2fa, actual.Use2fa);
+ Assert.Equal(expectedOrganization.UseApi, actual.UseApi);
+ Assert.Equal(expectedOrganization.UseResetPassword, actual.UseResetPassword);
+ Assert.Equal(expectedOrganization.UsersGetPremium, actual.UsersGetPremium);
+ Assert.Equal(expectedOrganization.UseCustomPermissions, actual.UseCustomPermissions);
+ Assert.Equal(expectedOrganization.SelfHost, actual.SelfHost);
+ Assert.Equal(expectedOrganization.Seats, actual.Seats);
+ Assert.Equal(expectedOrganization.MaxCollections, actual.MaxCollections);
+ Assert.Equal(expectedOrganization.MaxStorageGb, actual.MaxStorageGb);
+ Assert.Equal(expectedOrganization.Identifier, actual.Identifier);
+ Assert.Equal(expectedOrganization.PublicKey, actual.PublicKey);
+ Assert.Equal(expectedOrganization.PrivateKey, actual.PrivateKey);
+ Assert.Equal(expectedOrganization.Enabled, actual.Enabled);
+ Assert.Equal(expectedOrganization.PlanType, actual.PlanType);
+ Assert.Equal(expectedOrganization.LimitCollectionCreation, actual.LimitCollectionCreation);
+ Assert.Equal(expectedOrganization.LimitCollectionDeletion, actual.LimitCollectionDeletion);
+ Assert.Equal(expectedOrganization.LimitItemDeletion, actual.LimitItemDeletion);
+ Assert.Equal(expectedOrganization.AllowAdminAccessToAllCollectionItems, actual.AllowAdminAccessToAllCollectionItems);
+ Assert.Equal(expectedOrganization.UseRiskInsights, actual.UseRiskInsights);
+ Assert.Equal(expectedOrganization.UseOrganizationDomains, actual.UseOrganizationDomains);
+ Assert.Equal(expectedOrganization.UseAdminSponsoredFamilies, actual.UseAdminSponsoredFamilies);
+ Assert.Equal(expectedOrganization.UseAutomaticUserConfirmation, actual.UseAutomaticUserConfirmation);
+
+ // Provider-specific properties
+ Assert.Equal(expectedProvider.Id, actual.ProviderId);
+ Assert.Equal(expectedProvider.Name, actual.ProviderName);
+ Assert.Equal(expectedProvider.Type, actual.ProviderType);
+ Assert.Equal(expectedProviderUser.Id, actual.ProviderUserId);
+ Assert.Equal(expectedProviderUser.Status, actual.Status);
+ Assert.Equal(expectedProviderUser.Type, actual.Type);
+ }
+}
diff --git a/util/Migrator/DbScripts/2025-10-22_00_ProviderUserOrganizationSsoData.sql b/util/Migrator/DbScripts/2025-10-22_00_ProviderUserOrganizationSsoData.sql
new file mode 100644
index 0000000000..4b28740f2f
--- /dev/null
+++ b/util/Migrator/DbScripts/2025-10-22_00_ProviderUserOrganizationSsoData.sql
@@ -0,0 +1,64 @@
+CREATE OR ALTER VIEW [dbo].[ProviderUserProviderOrganizationDetailsView]
+AS
+SELECT
+ PU.[UserId],
+ PO.[OrganizationId],
+ O.[Name],
+ O.[Enabled],
+ O.[UsePolicies],
+ O.[UseSso],
+ O.[UseKeyConnector],
+ O.[UseScim],
+ O.[UseGroups],
+ O.[UseDirectory],
+ O.[UseEvents],
+ O.[UseTotp],
+ O.[Use2fa],
+ O.[UseApi],
+ O.[UseResetPassword],
+ O.[UseSecretsManager],
+ O.[UsePasswordManager],
+ O.[SelfHost],
+ O.[UsersGetPremium],
+ O.[UseCustomPermissions],
+ O.[Seats],
+ O.[MaxCollections],
+ O.[MaxStorageGb],
+ O.[Identifier],
+ PO.[Key],
+ O.[PublicKey],
+ O.[PrivateKey],
+ PU.[Status],
+ PU.[Type],
+ PO.[ProviderId],
+ PU.[Id] ProviderUserId,
+ P.[Name] ProviderName,
+ O.[PlanType],
+ O.[LimitCollectionCreation],
+ O.[LimitCollectionDeletion],
+ O.[AllowAdminAccessToAllCollectionItems],
+ O.[UseRiskInsights],
+ O.[UseAdminSponsoredFamilies],
+ P.[Type] ProviderType,
+ O.[LimitItemDeletion],
+ O.[UseOrganizationDomains],
+ O.[UseAutomaticUserConfirmation],
+ SS.[Enabled] SsoEnabled,
+ SS.[Data] SsoConfig
+FROM
+ [dbo].[ProviderUser] PU
+INNER JOIN
+ [dbo].[ProviderOrganization] PO ON PO.[ProviderId] = PU.[ProviderId]
+INNER JOIN
+ [dbo].[Organization] O ON O.[Id] = PO.[OrganizationId]
+INNER JOIN
+ [dbo].[Provider] P ON P.[Id] = PU.[ProviderId]
+LEFT JOIN
+ [dbo].[SsoConfig] SS ON SS.[OrganizationId] = O.[Id]
+GO
+
+IF OBJECT_ID('[dbo].[ProviderUserProviderOrganizationDetails_ReadByUserIdStatus]') IS NOT NULL
+BEGIN
+ EXECUTE sp_refreshsqlmodule N'[dbo].[ProviderUserProviderOrganizationDetails_ReadByUserIdStatus]';
+END
+GO