diff --git a/src/Admin/Models/OrganizationEditModel.cs b/src/Admin/Models/OrganizationEditModel.cs
index 30b6f897d2..fe9c445cb5 100644
--- a/src/Admin/Models/OrganizationEditModel.cs
+++ b/src/Admin/Models/OrganizationEditModel.cs
@@ -32,6 +32,7 @@ namespace Bit.Admin.Models
MaxCollections = org.MaxCollections;
UsePolicies = org.UsePolicies;
UseSso = org.UseSso;
+ UseKeyConnector = org.UseKeyConnector;
UseGroups = org.UseGroups;
UseDirectory = org.UseDirectory;
UseEvents = org.UseEvents;
@@ -78,6 +79,8 @@ namespace Bit.Admin.Models
public bool UsePolicies { get; set; }
[Display(Name = "SSO")]
public bool UseSso { get; set; }
+ [Display(Name = "Key Connector with Customer Encryption")]
+ public bool UseKeyConnector { get; set; }
[Display(Name = "Groups")]
public bool UseGroups { get; set; }
[Display(Name = "Directory")]
@@ -123,6 +126,7 @@ namespace Bit.Admin.Models
existingOrganization.MaxCollections = MaxCollections;
existingOrganization.UsePolicies = UsePolicies;
existingOrganization.UseSso = UseSso;
+ existingOrganization.UseKeyConnector = UseKeyConnector;
existingOrganization.UseGroups = UseGroups;
existingOrganization.UseDirectory = UseDirectory;
existingOrganization.UseEvents = UseEvents;
diff --git a/src/Admin/Views/Organizations/Edit.cshtml b/src/Admin/Views/Organizations/Edit.cshtml
index bc1c7ad571..c154ea2457 100644
--- a/src/Admin/Views/Organizations/Edit.cshtml
+++ b/src/Admin/Views/Organizations/Edit.cshtml
@@ -215,6 +215,10 @@
+
diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs
index 74a59d021f..eb977568fc 100644
--- a/src/Api/Controllers/OrganizationsController.cs
+++ b/src/Api/Controllers/OrganizationsController.cs
@@ -385,7 +385,7 @@ namespace Bit.Api.Controllers
}
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(orgGuidId);
- if (ssoConfig?.GetData()?.UseKeyConnector == true)
+ if (ssoConfig?.GetData()?.KeyConnectorEnabled == true)
{
throw new BadRequestException("You cannot leave an Organization that is using Key Connector.");
}
@@ -648,7 +648,7 @@ namespace Bit.Api.Controllers
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(id);
ssoConfig = ssoConfig == null ? model.ToSsoConfig(id) : model.ToSsoConfig(ssoConfig);
- await _ssoConfigService.SaveAsync(ssoConfig);
+ await _ssoConfigService.SaveAsync(ssoConfig, organization);
return new OrganizationSsoResponseModel(organization, _globalSettings, ssoConfig);
}
diff --git a/src/Core/IdentityServer/CustomTokenRequestValidator.cs b/src/Core/IdentityServer/CustomTokenRequestValidator.cs
index bc113b82a0..876706ce96 100644
--- a/src/Core/IdentityServer/CustomTokenRequestValidator.cs
+++ b/src/Core/IdentityServer/CustomTokenRequestValidator.cs
@@ -111,7 +111,7 @@ namespace Bit.Core.IdentityServer
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organizationId);
var ssoConfigData = ssoConfig.GetData();
- if (ssoConfigData is { UseKeyConnector: true } && !string.IsNullOrEmpty(ssoConfigData.KeyConnectorUrl))
+ if (ssoConfigData is { KeyConnectorEnabled: true } && !string.IsNullOrEmpty(ssoConfigData.KeyConnectorUrl))
{
context.Result.CustomResponse["KeyConnectorUrl"] = ssoConfigData.KeyConnectorUrl;
// Prevent clients redirecting to set-password
diff --git a/src/Core/Models/Api/Request/Organizations/OrganizationSsoRequestModel.cs b/src/Core/Models/Api/Request/Organizations/OrganizationSsoRequestModel.cs
index f87ddfdf7d..c52740c5b0 100644
--- a/src/Core/Models/Api/Request/Organizations/OrganizationSsoRequestModel.cs
+++ b/src/Core/Models/Api/Request/Organizations/OrganizationSsoRequestModel.cs
@@ -42,7 +42,7 @@ namespace Bit.Core.Models.Api
[Required]
public SsoType ConfigType { get; set; }
- public bool UseKeyConnector { get; set; }
+ public bool KeyConnectorEnabled { get; set; }
public string KeyConnectorUrl { get; set; }
// OIDC
@@ -178,7 +178,7 @@ namespace Bit.Core.Models.Api
return new SsoConfigurationData
{
ConfigType = ConfigType,
- UseKeyConnector = UseKeyConnector,
+ KeyConnectorEnabled = KeyConnectorEnabled,
KeyConnectorUrl = KeyConnectorUrl,
Authority = Authority,
ClientId = ClientId,
diff --git a/src/Core/Models/Api/Response/OrganizationResponseModel.cs b/src/Core/Models/Api/Response/OrganizationResponseModel.cs
index 7454a37d4a..bee98103ef 100644
--- a/src/Core/Models/Api/Response/OrganizationResponseModel.cs
+++ b/src/Core/Models/Api/Response/OrganizationResponseModel.cs
@@ -35,6 +35,7 @@ namespace Bit.Core.Models.Api
MaxStorageGb = organization.MaxStorageGb;
UsePolicies = organization.UsePolicies;
UseSso = organization.UseSso;
+ UseKeyConnector = organization.UseKeyConnector;
UseGroups = organization.UseGroups;
UseDirectory = organization.UseDirectory;
UseEvents = organization.UseEvents;
@@ -65,6 +66,7 @@ namespace Bit.Core.Models.Api
public short? MaxStorageGb { get; set; }
public bool UsePolicies { get; set; }
public bool UseSso { get; set; }
+ public bool UseKeyConnector { get; set; }
public bool UseGroups { get; set; }
public bool UseDirectory { get; set; }
public bool UseEvents { get; set; }
diff --git a/src/Core/Models/Api/Response/ProfileOrganizationResponseModel.cs b/src/Core/Models/Api/Response/ProfileOrganizationResponseModel.cs
index 427873b653..b4476c3afd 100644
--- a/src/Core/Models/Api/Response/ProfileOrganizationResponseModel.cs
+++ b/src/Core/Models/Api/Response/ProfileOrganizationResponseModel.cs
@@ -14,6 +14,7 @@ namespace Bit.Core.Models.Api
Name = organization.Name;
UsePolicies = organization.UsePolicies;
UseSso = organization.UseSso;
+ UseKeyConnector = organization.UseKeyConnector;
UseGroups = organization.UseGroups;
UseDirectory = organization.UseDirectory;
UseEvents = organization.UseEvents;
@@ -41,7 +42,7 @@ namespace Bit.Core.Models.Api
if (organization.SsoConfig != null)
{
var ssoConfigData = SsoConfigurationData.Deserialize(organization.SsoConfig);
- UsesKeyConnector = ssoConfigData.UseKeyConnector && !string.IsNullOrEmpty(ssoConfigData.KeyConnectorUrl);
+ KeyConnectorEnabled = ssoConfigData.KeyConnectorEnabled && !string.IsNullOrEmpty(ssoConfigData.KeyConnectorUrl);
KeyConnectorUrl = ssoConfigData.KeyConnectorUrl;
}
}
@@ -50,6 +51,7 @@ namespace Bit.Core.Models.Api
public string Name { get; set; }
public bool UsePolicies { get; set; }
public bool UseSso { get; set; }
+ public bool UseKeyConnector { get; set; }
public bool UseGroups { get; set; }
public bool UseDirectory { get; set; }
public bool UseEvents { get; set; }
@@ -74,7 +76,7 @@ namespace Bit.Core.Models.Api
public bool HasPublicAndPrivateKeys { get; set; }
public string ProviderId { get; set; }
public string ProviderName { get; set; }
- public bool UsesKeyConnector { get; set; }
+ public bool KeyConnectorEnabled { get; set; }
public string KeyConnectorUrl { get; set; }
}
}
diff --git a/src/Core/Models/Api/Response/ProfileProviderOrganizationResponseModel.cs b/src/Core/Models/Api/Response/ProfileProviderOrganizationResponseModel.cs
index c877fc6352..f760c78454 100644
--- a/src/Core/Models/Api/Response/ProfileProviderOrganizationResponseModel.cs
+++ b/src/Core/Models/Api/Response/ProfileProviderOrganizationResponseModel.cs
@@ -12,6 +12,7 @@ namespace Bit.Core.Models.Api
Name = organization.Name;
UsePolicies = organization.UsePolicies;
UseSso = organization.UseSso;
+ UseKeyConnector = organization.UseKeyConnector;
UseGroups = organization.UseGroups;
UseDirectory = organization.UseDirectory;
UseEvents = organization.UseEvents;
diff --git a/src/Core/Models/Business/OrganizationLicense.cs b/src/Core/Models/Business/OrganizationLicense.cs
index cd68527e3d..d493855e6d 100644
--- a/src/Core/Models/Business/OrganizationLicense.cs
+++ b/src/Core/Models/Business/OrganizationLicense.cs
@@ -20,7 +20,7 @@ namespace Bit.Core.Models.Business
public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, Guid installationId,
ILicensingService licenseService, int? version = null)
{
- Version = version.GetValueOrDefault(7); // TODO: bump to version 8
+ Version = version.GetValueOrDefault(CURRENT_LICENSE_FILE_VERSION); // TODO: Remember to change the constant
LicenseKey = org.LicenseKey;
InstallationId = installationId;
Id = org.Id;
@@ -34,6 +34,7 @@ namespace Bit.Core.Models.Business
MaxCollections = org.MaxCollections;
UsePolicies = org.UsePolicies;
UseSso = org.UseSso;
+ UseKeyConnector = org.UseKeyConnector;
UseGroups = org.UseGroups;
UseEvents = org.UseEvents;
UseDirectory = org.UseDirectory;
@@ -104,6 +105,7 @@ namespace Bit.Core.Models.Business
public short? MaxCollections { get; set; }
public bool UsePolicies { get; set; }
public bool UseSso { get; set; }
+ public bool UseKeyConnector { get; set; }
public bool UseGroups { get; set; }
public bool UseEvents { get; set; }
public bool UseDirectory { get; set; }
@@ -124,10 +126,19 @@ namespace Bit.Core.Models.Business
[JsonIgnore]
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
+ ///
+ /// Represents the current version of the license format. Should be updated whenever new fields are added.
+ ///
+ private const int CURRENT_LICENSE_FILE_VERSION = 8;
+ private bool ValidLicenseVersion
+ {
+ get => Version is >= 1 and <= 9;
+ }
+
public byte[] GetDataBytes(bool forHash = false)
{
string data = null;
- if (Version >= 1 && Version <= 8)
+ if (ValidLicenseVersion)
{
var props = typeof(OrganizationLicense)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
@@ -148,6 +159,8 @@ namespace Bit.Core.Models.Business
(Version >= 7 || !p.Name.Equals(nameof(UseSso))) &&
// UseResetPassword was added in Version 8
(Version >= 8 || !p.Name.Equals(nameof(UseResetPassword))) &&
+ // UseKeyConnector was added in Version 9
+ (Version >= 9 || !p.Name.Equals(nameof(UseKeyConnector))) &&
(
!forHash ||
(
@@ -184,7 +197,7 @@ namespace Bit.Core.Models.Business
return false;
}
- if (Version >= 1 && Version <= 8)
+ if (ValidLicenseVersion)
{
return InstallationId == globalSettings.Installation.Id && SelfHost;
}
@@ -201,7 +214,7 @@ namespace Bit.Core.Models.Business
return false;
}
- if (Version >= 1 && Version <= 8)
+ if (ValidLicenseVersion)
{
var valid =
globalSettings.Installation.Id == InstallationId &&
@@ -245,12 +258,17 @@ namespace Bit.Core.Models.Business
{
valid = organization.UseSso == UseSso;
}
-
+
if (valid && Version >= 8)
{
valid = organization.UseResetPassword == UseResetPassword;
}
+ if (valid && Version >= 9)
+ {
+ valid = organization.UseKeyConnector == UseKeyConnector;
+ }
+
return valid;
}
else
diff --git a/src/Core/Models/Data/OrganizationAbility.cs b/src/Core/Models/Data/OrganizationAbility.cs
index 0433f38805..e02ab6a18c 100644
--- a/src/Core/Models/Data/OrganizationAbility.cs
+++ b/src/Core/Models/Data/OrganizationAbility.cs
@@ -17,6 +17,7 @@ namespace Bit.Core.Models.Data
UsersGetPremium = organization.UsersGetPremium;
Enabled = organization.Enabled;
UseSso = organization.UseSso;
+ UseKeyConnector = organization.UseKeyConnector;
UseResetPassword = organization.UseResetPassword;
}
@@ -27,6 +28,7 @@ namespace Bit.Core.Models.Data
public bool UsersGetPremium { get; set; }
public bool Enabled { get; set; }
public bool UseSso { get; set; }
+ public bool UseKeyConnector { get; set; }
public bool UseResetPassword { get; set; }
}
}
diff --git a/src/Core/Models/Data/OrganizationUserOrganizationDetails.cs b/src/Core/Models/Data/OrganizationUserOrganizationDetails.cs
index 85f1cc3968..06a3ba5123 100644
--- a/src/Core/Models/Data/OrganizationUserOrganizationDetails.cs
+++ b/src/Core/Models/Data/OrganizationUserOrganizationDetails.cs
@@ -9,6 +9,7 @@ namespace Bit.Core.Models.Data
public string Name { get; set; }
public bool UsePolicies { get; set; }
public bool UseSso { get; set; }
+ public bool UseKeyConnector { get; set; }
public bool UseGroups { get; set; }
public bool UseDirectory { get; set; }
public bool UseEvents { get; set; }
diff --git a/src/Core/Models/Data/Provider/ProviderUserOrganizationDetails.cs b/src/Core/Models/Data/Provider/ProviderUserOrganizationDetails.cs
index 135617ddd4..38c5bb2106 100644
--- a/src/Core/Models/Data/Provider/ProviderUserOrganizationDetails.cs
+++ b/src/Core/Models/Data/Provider/ProviderUserOrganizationDetails.cs
@@ -10,6 +10,7 @@ namespace Bit.Core.Models.Data
public string Name { get; set; }
public bool UsePolicies { get; set; }
public bool UseSso { get; set; }
+ public bool UseKeyConnector { get; set; }
public bool UseGroups { get; set; }
public bool UseDirectory { get; set; }
public bool UseEvents { get; set; }
diff --git a/src/Core/Models/Data/SsoConfigurationData.cs b/src/Core/Models/Data/SsoConfigurationData.cs
index a7f2e3f99a..b8745e6049 100644
--- a/src/Core/Models/Data/SsoConfigurationData.cs
+++ b/src/Core/Models/Data/SsoConfigurationData.cs
@@ -27,7 +27,7 @@ namespace Bit.Core.Models.Data
public SsoType ConfigType { get; set; }
- public bool UseKeyConnector { get; set; }
+ public bool KeyConnectorEnabled { get; set; }
public string KeyConnectorUrl { get; set; }
// OIDC
diff --git a/src/Core/Models/StaticStore/Plan.cs b/src/Core/Models/StaticStore/Plan.cs
index b03e35d4cd..2bed65c7fe 100644
--- a/src/Core/Models/StaticStore/Plan.cs
+++ b/src/Core/Models/StaticStore/Plan.cs
@@ -33,6 +33,7 @@ namespace Bit.Core.Models.StaticStore
public bool Has2fa { get; set; }
public bool HasApi { get; set; }
public bool HasSso { get; set; }
+ public bool HasKeyConnector { get; set; }
public bool HasResetPassword { get; set; }
public bool UsersGetPremium { get; set; }
diff --git a/src/Core/Models/Table/Organization.cs b/src/Core/Models/Table/Organization.cs
index de65fbc83b..644017bac6 100644
--- a/src/Core/Models/Table/Organization.cs
+++ b/src/Core/Models/Table/Organization.cs
@@ -38,6 +38,7 @@ namespace Bit.Core.Models.Table
public short? MaxCollections { get; set; }
public bool UsePolicies { get; set; }
public bool UseSso { get; set; }
+ public bool UseKeyConnector { get; set; }
public bool UseGroups { get; set; }
public bool UseDirectory { get; set; }
public bool UseEvents { get; set; }
diff --git a/src/Core/Repositories/EntityFramework/OrganizationRepository.cs b/src/Core/Repositories/EntityFramework/OrganizationRepository.cs
index 2f7c5781d5..d6eb20cc08 100644
--- a/src/Core/Repositories/EntityFramework/OrganizationRepository.cs
+++ b/src/Core/Repositories/EntityFramework/OrganizationRepository.cs
@@ -87,6 +87,7 @@ namespace Bit.Core.Repositories.EntityFramework
UsersGetPremium = e.UsersGetPremium,
Using2fa = e.Use2fa && e.TwoFactorProviders != null,
UseSso = e.UseSso,
+ UseKeyConnector = e.UseKeyConnector,
}).ToListAsync();
}
}
diff --git a/src/Core/Repositories/EntityFramework/Queries/OrganizationUserOrganizationDetailsViewQuery.cs b/src/Core/Repositories/EntityFramework/Queries/OrganizationUserOrganizationDetailsViewQuery.cs
index 293a8dfb86..d48f0cfc69 100644
--- a/src/Core/Repositories/EntityFramework/Queries/OrganizationUserOrganizationDetailsViewQuery.cs
+++ b/src/Core/Repositories/EntityFramework/Queries/OrganizationUserOrganizationDetailsViewQuery.cs
@@ -29,6 +29,7 @@ namespace Bit.Core.Repositories.EntityFramework.Queries
Enabled = x.o.Enabled,
UsePolicies = x.o.UsePolicies,
UseSso = x.o.UseSso,
+ UseKeyConnector = x.o.UseKeyConnector,
UseGroups = x.o.UseGroups,
UseDirectory = x.o.UseDirectory,
UseEvents = x.o.UseEvents,
diff --git a/src/Core/Repositories/EntityFramework/Queries/ProviderUserOrganizationDetailsViewQuery.cs b/src/Core/Repositories/EntityFramework/Queries/ProviderUserOrganizationDetailsViewQuery.cs
index 436f8ee339..c21c68fcc5 100644
--- a/src/Core/Repositories/EntityFramework/Queries/ProviderUserOrganizationDetailsViewQuery.cs
+++ b/src/Core/Repositories/EntityFramework/Queries/ProviderUserOrganizationDetailsViewQuery.cs
@@ -21,6 +21,7 @@ namespace Bit.Core.Repositories.EntityFramework.Queries
Enabled = x.o.Enabled,
UsePolicies = x.o.UsePolicies,
UseSso = x.o.UseSso,
+ UseKeyConnector = x.o.UseKeyConnector,
UseGroups = x.o.UseGroups,
UseDirectory = x.o.UseDirectory,
UseEvents = x.o.UseEvents,
diff --git a/src/Core/Services/ISsoConfigService.cs b/src/Core/Services/ISsoConfigService.cs
index 9154dcccab..3c1d06d755 100644
--- a/src/Core/Services/ISsoConfigService.cs
+++ b/src/Core/Services/ISsoConfigService.cs
@@ -5,6 +5,6 @@ namespace Bit.Core.Services
{
public interface ISsoConfigService
{
- Task SaveAsync(SsoConfig config);
+ Task SaveAsync(SsoConfig config, Organization organization);
}
}
diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs
index b775eb4bf7..b222fe724d 100644
--- a/src/Core/Services/Implementations/OrganizationService.cs
+++ b/src/Core/Services/Implementations/OrganizationService.cs
@@ -247,6 +247,16 @@ namespace Bit.Core.Services
}
}
+ if (!newPlan.HasKeyConnector && organization.UseKeyConnector)
+ {
+ var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id);
+ if (ssoConfig != null && ssoConfig.GetData().KeyConnectorEnabled)
+ {
+ throw new BadRequestException("Your new plan does not allow the Key Connector feature. " +
+ "Disable your Key Connector.");
+ }
+ }
+
if (!newPlan.HasResetPassword && organization.UseResetPassword)
{
var resetPasswordPolicy =
@@ -295,6 +305,7 @@ namespace Bit.Core.Services
organization.Use2fa = newPlan.Has2fa;
organization.UseApi = newPlan.HasApi;
organization.UseSso = newPlan.HasSso;
+ organization.UseKeyConnector = newPlan.HasKeyConnector;
organization.UseResetPassword = newPlan.HasResetPassword;
organization.SelfHost = newPlan.HasSelfHost;
organization.UsersGetPremium = newPlan.UsersGetPremium || upgrade.PremiumAccessAddon;
@@ -687,6 +698,7 @@ namespace Bit.Core.Services
MaxStorageGb = _globalSettings.SelfHosted ? 10240 : license.MaxStorageGb, // 10 TB
UsePolicies = license.UsePolicies,
UseSso = license.UseSso,
+ UseKeyConnector = license.UseKeyConnector,
UseGroups = license.UseGroups,
UseDirectory = license.UseDirectory,
UseEvents = license.UseEvents,
@@ -865,6 +877,16 @@ namespace Bit.Core.Services
}
}
+ if (!license.UseKeyConnector && organization.UseKeyConnector)
+ {
+ var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id);
+ if (ssoConfig != null && ssoConfig.GetData().KeyConnectorEnabled)
+ {
+ throw new BadRequestException($"Your organization currently has Key Connector enabled. " +
+ $"Your new license does not allow for the use of Key Connector. Disable your Key Connector.");
+ }
+ }
+
if (!license.UseResetPassword && organization.UseResetPassword)
{
var resetPasswordPolicy =
@@ -895,6 +917,7 @@ namespace Bit.Core.Services
organization.UseApi = license.UseApi;
organization.UsePolicies = license.UsePolicies;
organization.UseSso = license.UseSso;
+ organization.UseKeyConnector = license.UseKeyConnector;
organization.UseResetPassword = license.UseResetPassword;
organization.SelfHost = license.SelfHost;
organization.UsersGetPremium = license.UsersGetPremium;
@@ -2141,7 +2164,7 @@ namespace Bit.Core.Services
private async Task ValidateDeleteOrganizationAsync(Organization organization)
{
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id);
- if (ssoConfig?.GetData()?.UseKeyConnector == true)
+ if (ssoConfig?.GetData()?.KeyConnectorEnabled == true)
{
throw new BadRequestException("You cannot delete an Organization that is using Key Connector.");
}
diff --git a/src/Core/Services/Implementations/PolicyService.cs b/src/Core/Services/Implementations/PolicyService.cs
index 953317a0bf..8f7fbf1302 100644
--- a/src/Core/Services/Implementations/PolicyService.cs
+++ b/src/Core/Services/Implementations/PolicyService.cs
@@ -157,7 +157,7 @@ namespace Bit.Core.Services
{
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(org.Id);
- if (ssoConfig?.GetData()?.UseKeyConnector == true)
+ if (ssoConfig?.GetData()?.KeyConnectorEnabled == true)
{
throw new BadRequestException("Key Connector is enabled.");
}
diff --git a/src/Core/Services/Implementations/SsoConfigService.cs b/src/Core/Services/Implementations/SsoConfigService.cs
index b97c338699..40fe6a87e1 100644
--- a/src/Core/Services/Implementations/SsoConfigService.cs
+++ b/src/Core/Services/Implementations/SsoConfigService.cs
@@ -30,7 +30,7 @@ namespace Bit.Core.Services
_eventService = eventService;
}
- public async Task SaveAsync(SsoConfig config)
+ public async Task SaveAsync(SsoConfig config, Organization organization)
{
var now = DateTime.UtcNow;
config.RevisionDate = now;
@@ -39,14 +39,14 @@ namespace Bit.Core.Services
config.CreationDate = now;
}
- var useKeyConnector = config.GetData().UseKeyConnector;
+ var useKeyConnector = config.GetData().KeyConnectorEnabled;
if (useKeyConnector)
{
- await VerifyDependenciesAsync(config);
+ await VerifyDependenciesAsync(config, organization);
}
var oldConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(config.OrganizationId);
- var disabledKeyConnector = oldConfig?.GetData()?.UseKeyConnector == true && !useKeyConnector;
+ var disabledKeyConnector = oldConfig?.GetData()?.KeyConnectorEnabled == true && !useKeyConnector;
if (disabledKeyConnector && await AnyOrgUserHasKeyConnectorEnabledAsync(config.OrganizationId))
{
throw new BadRequestException("Key Connector cannot be disabled at this moment.");
@@ -63,8 +63,13 @@ namespace Bit.Core.Services
return userDetails.Any(u => u.UsesKeyConnector);
}
- private async Task VerifyDependenciesAsync(SsoConfig config)
+ private async Task VerifyDependenciesAsync(SsoConfig config, Organization organization)
{
+ if (!organization.UseKeyConnector)
+ {
+ throw new BadRequestException("Organization cannot use Key Connector.");
+ }
+
var singleOrgPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.SingleOrg);
if (singleOrgPolicy is not { Enabled: true })
{
@@ -91,10 +96,10 @@ namespace Bit.Core.Services
await _eventService.LogOrganizationEventAsync(organization, e);
}
- var useKeyConnector = config.GetData().UseKeyConnector;
- if (oldConfig?.GetData()?.UseKeyConnector != useKeyConnector)
+ var keyConnectorEnabled = config.GetData().KeyConnectorEnabled;
+ if (oldConfig?.GetData()?.KeyConnectorEnabled != keyConnectorEnabled)
{
- var e = useKeyConnector
+ var e = keyConnectorEnabled
? EventType.Organization_EnabledKeyConnector
: EventType.Organization_DisabledKeyConnector;
await _eventService.LogOrganizationEventAsync(organization, e);
diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs
index 215e895858..a5b30de095 100644
--- a/src/Core/Services/Implementations/UserService.cs
+++ b/src/Core/Services/Implementations/UserService.cs
@@ -646,7 +646,7 @@ namespace Bit.Core.Services
if (user.UsesKeyConnector)
{
- Logger.LogWarning("Already uses key connector.");
+ Logger.LogWarning("Already uses Key Connector.");
return IdentityResult.Failed(_identityErrorDescriber.UserAlreadyHasPassword());
}
@@ -671,7 +671,7 @@ namespace Bit.Core.Services
if (user.UsesKeyConnector)
{
- Logger.LogWarning("Already uses key connector.");
+ Logger.LogWarning("Already uses Key Connector.");
return IdentityResult.Failed(_identityErrorDescriber.UserAlreadyHasPassword());
}
@@ -740,7 +740,7 @@ namespace Bit.Core.Services
if (user.UsesKeyConnector)
{
- throw new BadRequestException("Cannot reset password of a user with key connector.");
+ throw new BadRequestException("Cannot reset password of a user with Key Connector.");
}
var result = await UpdatePasswordHash(user, newMasterPassword);
@@ -1387,7 +1387,7 @@ namespace Bit.Core.Services
if (!user.UsesKeyConnector)
{
- throw new BadRequestException("Not using key connector.");
+ throw new BadRequestException("Not using Key Connector.");
}
var token = await base.GenerateUserTokenAsync(user, TokenOptions.DefaultEmailProvider,
diff --git a/src/Core/Utilities/StaticStore.cs b/src/Core/Utilities/StaticStore.cs
index f809adc062..c6fb25c6d1 100644
--- a/src/Core/Utilities/StaticStore.cs
+++ b/src/Core/Utilities/StaticStore.cs
@@ -410,6 +410,7 @@ namespace Bit.Core.Utilities
Has2fa = true,
HasApi = true,
HasSso = true,
+ HasKeyConnector = true,
HasResetPassword = true,
UsersGetPremium = true,
@@ -448,6 +449,7 @@ namespace Bit.Core.Utilities
HasApi = true,
HasSelfHost = true,
HasSso = true,
+ HasKeyConnector = true,
HasResetPassword = true,
UsersGetPremium = true,
diff --git a/src/Sql/dbo/Stored Procedures/Organization_Create.sql b/src/Sql/dbo/Stored Procedures/Organization_Create.sql
index 22e190b1c8..3f467e4a60 100644
--- a/src/Sql/dbo/Stored Procedures/Organization_Create.sql
+++ b/src/Sql/dbo/Stored Procedures/Organization_Create.sql
@@ -40,7 +40,8 @@
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@OwnersNotifiedOfAutoscaling DATETIME2(7),
- @MaxAutoscaleSeats INT
+ @MaxAutoscaleSeats INT,
+ @UseKeyConnector BIT = 0
AS
BEGIN
SET NOCOUNT ON
@@ -88,7 +89,8 @@ BEGIN
[CreationDate],
[RevisionDate],
[OwnersNotifiedOfAutoscaling],
- [MaxAutoscaleSeats]
+ [MaxAutoscaleSeats],
+ [UseKeyConnector]
)
VALUES
(
@@ -133,6 +135,7 @@ BEGIN
@CreationDate,
@RevisionDate,
@OwnersNotifiedOfAutoscaling,
- @MaxAutoscaleSeats
+ @MaxAutoscaleSeats,
+ @UseKeyConnector
)
END
diff --git a/src/Sql/dbo/Stored Procedures/Organization_ReadAbilities.sql b/src/Sql/dbo/Stored Procedures/Organization_ReadAbilities.sql
index 88463a2dc5..cf5219f088 100644
--- a/src/Sql/dbo/Stored Procedures/Organization_ReadAbilities.sql
+++ b/src/Sql/dbo/Stored Procedures/Organization_ReadAbilities.sql
@@ -15,6 +15,7 @@ BEGIN
END AS [Using2fa],
[UsersGetPremium],
[UseSso],
+ [UseKeyConnector],
[UseResetPassword],
[Enabled]
FROM
diff --git a/src/Sql/dbo/Stored Procedures/Organization_Update.sql b/src/Sql/dbo/Stored Procedures/Organization_Update.sql
index 2d1eb4fb08..a0ff29c7e2 100644
--- a/src/Sql/dbo/Stored Procedures/Organization_Update.sql
+++ b/src/Sql/dbo/Stored Procedures/Organization_Update.sql
@@ -40,7 +40,8 @@
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@OwnersNotifiedOfAutoscaling DATETIME2(7),
- @MaxAutoscaleSeats INT
+ @MaxAutoscaleSeats INT,
+ @UseKeyConnector BIT = 0
AS
BEGIN
SET NOCOUNT ON
@@ -88,7 +89,8 @@ BEGIN
[CreationDate] = @CreationDate,
[RevisionDate] = @RevisionDate,
[OwnersNotifiedOfAutoscaling] = @OwnersNotifiedOfAutoscaling,
- [MaxAutoscaleSeats] = @MaxAutoscaleSeats
+ [MaxAutoscaleSeats] = @MaxAutoscaleSeats,
+ [UseKeyConnector] = @UseKeyConnector
WHERE
[Id] = @Id
END
diff --git a/src/Sql/dbo/Tables/Organization.sql b/src/Sql/dbo/Tables/Organization.sql
index cd1ffcae69..a40a462b2c 100644
--- a/src/Sql/dbo/Tables/Organization.sql
+++ b/src/Sql/dbo/Tables/Organization.sql
@@ -41,6 +41,7 @@
[RevisionDate] DATETIME2 (7) NOT NULL,
[OwnersNotifiedOfAutoscaling] DATETIME2(7) NULL,
[MaxAutoscaleSeats] INT NULL,
+ [UseKeyConnector] BIT NOT NULL,
CONSTRAINT [PK_Organization] PRIMARY KEY CLUSTERED ([Id] ASC)
);
diff --git a/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql b/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql
index c65c3f3287..288ae8612c 100644
--- a/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql
+++ b/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql
@@ -7,6 +7,7 @@ SELECT
O.[Enabled],
O.[UsePolicies],
O.[UseSso],
+ O.[UseKeyConnector],
O.[UseGroups],
O.[UseDirectory],
O.[UseEvents],
diff --git a/src/Sql/dbo/Views/ProviderUserProviderOrganizationDetailsView.sql b/src/Sql/dbo/Views/ProviderUserProviderOrganizationDetailsView.sql
index 94740127eb..27daa48006 100644
--- a/src/Sql/dbo/Views/ProviderUserProviderOrganizationDetailsView.sql
+++ b/src/Sql/dbo/Views/ProviderUserProviderOrganizationDetailsView.sql
@@ -7,6 +7,7 @@ SELECT
O.[Enabled],
O.[UsePolicies],
O.[UseSso],
+ O.[UseKeyConnector],
O.[UseGroups],
O.[UseDirectory],
O.[UseEvents],
diff --git a/test/Api.Test/Controllers/OrganizationsControllerTests.cs b/test/Api.Test/Controllers/OrganizationsControllerTests.cs
index 9fa3b8ca6d..4011c72b03 100644
--- a/test/Api.Test/Controllers/OrganizationsControllerTests.cs
+++ b/test/Api.Test/Controllers/OrganizationsControllerTests.cs
@@ -10,6 +10,7 @@ using NSubstitute;
using System.Threading.Tasks;
using System.Security.Claims;
using System;
+using Bit.Core.Models.Data;
using Xunit;
namespace Bit.Api.Test.Controllers
@@ -59,7 +60,10 @@ namespace Bit.Api.Test.Controllers
var ssoConfig = new SsoConfig
{
Id = default,
- Data = "{\"useKeyConnector\": true}",
+ Data = new SsoConfigurationData
+ {
+ KeyConnectorEnabled = true,
+ }.Serialize(),
Enabled = true,
OrganizationId = orgId,
};
diff --git a/test/Core.Test/Repositories/EntityFramework/EqualityComparers/OrganizationCompare.cs b/test/Core.Test/Repositories/EntityFramework/EqualityComparers/OrganizationCompare.cs
index d8f51febf1..04fa1b10a9 100644
--- a/test/Core.Test/Repositories/EntityFramework/EqualityComparers/OrganizationCompare.cs
+++ b/test/Core.Test/Repositories/EntityFramework/EqualityComparers/OrganizationCompare.cs
@@ -25,6 +25,7 @@ namespace Bit.Core.Test.Repositories.EntityFramework.EqualityComparers
x.MaxCollections.Equals(y.MaxCollections) &&
x.UsePolicies.Equals(y.UsePolicies) &&
x.UseSso.Equals(y.UseSso) &&
+ x.UseKeyConnector.Equals(y.UseKeyConnector) &&
x.UseGroups.Equals(y.UseGroups) &&
x.UseDirectory.Equals(y.UseDirectory) &&
x.UseEvents.Equals(y.UseEvents) &&
diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs
index 8a505a1cd5..a860da7851 100644
--- a/test/Core.Test/Services/OrganizationServiceTests.cs
+++ b/test/Core.Test/Services/OrganizationServiceTests.cs
@@ -909,7 +909,7 @@ namespace Bit.Core.Test.Services
SsoConfig ssoConfig)
{
ssoConfig.Enabled = true;
- ssoConfig.SetData(new SsoConfigurationData { UseKeyConnector = true });
+ ssoConfig.SetData(new SsoConfigurationData { KeyConnectorEnabled = true });
var ssoConfigRepository = sutProvider.GetDependency
();
var organizationRepository = sutProvider.GetDependency();
var applicationCacheService = sutProvider.GetDependency();
diff --git a/test/Core.Test/Services/PolicyServiceTests.cs b/test/Core.Test/Services/PolicyServiceTests.cs
index dc7705dc1a..00ad9dcaec 100644
--- a/test/Core.Test/Services/PolicyServiceTests.cs
+++ b/test/Core.Test/Services/PolicyServiceTests.cs
@@ -144,7 +144,7 @@ namespace Bit.Core.Test.Services
});
var ssoConfig = new SsoConfig { Enabled = true };
- var data = new SsoConfigurationData { UseKeyConnector = true };
+ var data = new SsoConfigurationData { KeyConnectorEnabled = true };
ssoConfig.SetData(data);
sutProvider.GetDependency()
diff --git a/test/Core.Test/Services/SsoConfigServiceTests.cs b/test/Core.Test/Services/SsoConfigServiceTests.cs
index 0205a1e760..ecb732e948 100644
--- a/test/Core.Test/Services/SsoConfigServiceTests.cs
+++ b/test/Core.Test/Services/SsoConfigServiceTests.cs
@@ -15,9 +15,9 @@ namespace Bit.Core.Test.Services
public class SsoConfigServiceTests
{
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
- public async Task SaveAsync_ExistingItem_UpdatesRevisionDateOnly(SutProvider sutProvider)
+ public async Task SaveAsync_ExistingItem_UpdatesRevisionDateOnly(SutProvider sutProvider,
+ Organization organization)
{
-
var utcNow = DateTime.UtcNow;
var ssoConfig = new SsoConfig
@@ -25,7 +25,7 @@ namespace Bit.Core.Test.Services
Id = 1,
Data = "{}",
Enabled = true,
- OrganizationId = Guid.NewGuid(),
+ OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
@@ -33,7 +33,7 @@ namespace Bit.Core.Test.Services
sutProvider.GetDependency()
.UpsertAsync(ssoConfig).Returns(Task.CompletedTask);
- await sutProvider.Sut.SaveAsync(ssoConfig);
+ await sutProvider.Sut.SaveAsync(ssoConfig, organization);
await sutProvider.GetDependency().Received()
.UpsertAsync(ssoConfig);
@@ -43,7 +43,8 @@ namespace Bit.Core.Test.Services
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
- public async Task SaveAsync_NewItem_UpdatesCreationAndRevisionDate(SutProvider sutProvider)
+ public async Task SaveAsync_NewItem_UpdatesCreationAndRevisionDate(SutProvider sutProvider,
+ Organization organization)
{
var utcNow = DateTime.UtcNow;
@@ -52,7 +53,7 @@ namespace Bit.Core.Test.Services
Id = default,
Data = "{}",
Enabled = true,
- OrganizationId = Guid.NewGuid(),
+ OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
@@ -60,7 +61,7 @@ namespace Bit.Core.Test.Services
sutProvider.GetDependency()
.UpsertAsync(ssoConfig).Returns(Task.CompletedTask);
- await sutProvider.Sut.SaveAsync(ssoConfig);
+ await sutProvider.Sut.SaveAsync(ssoConfig, organization);
await sutProvider.GetDependency().Received()
.UpsertAsync(ssoConfig);
@@ -70,16 +71,20 @@ namespace Bit.Core.Test.Services
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
- public async Task SaveAsync_PreventDisablingKeyConnector(SutProvider sutProvider, Guid orgId)
+ public async Task SaveAsync_PreventDisablingKeyConnector(SutProvider sutProvider,
+ Organization organization)
{
var utcNow = DateTime.UtcNow;
var oldSsoConfig = new SsoConfig
{
Id = 1,
- Data = "{\"useKeyConnector\": true}",
+ Data = new SsoConfigurationData
+ {
+ KeyConnectorEnabled = true,
+ }.Serialize(),
Enabled = true,
- OrganizationId = orgId,
+ OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
@@ -89,19 +94,19 @@ namespace Bit.Core.Test.Services
Id = 1,
Data = "{}",
Enabled = true,
- OrganizationId = orgId,
+ OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow,
};
var ssoConfigRepository = sutProvider.GetDependency();
- ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(oldSsoConfig);
+ ssoConfigRepository.GetByOrganizationIdAsync(organization.Id).Returns(oldSsoConfig);
ssoConfigRepository.UpsertAsync(newSsoConfig).Returns(Task.CompletedTask);
- sutProvider.GetDependency().GetManyDetailsByOrganizationAsync(orgId)
+ sutProvider.GetDependency().GetManyDetailsByOrganizationAsync(organization.Id)
.Returns(new[] { new OrganizationUserUserDetails { UsesKeyConnector = true } });
var exception = await Assert.ThrowsAsync(
- () => sutProvider.Sut.SaveAsync(newSsoConfig));
+ () => sutProvider.Sut.SaveAsync(newSsoConfig, organization));
Assert.Contains("Key Connector cannot be disabled at this moment.", exception.Message);
@@ -111,16 +116,19 @@ namespace Bit.Core.Test.Services
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_AllowDisablingKeyConnectorWhenNoUserIsUsingIt(
- SutProvider sutProvider, Guid orgId)
+ SutProvider sutProvider, Organization organization)
{
var utcNow = DateTime.UtcNow;
var oldSsoConfig = new SsoConfig
{
Id = 1,
- Data = "{\"useKeyConnector\": true}",
+ Data = new SsoConfigurationData
+ {
+ KeyConnectorEnabled = true,
+ }.Serialize(),
Enabled = true,
- OrganizationId = orgId,
+ OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
@@ -130,37 +138,41 @@ namespace Bit.Core.Test.Services
Id = 1,
Data = "{}",
Enabled = true,
- OrganizationId = orgId,
+ OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow,
};
var ssoConfigRepository = sutProvider.GetDependency();
- ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(oldSsoConfig);
+ ssoConfigRepository.GetByOrganizationIdAsync(organization.Id).Returns(oldSsoConfig);
ssoConfigRepository.UpsertAsync(newSsoConfig).Returns(Task.CompletedTask);
- sutProvider.GetDependency().GetManyDetailsByOrganizationAsync(orgId)
+ sutProvider.GetDependency().GetManyDetailsByOrganizationAsync(organization.Id)
.Returns(new[] { new OrganizationUserUserDetails { UsesKeyConnector = false } });
- await sutProvider.Sut.SaveAsync(newSsoConfig);
+ await sutProvider.Sut.SaveAsync(newSsoConfig, organization);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
- public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled_Throws(SutProvider sutProvider)
+ public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled_Throws(SutProvider sutProvider,
+ Organization organization)
{
var utcNow = DateTime.UtcNow;
var ssoConfig = new SsoConfig
{
Id = default,
- Data = "{\"useKeyConnector\": true}",
+ Data = new SsoConfigurationData
+ {
+ KeyConnectorEnabled = true,
+ }.Serialize(),
Enabled = true,
- OrganizationId = Guid.NewGuid(),
+ OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
var exception = await Assert.ThrowsAsync(
- () => sutProvider.Sut.SaveAsync(ssoConfig));
+ () => sutProvider.Sut.SaveAsync(ssoConfig, organization));
Assert.Contains("Key Connector requires the Single Organization policy to be enabled.", exception.Message);
@@ -169,16 +181,20 @@ namespace Bit.Core.Test.Services
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
- public async Task SaveAsync_KeyConnector_SsoPolicyNotEnabled_Throws(SutProvider sutProvider)
+ public async Task SaveAsync_KeyConnector_SsoPolicyNotEnabled_Throws(SutProvider sutProvider,
+ Organization organization)
{
var utcNow = DateTime.UtcNow;
var ssoConfig = new SsoConfig
{
Id = default,
- Data = "{\"useKeyConnector\": true}",
+ Data = new SsoConfigurationData
+ {
+ KeyConnectorEnabled = true,
+ }.Serialize(),
Enabled = true,
- OrganizationId = Guid.NewGuid(),
+ OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
@@ -190,7 +206,7 @@ namespace Bit.Core.Test.Services
});
var exception = await Assert.ThrowsAsync(
- () => sutProvider.Sut.SaveAsync(ssoConfig));
+ () => sutProvider.Sut.SaveAsync(ssoConfig, organization));
Assert.Contains("Key Connector requires the Single Sign-On Authentication policy to be enabled.", exception.Message);
@@ -199,16 +215,20 @@ namespace Bit.Core.Test.Services
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
- public async Task SaveAsync_KeyConnector_SsoConfigNotEnabled_Throws(SutProvider sutProvider)
+ public async Task SaveAsync_KeyConnector_SsoConfigNotEnabled_Throws(SutProvider sutProvider,
+ Organization organization)
{
var utcNow = DateTime.UtcNow;
var ssoConfig = new SsoConfig
{
Id = default,
- Data = "{\"useKeyConnector\": true}",
+ Data = new SsoConfigurationData
+ {
+ KeyConnectorEnabled = true,
+ }.Serialize(),
Enabled = false,
- OrganizationId = Guid.NewGuid(),
+ OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
@@ -220,12 +240,79 @@ namespace Bit.Core.Test.Services
});
var exception = await Assert.ThrowsAsync(
- () => sutProvider.Sut.SaveAsync(ssoConfig));
+ () => sutProvider.Sut.SaveAsync(ssoConfig, organization));
Assert.Contains("You must enable SSO to use Key Connector.", exception.Message);
await sutProvider.GetDependency().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
+
+ [Theory, CustomAutoData(typeof(SutProviderCustomization))]
+ public async Task SaveAsync_KeyConnector_KeyConnectorAbilityNotEnabled_Throws(SutProvider sutProvider,
+ Organization organization)
+ {
+ var utcNow = DateTime.UtcNow;
+
+ organization.UseKeyConnector = false;
+ var ssoConfig = new SsoConfig
+ {
+ Id = default,
+ Data = new SsoConfigurationData
+ {
+ KeyConnectorEnabled = true,
+ }.Serialize(),
+ Enabled = true,
+ OrganizationId = organization.Id,
+ CreationDate = utcNow.AddDays(-10),
+ RevisionDate = utcNow.AddDays(-10),
+ };
+
+ sutProvider.GetDependency().GetByOrganizationIdTypeAsync(
+ Arg.Any(), Arg.Any()).Returns(new Policy
+ {
+ Enabled = true,
+ });
+
+ var exception = await Assert.ThrowsAsync(
+ () => sutProvider.Sut.SaveAsync(ssoConfig, organization));
+
+ Assert.Contains("Organization cannot use Key Connector.", exception.Message);
+
+ await sutProvider.GetDependency().DidNotReceiveWithAnyArgs()
+ .UpsertAsync(default);
+ }
+
+ [Theory, CustomAutoData(typeof(SutProviderCustomization))]
+ public async Task SaveAsync_KeyConnector_Success(SutProvider sutProvider,
+ Organization organization)
+ {
+ var utcNow = DateTime.UtcNow;
+
+ organization.UseKeyConnector = true;
+ var ssoConfig = new SsoConfig
+ {
+ Id = default,
+ Data = new SsoConfigurationData
+ {
+ KeyConnectorEnabled = true,
+ }.Serialize(),
+ Enabled = true,
+ OrganizationId = organization.Id,
+ CreationDate = utcNow.AddDays(-10),
+ RevisionDate = utcNow.AddDays(-10),
+ };
+
+ sutProvider.GetDependency().GetByOrganizationIdTypeAsync(
+ Arg.Any(), Arg.Any()).Returns(new Policy
+ {
+ Enabled = true,
+ });
+
+ await sutProvider.Sut.SaveAsync(ssoConfig, organization);
+
+ await sutProvider.GetDependency().ReceivedWithAnyArgs()
+ .UpsertAsync(default);
+ }
}
}
diff --git a/util/Migrator/DbScripts/2021-11-12_00_KeyConnectorFlag.sql b/util/Migrator/DbScripts/2021-11-12_00_KeyConnectorFlag.sql
new file mode 100644
index 0000000000..b216601e56
--- /dev/null
+++ b/util/Migrator/DbScripts/2021-11-12_00_KeyConnectorFlag.sql
@@ -0,0 +1,420 @@
+IF COL_LENGTH('[dbo].[Organization]', 'UseKeyConnector') IS NULL
+ BEGIN
+ ALTER TABLE
+ [dbo].[Organization]
+ ADD
+ [UseKeyConnector] BIT NULL
+ END
+GO
+
+UPDATE
+ [dbo].[Organization]
+SET
+ [UseKeyConnector] = 0
+WHERE
+ [UseKeyConnector] IS NULL
+GO
+
+ALTER TABLE
+ [dbo].[Organization]
+ ALTER COLUMN
+ [UseKeyConnector] BIT NOT NULL
+GO
+
+IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'OrganizationView')
+ BEGIN
+ DROP VIEW [dbo].[OrganizationView]
+ END
+GO
+
+CREATE VIEW [dbo].[OrganizationView]
+AS
+SELECT
+ *
+FROM
+ [dbo].[Organization]
+GO
+
+IF OBJECT_ID('[dbo].[Organization_Create]') IS NOT NULL
+ BEGIN
+ DROP PROCEDURE [dbo].[Organization_Create]
+ END
+GO
+
+CREATE PROCEDURE [dbo].[Organization_Create]
+ @Id UNIQUEIDENTIFIER OUTPUT,
+ @Identifier NVARCHAR(50),
+ @Name NVARCHAR(50),
+ @BusinessName NVARCHAR(50),
+ @BusinessAddress1 NVARCHAR(50),
+ @BusinessAddress2 NVARCHAR(50),
+ @BusinessAddress3 NVARCHAR(50),
+ @BusinessCountry VARCHAR(2),
+ @BusinessTaxNumber NVARCHAR(30),
+ @BillingEmail NVARCHAR(256),
+ @Plan NVARCHAR(50),
+ @PlanType TINYINT,
+ @Seats INT,
+ @MaxCollections SMALLINT,
+ @UsePolicies BIT,
+ @UseSso BIT,
+ @UseGroups BIT,
+ @UseDirectory BIT,
+ @UseEvents BIT,
+ @UseTotp BIT,
+ @Use2fa BIT,
+ @UseApi BIT,
+ @UseResetPassword BIT,
+ @SelfHost BIT,
+ @UsersGetPremium BIT,
+ @Storage BIGINT,
+ @MaxStorageGb SMALLINT,
+ @Gateway TINYINT,
+ @GatewayCustomerId VARCHAR(50),
+ @GatewaySubscriptionId VARCHAR(50),
+ @ReferenceData VARCHAR(MAX),
+ @Enabled BIT,
+ @LicenseKey VARCHAR(100),
+ @ApiKey VARCHAR(30),
+ @PublicKey VARCHAR(MAX),
+ @PrivateKey VARCHAR(MAX),
+ @TwoFactorProviders NVARCHAR(MAX),
+ @ExpirationDate DATETIME2(7),
+ @CreationDate DATETIME2(7),
+ @RevisionDate DATETIME2(7),
+ @OwnersNotifiedOfAutoscaling DATETIME2(7),
+ @MaxAutoscaleSeats INT,
+ @UseKeyConnector BIT = 0
+AS
+BEGIN
+ SET NOCOUNT ON
+
+ INSERT INTO [dbo].[Organization]
+ (
+ [Id],
+ [Identifier],
+ [Name],
+ [BusinessName],
+ [BusinessAddress1],
+ [BusinessAddress2],
+ [BusinessAddress3],
+ [BusinessCountry],
+ [BusinessTaxNumber],
+ [BillingEmail],
+ [Plan],
+ [PlanType],
+ [Seats],
+ [MaxCollections],
+ [UsePolicies],
+ [UseSso],
+ [UseGroups],
+ [UseDirectory],
+ [UseEvents],
+ [UseTotp],
+ [Use2fa],
+ [UseApi],
+ [UseResetPassword],
+ [SelfHost],
+ [UsersGetPremium],
+ [Storage],
+ [MaxStorageGb],
+ [Gateway],
+ [GatewayCustomerId],
+ [GatewaySubscriptionId],
+ [ReferenceData],
+ [Enabled],
+ [LicenseKey],
+ [ApiKey],
+ [PublicKey],
+ [PrivateKey],
+ [TwoFactorProviders],
+ [ExpirationDate],
+ [CreationDate],
+ [RevisionDate],
+ [OwnersNotifiedOfAutoscaling],
+ [MaxAutoscaleSeats],
+ [UseKeyConnector]
+ )
+ VALUES
+ (
+ @Id,
+ @Identifier,
+ @Name,
+ @BusinessName,
+ @BusinessAddress1,
+ @BusinessAddress2,
+ @BusinessAddress3,
+ @BusinessCountry,
+ @BusinessTaxNumber,
+ @BillingEmail,
+ @Plan,
+ @PlanType,
+ @Seats,
+ @MaxCollections,
+ @UsePolicies,
+ @UseSso,
+ @UseGroups,
+ @UseDirectory,
+ @UseEvents,
+ @UseTotp,
+ @Use2fa,
+ @UseApi,
+ @UseResetPassword,
+ @SelfHost,
+ @UsersGetPremium,
+ @Storage,
+ @MaxStorageGb,
+ @Gateway,
+ @GatewayCustomerId,
+ @GatewaySubscriptionId,
+ @ReferenceData,
+ @Enabled,
+ @LicenseKey,
+ @ApiKey,
+ @PublicKey,
+ @PrivateKey,
+ @TwoFactorProviders,
+ @ExpirationDate,
+ @CreationDate,
+ @RevisionDate,
+ @OwnersNotifiedOfAutoscaling,
+ @MaxAutoscaleSeats,
+ @UseKeyConnector
+ )
+END
+GO
+
+IF OBJECT_ID('[dbo].[Organization_ReadAbilities]') IS NOT NULL
+ BEGIN
+ DROP PROCEDURE [dbo].[Organization_ReadAbilities]
+ END
+GO
+
+CREATE PROCEDURE [dbo].[Organization_ReadAbilities]
+AS
+BEGIN
+ SET NOCOUNT ON
+
+ SELECT
+ [Id],
+ [UseEvents],
+ [Use2fa],
+ CASE
+ WHEN [Use2fa] = 1 AND [TwoFactorProviders] IS NOT NULL AND [TwoFactorProviders] != '{}' THEN
+ 1
+ ELSE
+ 0
+ END AS [Using2fa],
+ [UsersGetPremium],
+ [UseSso],
+ [UseKeyConnector],
+ [UseResetPassword],
+ [Enabled]
+ FROM
+ [dbo].[Organization]
+END
+GO
+
+IF OBJECT_ID('[dbo].[Organization_Update]') IS NOT NULL
+ BEGIN
+ DROP PROCEDURE [dbo].[Organization_Update]
+ END
+GO
+
+CREATE PROCEDURE [dbo].[Organization_Update]
+ @Id UNIQUEIDENTIFIER,
+ @Identifier NVARCHAR(50),
+ @Name NVARCHAR(50),
+ @BusinessName NVARCHAR(50),
+ @BusinessAddress1 NVARCHAR(50),
+ @BusinessAddress2 NVARCHAR(50),
+ @BusinessAddress3 NVARCHAR(50),
+ @BusinessCountry VARCHAR(2),
+ @BusinessTaxNumber NVARCHAR(30),
+ @BillingEmail NVARCHAR(256),
+ @Plan NVARCHAR(50),
+ @PlanType TINYINT,
+ @Seats INT,
+ @MaxCollections SMALLINT,
+ @UsePolicies BIT,
+ @UseSso BIT,
+ @UseGroups BIT,
+ @UseDirectory BIT,
+ @UseEvents BIT,
+ @UseTotp BIT,
+ @Use2fa BIT,
+ @UseApi BIT,
+ @UseResetPassword BIT,
+ @SelfHost BIT,
+ @UsersGetPremium BIT,
+ @Storage BIGINT,
+ @MaxStorageGb SMALLINT,
+ @Gateway TINYINT,
+ @GatewayCustomerId VARCHAR(50),
+ @GatewaySubscriptionId VARCHAR(50),
+ @ReferenceData VARCHAR(MAX),
+ @Enabled BIT,
+ @LicenseKey VARCHAR(100),
+ @ApiKey VARCHAR(30),
+ @PublicKey VARCHAR(MAX),
+ @PrivateKey VARCHAR(MAX),
+ @TwoFactorProviders NVARCHAR(MAX),
+ @ExpirationDate DATETIME2(7),
+ @CreationDate DATETIME2(7),
+ @RevisionDate DATETIME2(7),
+ @OwnersNotifiedOfAutoscaling DATETIME2(7),
+ @MaxAutoscaleSeats INT,
+ @UseKeyConnector BIT = 0
+AS
+BEGIN
+ SET NOCOUNT ON
+
+ UPDATE
+ [dbo].[Organization]
+ SET
+ [Identifier] = @Identifier,
+ [Name] = @Name,
+ [BusinessName] = @BusinessName,
+ [BusinessAddress1] = @BusinessAddress1,
+ [BusinessAddress2] = @BusinessAddress2,
+ [BusinessAddress3] = @BusinessAddress3,
+ [BusinessCountry] = @BusinessCountry,
+ [BusinessTaxNumber] = @BusinessTaxNumber,
+ [BillingEmail] = @BillingEmail,
+ [Plan] = @Plan,
+ [PlanType] = @PlanType,
+ [Seats] = @Seats,
+ [MaxCollections] = @MaxCollections,
+ [UsePolicies] = @UsePolicies,
+ [UseSso] = @UseSso,
+ [UseGroups] = @UseGroups,
+ [UseDirectory] = @UseDirectory,
+ [UseEvents] = @UseEvents,
+ [UseTotp] = @UseTotp,
+ [Use2fa] = @Use2fa,
+ [UseApi] = @UseApi,
+ [UseResetPassword] = @UseResetPassword,
+ [SelfHost] = @SelfHost,
+ [UsersGetPremium] = @UsersGetPremium,
+ [Storage] = @Storage,
+ [MaxStorageGb] = @MaxStorageGb,
+ [Gateway] = @Gateway,
+ [GatewayCustomerId] = @GatewayCustomerId,
+ [GatewaySubscriptionId] = @GatewaySubscriptionId,
+ [ReferenceData] = @ReferenceData,
+ [Enabled] = @Enabled,
+ [LicenseKey] = @LicenseKey,
+ [ApiKey] = @ApiKey,
+ [PublicKey] = @PublicKey,
+ [PrivateKey] = @PrivateKey,
+ [TwoFactorProviders] = @TwoFactorProviders,
+ [ExpirationDate] = @ExpirationDate,
+ [CreationDate] = @CreationDate,
+ [RevisionDate] = @RevisionDate,
+ [OwnersNotifiedOfAutoscaling] = @OwnersNotifiedOfAutoscaling,
+ [MaxAutoscaleSeats] = @MaxAutoscaleSeats,
+ [UseKeyConnector] = @UseKeyConnector
+ WHERE
+ [Id] = @Id
+END
+GO
+
+IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'OrganizationUserOrganizationDetailsView')
+ BEGIN
+ DROP VIEW [dbo].[OrganizationUserOrganizationDetailsView]
+ END
+GO
+
+CREATE VIEW [dbo].[OrganizationUserOrganizationDetailsView]
+AS
+SELECT
+ OU.[UserId],
+ OU.[OrganizationId],
+ O.[Name],
+ O.[Enabled],
+ O.[UsePolicies],
+ O.[UseSso],
+ O.[UseKeyConnector],
+ O.[UseGroups],
+ O.[UseDirectory],
+ O.[UseEvents],
+ O.[UseTotp],
+ O.[Use2fa],
+ O.[UseApi],
+ O.[UseResetPassword],
+ O.[SelfHost],
+ O.[UsersGetPremium],
+ O.[Seats],
+ O.[MaxCollections],
+ O.[MaxStorageGb],
+ O.[Identifier],
+ OU.[Key],
+ OU.[ResetPasswordKey],
+ O.[PublicKey],
+ O.[PrivateKey],
+ OU.[Status],
+ OU.[Type],
+ SU.[ExternalId] SsoExternalId,
+ OU.[Permissions],
+ PO.[ProviderId],
+ P.[Name] ProviderName,
+ SS.[Data] SsoConfig
+FROM
+ [dbo].[OrganizationUser] OU
+INNER JOIN
+ [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId]
+LEFT JOIN
+ [dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId]
+LEFT JOIN
+ [dbo].[ProviderOrganization] PO ON PO.[OrganizationId] = O.[Id]
+LEFT JOIN
+ [dbo].[Provider] P ON P.[Id] = PO.[ProviderId]
+LEFT JOIN
+ [dbo].[SsoConfig] SS ON SS.[OrganizationId] = OU.[OrganizationId]
+GO
+
+IF OBJECT_ID('[dbo].[ProviderUserProviderOrganizationDetailsView]') IS NOT NULL
+ BEGIN
+ DROP VIEW [dbo].[ProviderUserProviderOrganizationDetailsView]
+ END
+GO
+
+CREATE VIEW [dbo].[ProviderUserProviderOrganizationDetailsView]
+AS
+SELECT
+ PU.[UserId],
+ PO.[OrganizationId],
+ O.[Name],
+ O.[Enabled],
+ O.[UsePolicies],
+ O.[UseSso],
+ O.[UseKeyConnector],
+ O.[UseGroups],
+ O.[UseDirectory],
+ O.[UseEvents],
+ O.[UseTotp],
+ O.[Use2fa],
+ O.[UseApi],
+ O.[UseResetPassword],
+ O.[SelfHost],
+ O.[UsersGetPremium],
+ 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
+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]
diff --git a/util/MySqlMigrations/Migrations/20211115145402_KeyConnectorFlag.Designer.cs b/util/MySqlMigrations/Migrations/20211115145402_KeyConnectorFlag.Designer.cs
new file mode 100644
index 0000000000..287ac5bdac
--- /dev/null
+++ b/util/MySqlMigrations/Migrations/20211115145402_KeyConnectorFlag.Designer.cs
@@ -0,0 +1,1498 @@
+//
+using System;
+using Bit.Core.Repositories.EntityFramework;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace Bit.MySqlMigrations.Migrations
+{
+ [DbContext(typeof(DatabaseContext))]
+ [Migration("20211115145402_KeyConnectorFlag")]
+ partial class KeyConnectorFlag
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("Relational:MaxIdentifierLength", 64)
+ .HasAnnotation("ProductVersion", "5.0.9");
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.Cipher", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ 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("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");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.Collection", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("ExternalId")
+ .HasMaxLength(300)
+ .HasColumnType("varchar(300)");
+
+ b.Property("Name")
+ .HasColumnType("longtext");
+
+ b.Property("OrganizationId")
+ .HasColumnType("char(36)");
+
+ b.Property("RevisionDate")
+ .HasColumnType("datetime(6)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrganizationId");
+
+ b.ToTable("Collection");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.CollectionCipher", b =>
+ {
+ b.Property("CollectionId")
+ .HasColumnType("char(36)");
+
+ b.Property("CipherId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("CollectionId", "CipherId");
+
+ b.HasIndex("CipherId");
+
+ b.ToTable("CollectionCipher");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.CollectionGroup", b =>
+ {
+ b.Property("CollectionId")
+ .HasColumnType("char(36)");
+
+ b.Property("GroupId")
+ .HasColumnType("char(36)");
+
+ b.Property("HidePasswords")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("ReadOnly")
+ .HasColumnType("tinyint(1)");
+
+ b.HasKey("CollectionId", "GroupId");
+
+ b.HasIndex("GroupId");
+
+ b.ToTable("CollectionGroups");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.CollectionUser", b =>
+ {
+ b.Property("CollectionId")
+ .HasColumnType("char(36)");
+
+ b.Property("OrganizationUserId")
+ .HasColumnType("char(36)");
+
+ b.Property("HidePasswords")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("ReadOnly")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UserId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("CollectionId", "OrganizationUserId");
+
+ b.HasIndex("OrganizationUserId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("CollectionUsers");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.Device", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("char(36)");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Identifier")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("Name")
+ .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("UserId");
+
+ b.ToTable("Device");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.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");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.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("GroupId")
+ .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("ProviderId")
+ .HasColumnType("char(36)");
+
+ b.Property("ProviderOrganizationId")
+ .HasColumnType("char(36)");
+
+ b.Property("ProviderUserId")
+ .HasColumnType("char(36)");
+
+ b.Property("Type")
+ .HasColumnType("int");
+
+ b.Property("UserId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Event");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.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");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.Grant", b =>
+ {
+ b.Property("Key")
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("ClientId")
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("ConsumedDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Data")
+ .HasColumnType("longtext");
+
+ b.Property("Description")
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("ExpirationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("SessionId")
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("SubjectId")
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("Type")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.HasKey("Key");
+
+ b.ToTable("Grant");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.Group", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("AccessAll")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("ExternalId")
+ .HasMaxLength(300)
+ .HasColumnType("varchar(300)");
+
+ b.Property("Name")
+ .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");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.GroupUser", b =>
+ {
+ b.Property("GroupId")
+ .HasColumnType("char(36)");
+
+ b.Property("OrganizationUserId")
+ .HasColumnType("char(36)");
+
+ b.Property("UserId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("GroupId", "OrganizationUserId");
+
+ b.HasIndex("OrganizationUserId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("GroupUser");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.Installation", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("Enabled")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Key")
+ .HasMaxLength(150)
+ .HasColumnType("varchar(150)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Installation");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.Organization", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("ApiKey")
+ .HasMaxLength(30)
+ .HasColumnType("varchar(30)");
+
+ b.Property("BillingEmail")
+ .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("MaxAutoscaleSeats")
+ .HasColumnType("int");
+
+ b.Property("MaxCollections")
+ .HasColumnType("smallint");
+
+ b.Property("MaxStorageGb")
+ .HasColumnType("smallint");
+
+ b.Property("Name")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("OwnersNotifiedOfAutoscaling")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Plan")
+ .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("Storage")
+ .HasColumnType("bigint");
+
+ b.Property("TwoFactorProviders")
+ .HasColumnType("longtext");
+
+ b.Property("Use2fa")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseApi")
+ .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("UsePolicies")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseResetPassword")
+ .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.ToTable("Organization");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.OrganizationUser", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("AccessAll")
+ .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("tinyint unsigned");
+
+ b.Property("Type")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("UserId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrganizationId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("OrganizationUser");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.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");
+
+ b.ToTable("Policy");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.Provider", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("BillingEmail")
+ .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("Enabled")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Name")
+ .HasColumnType("longtext");
+
+ b.Property("RevisionDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Status")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("UseEvents")
+ .HasColumnType("tinyint(1)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Provider");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.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");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.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");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.Send", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("AccessCount")
+ .HasColumnType("int");
+
+ 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("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("OrganizationId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("Send");
+ });
+
+ modelBuilder.Entity("Bit.Core.Models.EntityFramework.SsoConfig", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ 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