From f866b25e43ed07a70bb76f62c74502f592c18f7e Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Wed, 17 Nov 2021 11:46:35 +0100 Subject: [PATCH] Key Connector feature toggle (#1716) --- src/Admin/Models/OrganizationEditModel.cs | 4 + src/Admin/Views/Organizations/Edit.cshtml | 4 + .../Controllers/OrganizationsController.cs | 4 +- .../CustomTokenRequestValidator.cs | 2 +- .../OrganizationSsoRequestModel.cs | 4 +- .../Api/Response/OrganizationResponseModel.cs | 2 + .../ProfileOrganizationResponseModel.cs | 6 +- ...rofileProviderOrganizationResponseModel.cs | 1 + .../Models/Business/OrganizationLicense.cs | 28 +- src/Core/Models/Data/OrganizationAbility.cs | 2 + .../OrganizationUserOrganizationDetails.cs | 1 + .../ProviderUserOrganizationDetails.cs | 1 + src/Core/Models/Data/SsoConfigurationData.cs | 2 +- src/Core/Models/StaticStore/Plan.cs | 1 + src/Core/Models/Table/Organization.cs | 1 + .../EntityFramework/OrganizationRepository.cs | 1 + ...izationUserOrganizationDetailsViewQuery.cs | 1 + ...roviderUserOrganizationDetailsViewQuery.cs | 1 + src/Core/Services/ISsoConfigService.cs | 2 +- .../Implementations/OrganizationService.cs | 25 +- .../Services/Implementations/PolicyService.cs | 2 +- .../Implementations/SsoConfigService.cs | 21 +- .../Services/Implementations/UserService.cs | 8 +- src/Core/Utilities/StaticStore.cs | 2 + .../Stored Procedures/Organization_Create.sql | 9 +- .../Organization_ReadAbilities.sql | 1 + .../Stored Procedures/Organization_Update.sql | 6 +- src/Sql/dbo/Tables/Organization.sql | 1 + ...rganizationUserOrganizationDetailsView.sql | 1 + ...derUserProviderOrganizationDetailsView.sql | 1 + .../OrganizationsControllerTests.cs | 6 +- .../EqualityComparers/OrganizationCompare.cs | 1 + .../Services/OrganizationServiceTests.cs | 2 +- test/Core.Test/Services/PolicyServiceTests.cs | 2 +- .../Services/SsoConfigServiceTests.cs | 153 +- .../2021-11-12_00_KeyConnectorFlag.sql | 420 +++++ ...0211115145402_KeyConnectorFlag.Designer.cs | 1498 ++++++++++++++++ .../20211115145402_KeyConnectorFlag.cs | 24 + .../DatabaseContextModelSnapshot.cs | 3 + util/MySqlMigrations/MySqlMigrations.csproj | 1 + .../2021-11-12_00_KeyConnectorFlag.sql | Bin 0 -> 492 bytes ...0211115142623_KeyConnectorFlag.Designer.cs | 1507 +++++++++++++++++ .../20211115142623_KeyConnectorFlag.cs | 24 + .../DatabaseContextModelSnapshot.cs | 3 + .../2021-11-12_00_KeyConnectorFlag.psql | Bin 0 -> 486 bytes 45 files changed, 3720 insertions(+), 69 deletions(-) create mode 100644 util/Migrator/DbScripts/2021-11-12_00_KeyConnectorFlag.sql create mode 100644 util/MySqlMigrations/Migrations/20211115145402_KeyConnectorFlag.Designer.cs create mode 100644 util/MySqlMigrations/Migrations/20211115145402_KeyConnectorFlag.cs create mode 100644 util/MySqlMigrations/Scripts/2021-11-12_00_KeyConnectorFlag.sql create mode 100644 util/PostgresMigrations/Migrations/20211115142623_KeyConnectorFlag.Designer.cs create mode 100644 util/PostgresMigrations/Migrations/20211115142623_KeyConnectorFlag.cs create mode 100644 util/PostgresMigrations/Scripts/2021-11-12_00_KeyConnectorFlag.psql 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("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Rate") + .HasColumnType("decimal(65,30)"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Transaction", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PaymentMethodType") + .HasColumnType("tinyint unsigned"); + + b.Property("Refunded") + .HasColumnType("tinyint(1)"); + + b.Property("RefundedAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.U2f", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AppId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Challenge") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("KeyHandle") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("Version") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("U2f"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.User", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccountRevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailVerified") + .HasColumnType("tinyint(1)"); + + b.Property("EquivalentDomains") + .HasColumnType("longtext"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("longtext"); + + b.Property("ForcePasswordReset") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Kdf") + .HasColumnType("tinyint unsigned"); + + b.Property("KdfIterations") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Premium") + .HasColumnType("tinyint(1)"); + + b.Property("PremiumExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RenewalReminderDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Cipher", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Collection", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.CollectionCipher", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.CollectionGroup", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.CollectionUser", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Device", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.EmergencyAccess", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Folder", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Group", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.GroupUser", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.OrganizationUser", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Policy", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderOrganization", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderUser", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Send", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.SsoConfig", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.SsoUser", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Transaction", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.U2f", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany("U2fs") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Organization", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + + b.Navigation("U2fs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20211115145402_KeyConnectorFlag.cs b/util/MySqlMigrations/Migrations/20211115145402_KeyConnectorFlag.cs new file mode 100644 index 0000000000..d68eb65a17 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20211115145402_KeyConnectorFlag.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Bit.MySqlMigrations.Migrations +{ + public partial class KeyConnectorFlag : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UseKeyConnector", + table: "Organization", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "UseKeyConnector", + table: "Organization"); + } + } +} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index ce4df31bda..50709984d4 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -565,6 +565,9 @@ namespace Bit.MySqlMigrations.Migrations b.Property("UseGroups") .HasColumnType("tinyint(1)"); + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + b.Property("UsePolicies") .HasColumnType("tinyint(1)"); diff --git a/util/MySqlMigrations/MySqlMigrations.csproj b/util/MySqlMigrations/MySqlMigrations.csproj index d197ece0cf..277d928c3a 100644 --- a/util/MySqlMigrations/MySqlMigrations.csproj +++ b/util/MySqlMigrations/MySqlMigrations.csproj @@ -2,6 +2,7 @@ net5.0 + 9f1cd3e0-70f2-4921-8068-b2538fd7c3f7 diff --git a/util/MySqlMigrations/Scripts/2021-11-12_00_KeyConnectorFlag.sql b/util/MySqlMigrations/Scripts/2021-11-12_00_KeyConnectorFlag.sql new file mode 100644 index 0000000000000000000000000000000000000000..9de11d2a233e11cb982df666a1289e28a59b23a1 GIT binary patch literal 492 zcmZ{hPfG$(6vfXv=sUP`20}D)5v?X0X=t3nQMVc?Lm)HY*up-1^*b*kA>wl1yMNC; z_r1@LQZpr*a(2)ucA=7UgqP_N??&f33z{e=no{N3M7Q`U_KLclDm|;#tKPZy+zsA} zY@$2FRdco4=m9-|?|hB@LUlvGYu#6q^|@E1fnuTs_~uuDkRu)NQjI~j?~Lsn-suV3 zENo)7uCO-)$2^vZ`zOq)nEcDP)jhHH$mkF_MVx;tGp&hV1iU9O8vM7g%YvPDVEnv5 z9Numb7|xkn(i5qxOZCygQHI=C8lsLa|9z#gw(za!_v|hmJvw^2#*R<6AF=BM$=Le) G2fqOGuS*&L literal 0 HcmV?d00001 diff --git a/util/PostgresMigrations/Migrations/20211115142623_KeyConnectorFlag.Designer.cs b/util/PostgresMigrations/Migrations/20211115142623_KeyConnectorFlag.Designer.cs new file mode 100644 index 0000000000..63628cfe1d --- /dev/null +++ b/util/PostgresMigrations/Migrations/20211115142623_KeyConnectorFlag.Designer.cs @@ -0,0 +1,1507 @@ +// +using System; +using Bit.Core.Repositories.EntityFramework; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace Bit.PostgresMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20211115142623_KeyConnectorFlag")] + partial class KeyConnectorFlag + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") + .HasAnnotation("Relational:MaxIdentifierLength", 63) + .HasAnnotation("ProductVersion", "5.0.9") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Cipher", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Favorites") + .HasColumnType("text"); + + b.Property("Folders") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Reprompt") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Collection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.Property("UserId") + .HasColumnType("uuid"); + + 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("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GranteeId") + .HasColumnType("uuid"); + + b.Property("GrantorId") + .HasColumnType("uuid"); + + b.Property("KeyEncrypted") + .HasColumnType("text"); + + b.Property("LastNotificationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("WaitTimeDays") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ActingUserId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp without time zone"); + + b.Property("DeviceType") + .HasColumnType("smallint"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("PolicyId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("ProviderOrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderUserId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Event"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Folder", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedDate") + .HasColumnType("timestamp without time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Group", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + 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("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("timestamp without time zone"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("SelfHost") + .HasColumnType("boolean"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("Use2fa") + .HasColumnType("boolean"); + + b.Property("UseApi") + .HasColumnType("boolean"); + + b.Property("UseDirectory") + .HasColumnType("boolean"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.Property("UseGroups") + .HasColumnType("boolean"); + + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + + b.Property("UsePolicies") + .HasColumnType("boolean"); + + b.Property("UseResetPassword") + .HasColumnType("boolean"); + + b.Property("UseSso") + .HasColumnType("boolean"); + + b.Property("UseTotp") + .HasColumnType("boolean"); + + b.Property("UsersGetPremium") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Organization"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Policy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.Provider", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasColumnType("text"); + + b.Property("BusinessAddress1") + .HasColumnType("text"); + + b.Property("BusinessAddress2") + .HasColumnType("text"); + + b.Property("BusinessAddress3") + .HasColumnType("text"); + + b.Property("BusinessCountry") + .HasColumnType("text"); + + b.Property("BusinessName") + .HasColumnType("text"); + + b.Property("BusinessTaxNumber") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Provider"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Settings") + .HasColumnType("text"); + + 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("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Send", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCount") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("HideEmail") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MaxAccessCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + 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") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Rate") + .HasColumnType("numeric"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Transaction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PaymentMethodType") + .HasColumnType("smallint"); + + b.Property("Refunded") + .HasColumnType("boolean"); + + b.Property("RefundedAmount") + .HasColumnType("numeric"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.U2f", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AppId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Challenge") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("KeyHandle") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("U2f"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.User", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccountRevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("EquivalentDomains") + .HasColumnType("text"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("text"); + + b.Property("ForcePasswordReset") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Kdf") + .HasColumnType("smallint"); + + b.Property("KdfIterations") + .HasColumnType("integer"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Premium") + .HasColumnType("boolean"); + + b.Property("PremiumExpirationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RenewalReminderDate") + .HasColumnType("timestamp without time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Cipher", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Collection", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.CollectionCipher", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.CollectionGroup", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.CollectionUser", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Device", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.EmergencyAccess", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Folder", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Group", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.GroupUser", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.OrganizationUser", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Policy", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderOrganization", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderUser", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Send", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.SsoConfig", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.SsoUser", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Transaction", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.U2f", b => + { + b.HasOne("Bit.Core.Models.EntityFramework.User", "User") + .WithMany("U2fs") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.Organization", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Core.Models.EntityFramework.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + + b.Navigation("U2fs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20211115142623_KeyConnectorFlag.cs b/util/PostgresMigrations/Migrations/20211115142623_KeyConnectorFlag.cs new file mode 100644 index 0000000000..edc5220861 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20211115142623_KeyConnectorFlag.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Bit.PostgresMigrations.Migrations +{ + public partial class KeyConnectorFlag : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UseKeyConnector", + table: "Organization", + type: "boolean", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "UseKeyConnector", + table: "Organization"); + } + } +} diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index 51d27d5395..1c4b70b115 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -569,6 +569,9 @@ namespace Bit.PostgresMigrations.Migrations b.Property("UseGroups") .HasColumnType("boolean"); + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + b.Property("UsePolicies") .HasColumnType("boolean"); diff --git a/util/PostgresMigrations/Scripts/2021-11-12_00_KeyConnectorFlag.psql b/util/PostgresMigrations/Scripts/2021-11-12_00_KeyConnectorFlag.psql new file mode 100644 index 0000000000000000000000000000000000000000..a65e3498691c9732b7fd3ac07219d1ab48d90783 GIT binary patch literal 486 zcmZ{hTWi8V5QWdP;C~3d#6S_P7Z9IPjWje?F~(O(wMd~DF!sUz_||W>76p-Ib}oDN z%$eQapHvHlnzMV@ocL8K`;aWwlx(1?D$YVBR3lJ^4fRAG5!b-&wNm6h6N?iRM^`#y zH-qUkgk=kOOu>p}_b2Gv((|IU)HAj6tmWv`)Y)&5u{P8n9N!F%g8a)(u|)SCj{n&M z_i9V0Fr#nEow|HIs)@DEtBdcD_pmnl?!C{Lcga!hyWYOKYQWP_pGt5w{g7EFteC0) G{_X_kvq%2` literal 0 HcmV?d00001