1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-04 20:50:21 -05:00

Key Connector feature toggle (#1716)

This commit is contained in:
Oscar Hinton 2021-11-17 11:46:35 +01:00 committed by GitHub
parent 3a22f91ff5
commit f866b25e43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 3720 additions and 69 deletions

View File

@ -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;

View File

@ -215,6 +215,10 @@
<input type="checkbox" class="form-check-input" asp-for="UseSso">
<label class="form-check-label" asp-for="UseSso"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseKeyConnector">
<label class="form-check-label" asp-for="UseKeyConnector"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseDirectory">
<label class="form-check-label" asp-for="UseDirectory"></label>

View File

@ -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);
}

View File

@ -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

View File

@ -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,

View File

@ -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; }

View File

@ -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; }
}
}

View File

@ -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;

View File

@ -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);
/// <summary>
/// Represents the current version of the license format. Should be updated whenever new fields are added.
/// </summary>
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

View File

@ -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; }
}
}

View File

@ -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; }

View File

@ -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; }

View File

@ -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

View File

@ -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; }

View File

@ -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; }

View File

@ -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();
}
}

View File

@ -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,

View File

@ -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,

View File

@ -5,6 +5,6 @@ namespace Bit.Core.Services
{
public interface ISsoConfigService
{
Task SaveAsync(SsoConfig config);
Task SaveAsync(SsoConfig config, Organization organization);
}
}

View File

@ -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.");
}

View File

@ -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.");
}

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -15,6 +15,7 @@ BEGIN
END AS [Using2fa],
[UsersGetPremium],
[UseSso],
[UseKeyConnector],
[UseResetPassword],
[Enabled]
FROM

View File

@ -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

View File

@ -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)
);

View File

@ -7,6 +7,7 @@ SELECT
O.[Enabled],
O.[UsePolicies],
O.[UseSso],
O.[UseKeyConnector],
O.[UseGroups],
O.[UseDirectory],
O.[UseEvents],

View File

@ -7,6 +7,7 @@ SELECT
O.[Enabled],
O.[UsePolicies],
O.[UseSso],
O.[UseKeyConnector],
O.[UseGroups],
O.[UseDirectory],
O.[UseEvents],

View File

@ -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,
};

View File

@ -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) &&

View File

@ -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<ISsoConfigRepository>();
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
var applicationCacheService = sutProvider.GetDependency<IApplicationCacheService>();

View File

@ -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<ISsoConfigRepository>()

View File

@ -15,9 +15,9 @@ namespace Bit.Core.Test.Services
public class SsoConfigServiceTests
{
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_ExistingItem_UpdatesRevisionDateOnly(SutProvider<SsoConfigService> sutProvider)
public async Task SaveAsync_ExistingItem_UpdatesRevisionDateOnly(SutProvider<SsoConfigService> 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<ISsoConfigRepository>()
.UpsertAsync(ssoConfig).Returns(Task.CompletedTask);
await sutProvider.Sut.SaveAsync(ssoConfig);
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
await sutProvider.GetDependency<ISsoConfigRepository>().Received()
.UpsertAsync(ssoConfig);
@ -43,7 +43,8 @@ namespace Bit.Core.Test.Services
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_NewItem_UpdatesCreationAndRevisionDate(SutProvider<SsoConfigService> sutProvider)
public async Task SaveAsync_NewItem_UpdatesCreationAndRevisionDate(SutProvider<SsoConfigService> 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<ISsoConfigRepository>()
.UpsertAsync(ssoConfig).Returns(Task.CompletedTask);
await sutProvider.Sut.SaveAsync(ssoConfig);
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
await sutProvider.GetDependency<ISsoConfigRepository>().Received()
.UpsertAsync(ssoConfig);
@ -70,16 +71,20 @@ namespace Bit.Core.Test.Services
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_PreventDisablingKeyConnector(SutProvider<SsoConfigService> sutProvider, Guid orgId)
public async Task SaveAsync_PreventDisablingKeyConnector(SutProvider<SsoConfigService> 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<ISsoConfigRepository>();
ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(oldSsoConfig);
ssoConfigRepository.GetByOrganizationIdAsync(organization.Id).Returns(oldSsoConfig);
ssoConfigRepository.UpsertAsync(newSsoConfig).Returns(Task.CompletedTask);
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyDetailsByOrganizationAsync(orgId)
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyDetailsByOrganizationAsync(organization.Id)
.Returns(new[] { new OrganizationUserUserDetails { UsesKeyConnector = true } });
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => 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<SsoConfigService> sutProvider, Guid orgId)
SutProvider<SsoConfigService> 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<ISsoConfigRepository>();
ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(oldSsoConfig);
ssoConfigRepository.GetByOrganizationIdAsync(organization.Id).Returns(oldSsoConfig);
ssoConfigRepository.UpsertAsync(newSsoConfig).Returns(Task.CompletedTask);
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyDetailsByOrganizationAsync(orgId)
sutProvider.GetDependency<IOrganizationUserRepository>().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<SsoConfigService> sutProvider)
public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled_Throws(SutProvider<SsoConfigService> 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<BadRequestException>(
() => 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<SsoConfigService> sutProvider)
public async Task SaveAsync_KeyConnector_SsoPolicyNotEnabled_Throws(SutProvider<SsoConfigService> 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<BadRequestException>(
() => 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<SsoConfigService> sutProvider)
public async Task SaveAsync_KeyConnector_SsoConfigNotEnabled_Throws(SutProvider<SsoConfigService> 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<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig));
() => sutProvider.Sut.SaveAsync(ssoConfig, organization));
Assert.Contains("You must enable SSO to use Key Connector.", exception.Message);
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_KeyConnector_KeyConnectorAbilityNotEnabled_Throws(SutProvider<SsoConfigService> 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<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Arg.Any<Enums.PolicyType>()).Returns(new Policy
{
Enabled = true,
});
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization));
Assert.Contains("Organization cannot use Key Connector.", exception.Message);
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_KeyConnector_Success(SutProvider<SsoConfigService> 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<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Arg.Any<Enums.PolicyType>()).Returns(new Policy
{
Enabled = true,
});
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
await sutProvider.GetDependency<ISsoConfigRepository>().ReceivedWithAnyArgs()
.UpsertAsync(default);
}
}
}

View File

@ -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]

File diff suppressed because it is too large Load Diff

View File

@ -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<bool>(
name: "UseKeyConnector",
table: "Organization",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "UseKeyConnector",
table: "Organization");
}
}
}

View File

@ -565,6 +565,9 @@ namespace Bit.MySqlMigrations.Migrations
b.Property<bool>("UseGroups")
.HasColumnType("tinyint(1)");
b.Property<bool>("UseKeyConnector")
.HasColumnType("tinyint(1)");
b.Property<bool>("UsePolicies")
.HasColumnType("tinyint(1)");

View File

@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<UserSecretsId>9f1cd3e0-70f2-4921-8068-b2538fd7c3f7</UserSecretsId>
</PropertyGroup>
<ItemGroup>

File diff suppressed because it is too large Load Diff

View File

@ -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<bool>(
name: "UseKeyConnector",
table: "Organization",
type: "boolean",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "UseKeyConnector",
table: "Organization");
}
}
}

View File

@ -569,6 +569,9 @@ namespace Bit.PostgresMigrations.Migrations
b.Property<bool>("UseGroups")
.HasColumnType("boolean");
b.Property<bool>("UseKeyConnector")
.HasColumnType("boolean");
b.Property<bool>("UsePolicies")
.HasColumnType("boolean");