mirror of
https://github.com/bitwarden/server.git
synced 2025-07-01 08:02:49 -05:00
Remove Business Portal (#1614)
This commit is contained in:
@ -10,9 +10,7 @@ using System.Security.Claims;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
|
||||
namespace Bit.Core.Context
|
||||
{
|
||||
@ -274,13 +272,6 @@ namespace Bit.Core.Context
|
||||
return Task.FromResult(Organizations?.Any(o => o.Id == orgId && o.Type == OrganizationUserType.Custom) ?? false);
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> AccessBusinessPortal(Guid orgId)
|
||||
{
|
||||
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
|
||||
&& (o.Permissions?.AccessBusinessPortal ?? false)) ?? false);
|
||||
}
|
||||
|
||||
public async Task<bool> AccessEventLogs(Guid orgId)
|
||||
{
|
||||
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
|
||||
@ -456,7 +447,6 @@ namespace Bit.Core.Context
|
||||
|
||||
return new Permissions
|
||||
{
|
||||
AccessBusinessPortal = hasClaim("accessbusinessportal"),
|
||||
AccessEventLogs = hasClaim("accesseventlogs"),
|
||||
AccessImportExport = hasClaim("accessimportexport"),
|
||||
AccessReports = hasClaim("accessreports"),
|
||||
|
@ -36,7 +36,6 @@ namespace Bit.Core.Context
|
||||
Task<bool> OrganizationAdmin(Guid orgId);
|
||||
Task<bool> OrganizationOwner(Guid orgId);
|
||||
Task<bool> OrganizationCustom(Guid orgId);
|
||||
Task<bool> AccessBusinessPortal(Guid orgId);
|
||||
Task<bool> AccessEventLogs(Guid orgId);
|
||||
Task<bool> AccessImportExport(Guid orgId);
|
||||
Task<bool> AccessReports(Guid orgId);
|
||||
|
@ -0,0 +1,239 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Collections.Generic;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Sso;
|
||||
using U2F.Core.Utils;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using Bit.Core.Models.Table;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class OrganizationSsoRequestModel
|
||||
{
|
||||
[Required]
|
||||
public bool Enabled { get; set; }
|
||||
[Required]
|
||||
public SsoConfigurationDataRequest Data { get; set; }
|
||||
|
||||
public SsoConfig ToSsoConfig(Guid organizationId)
|
||||
{
|
||||
return ToSsoConfig(new SsoConfig { OrganizationId = organizationId });
|
||||
}
|
||||
|
||||
public SsoConfig ToSsoConfig(SsoConfig existingConfig)
|
||||
{
|
||||
existingConfig.Enabled = Enabled;
|
||||
var configurationData = Data.ToConfigurationData();
|
||||
existingConfig.Data = JsonSerializer.Serialize(configurationData, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
});
|
||||
return existingConfig;
|
||||
}
|
||||
}
|
||||
|
||||
public class SsoConfigurationDataRequest : IValidatableObject
|
||||
{
|
||||
public SsoConfigurationDataRequest() {}
|
||||
|
||||
public SsoConfigurationDataRequest(SsoConfigurationData configurationData)
|
||||
{
|
||||
ConfigType = configurationData.ConfigType;
|
||||
Authority = configurationData.Authority;
|
||||
ClientId = configurationData.ClientId;
|
||||
ClientSecret = configurationData.ClientSecret;
|
||||
MetadataAddress = configurationData.MetadataAddress;
|
||||
RedirectBehavior = configurationData.RedirectBehavior;
|
||||
GetClaimsFromUserInfoEndpoint = configurationData.GetClaimsFromUserInfoEndpoint;
|
||||
IdpEntityId = configurationData.IdpEntityId;
|
||||
IdpBindingType = configurationData.IdpBindingType;
|
||||
IdpSingleSignOnServiceUrl = configurationData.IdpSingleSignOnServiceUrl;
|
||||
IdpSingleLogoutServiceUrl = configurationData.IdpSingleLogoutServiceUrl;
|
||||
IdpArtifactResolutionServiceUrl = configurationData.IdpArtifactResolutionServiceUrl;
|
||||
IdpX509PublicCert = configurationData.IdpX509PublicCert;
|
||||
IdpOutboundSigningAlgorithm = configurationData.IdpOutboundSigningAlgorithm;
|
||||
IdpAllowUnsolicitedAuthnResponse = configurationData.IdpAllowUnsolicitedAuthnResponse;
|
||||
IdpDisableOutboundLogoutRequests = configurationData.IdpDisableOutboundLogoutRequests;
|
||||
IdpWantAuthnRequestsSigned = configurationData.IdpWantAuthnRequestsSigned;
|
||||
SpNameIdFormat = configurationData.SpNameIdFormat;
|
||||
SpOutboundSigningAlgorithm = configurationData.SpOutboundSigningAlgorithm ?? SamlSigningAlgorithms.Sha256;
|
||||
SpSigningBehavior = configurationData.SpSigningBehavior;
|
||||
SpWantAssertionsSigned = configurationData.SpWantAssertionsSigned;
|
||||
SpValidateCertificates = configurationData.SpValidateCertificates;
|
||||
SpMinIncomingSigningAlgorithm = configurationData.SpMinIncomingSigningAlgorithm ?? SamlSigningAlgorithms.Sha256;
|
||||
AdditionalScopes = configurationData.AdditionalScopes;
|
||||
AdditionalUserIdClaimTypes = configurationData.AdditionalUserIdClaimTypes;
|
||||
AdditionalEmailClaimTypes = configurationData.AdditionalEmailClaimTypes;
|
||||
AdditionalNameClaimTypes = configurationData.AdditionalNameClaimTypes;
|
||||
AcrValues = configurationData.AcrValues;
|
||||
ExpectedReturnAcrValue = configurationData.ExpectedReturnAcrValue;
|
||||
}
|
||||
|
||||
[Required]
|
||||
public SsoType ConfigType { get; set; }
|
||||
|
||||
// OIDC
|
||||
public string Authority { get; set; }
|
||||
public string ClientId { get; set; }
|
||||
public string ClientSecret { get; set; }
|
||||
public string MetadataAddress { get; set; }
|
||||
public OpenIdConnectRedirectBehavior RedirectBehavior { get; set; }
|
||||
public bool GetClaimsFromUserInfoEndpoint { get; set; }
|
||||
public string AdditionalScopes { get; set; }
|
||||
public string AdditionalUserIdClaimTypes { get; set; }
|
||||
public string AdditionalEmailClaimTypes { get; set; }
|
||||
public string AdditionalNameClaimTypes { get; set; }
|
||||
public string AcrValues { get; set; }
|
||||
public string ExpectedReturnAcrValue { get; set; }
|
||||
|
||||
// SAML2 SP
|
||||
public Saml2NameIdFormat SpNameIdFormat { get; set; }
|
||||
public string SpOutboundSigningAlgorithm { get; set; }
|
||||
public Saml2SigningBehavior SpSigningBehavior { get; set; }
|
||||
public bool SpWantAssertionsSigned { get; set; }
|
||||
public bool SpValidateCertificates { get; set; }
|
||||
public string SpMinIncomingSigningAlgorithm { get; set; }
|
||||
|
||||
// SAML2 IDP
|
||||
public string IdpEntityId { get; set; }
|
||||
public Saml2BindingType IdpBindingType { get; set; }
|
||||
public string IdpSingleSignOnServiceUrl { get; set; }
|
||||
public string IdpSingleLogoutServiceUrl { get; set; }
|
||||
public string IdpArtifactResolutionServiceUrl { get; set; }
|
||||
public string IdpX509PublicCert { get; set; }
|
||||
public string IdpOutboundSigningAlgorithm { get; set; }
|
||||
public bool IdpAllowUnsolicitedAuthnResponse { get; set; }
|
||||
public bool IdpDisableOutboundLogoutRequests { get; set; }
|
||||
public bool IdpWantAuthnRequestsSigned { get; set; }
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext context)
|
||||
{
|
||||
var i18nService = context.GetService(typeof(II18nService)) as I18nService;
|
||||
|
||||
if (ConfigType == SsoType.OpenIdConnect)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Authority))
|
||||
{
|
||||
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("AuthorityValidationError"),
|
||||
new[] { nameof(Authority) });
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ClientId))
|
||||
{
|
||||
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("ClientIdValidationError"),
|
||||
new[] { nameof(ClientId) });
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ClientSecret))
|
||||
{
|
||||
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("ClientSecretValidationError"),
|
||||
new[] { nameof(ClientSecret) });
|
||||
}
|
||||
}
|
||||
else if (ConfigType == SsoType.Saml2)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(IdpEntityId))
|
||||
{
|
||||
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("IdpEntityIdValidationError"),
|
||||
new[] { nameof(IdpEntityId) });
|
||||
}
|
||||
|
||||
if (IdpBindingType == Saml2BindingType.Artifact && string.IsNullOrWhiteSpace(IdpArtifactResolutionServiceUrl))
|
||||
{
|
||||
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("Saml2BindingTypeValidationError"),
|
||||
new[] { nameof(IdpArtifactResolutionServiceUrl) });
|
||||
}
|
||||
|
||||
if (!Uri.IsWellFormedUriString(IdpEntityId, UriKind.Absolute) && string.IsNullOrWhiteSpace(IdpSingleSignOnServiceUrl))
|
||||
{
|
||||
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("IdpSingleSignOnServiceUrlValidationError"),
|
||||
new[] { nameof(IdpSingleSignOnServiceUrl) });
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(IdpX509PublicCert))
|
||||
{
|
||||
// Validate the certificate is in a valid format
|
||||
ValidationResult failedResult = null;
|
||||
try
|
||||
{
|
||||
var certData = StripPemCertificateElements(IdpX509PublicCert).Base64StringToByteArray();
|
||||
new X509Certificate2(certData);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
failedResult = new ValidationResult(i18nService.GetLocalizedHtmlString("IdpX509PublicCertInvalidFormatValidationError"),
|
||||
new[] { nameof(IdpX509PublicCert) });
|
||||
}
|
||||
catch (CryptographicException cryptoEx)
|
||||
{
|
||||
failedResult = new ValidationResult(i18nService.GetLocalizedHtmlString("IdpX509PublicCertCryptographicExceptionValidationError", cryptoEx.Message),
|
||||
new[] { nameof(IdpX509PublicCert) });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
failedResult = new ValidationResult(i18nService.GetLocalizedHtmlString("IdpX509PublicCertValidationError", ex.Message),
|
||||
new[] { nameof(IdpX509PublicCert) });
|
||||
}
|
||||
if (failedResult != null)
|
||||
{
|
||||
yield return failedResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SsoConfigurationData ToConfigurationData()
|
||||
{
|
||||
return new SsoConfigurationData
|
||||
{
|
||||
ConfigType = ConfigType,
|
||||
Authority = Authority,
|
||||
ClientId = ClientId,
|
||||
ClientSecret = ClientSecret,
|
||||
MetadataAddress = MetadataAddress,
|
||||
GetClaimsFromUserInfoEndpoint = GetClaimsFromUserInfoEndpoint,
|
||||
RedirectBehavior = RedirectBehavior,
|
||||
IdpEntityId = IdpEntityId,
|
||||
IdpBindingType = IdpBindingType,
|
||||
IdpSingleSignOnServiceUrl = IdpSingleSignOnServiceUrl,
|
||||
IdpSingleLogoutServiceUrl = IdpSingleLogoutServiceUrl,
|
||||
IdpArtifactResolutionServiceUrl = IdpArtifactResolutionServiceUrl,
|
||||
IdpX509PublicCert = StripPemCertificateElements(IdpX509PublicCert),
|
||||
IdpOutboundSigningAlgorithm = IdpOutboundSigningAlgorithm,
|
||||
IdpAllowUnsolicitedAuthnResponse = IdpAllowUnsolicitedAuthnResponse,
|
||||
IdpDisableOutboundLogoutRequests = IdpDisableOutboundLogoutRequests,
|
||||
IdpWantAuthnRequestsSigned = IdpWantAuthnRequestsSigned,
|
||||
SpNameIdFormat = SpNameIdFormat,
|
||||
SpOutboundSigningAlgorithm = SpOutboundSigningAlgorithm ?? SamlSigningAlgorithms.Sha256,
|
||||
SpSigningBehavior = SpSigningBehavior,
|
||||
SpWantAssertionsSigned = SpWantAssertionsSigned,
|
||||
SpValidateCertificates = SpValidateCertificates,
|
||||
SpMinIncomingSigningAlgorithm = SpMinIncomingSigningAlgorithm,
|
||||
AdditionalScopes = AdditionalScopes,
|
||||
AdditionalUserIdClaimTypes = AdditionalUserIdClaimTypes,
|
||||
AdditionalEmailClaimTypes = AdditionalEmailClaimTypes,
|
||||
AdditionalNameClaimTypes = AdditionalNameClaimTypes,
|
||||
AcrValues = AcrValues,
|
||||
ExpectedReturnAcrValue = ExpectedReturnAcrValue,
|
||||
};
|
||||
}
|
||||
|
||||
private string StripPemCertificateElements(string certificateText)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(certificateText))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Regex.Replace(certificateText,
|
||||
@"(((BEGIN|END) CERTIFICATE)|([\-\n\r\t\s\f]))",
|
||||
string.Empty,
|
||||
RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
}
|
||||
}
|
||||
}
|
51
src/Core/Models/Api/Response/OrganizationSsoResponseModel.cs
Normal file
51
src/Core/Models/Api/Response/OrganizationSsoResponseModel.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table;
|
||||
using Bit.Core.Settings;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class OrganizationSsoResponseModel : ResponseModel
|
||||
{
|
||||
public OrganizationSsoResponseModel(Organization organization, GlobalSettings globalSettings,
|
||||
SsoConfig config = null) : base("organizationSso")
|
||||
{
|
||||
if (config != null)
|
||||
{
|
||||
Enabled = config.Enabled;
|
||||
Data = JsonSerializer.Deserialize<SsoConfigurationData>(config.Data, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Data = new SsoConfigurationData();
|
||||
}
|
||||
|
||||
Urls = new SsoUrls(organization.Id.ToString(), Data, globalSettings);
|
||||
}
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
public SsoConfigurationData Data { get; set; }
|
||||
public SsoUrls Urls { get; set; }
|
||||
}
|
||||
|
||||
public class SsoUrls
|
||||
{
|
||||
public SsoUrls(string organizationId, SsoConfigurationData configurationData, GlobalSettings globalSettings)
|
||||
{
|
||||
CallbackPath = configurationData.BuildCallbackPath(globalSettings.BaseServiceUri.Sso);
|
||||
SignedOutCallbackPath = configurationData.BuildSignedOutCallbackPath(globalSettings.BaseServiceUri.Sso);
|
||||
SpEntityId = configurationData.BuildSaml2ModulePath(globalSettings.BaseServiceUri.Sso);
|
||||
SpMetadataUrl = configurationData.BuildSaml2MetadataUrl(globalSettings.BaseServiceUri.Sso, organizationId);
|
||||
SpAcsUrl = configurationData.BuildSaml2AcsUrl(globalSettings.BaseServiceUri.Sso, organizationId);
|
||||
}
|
||||
|
||||
public string CallbackPath { get; set; }
|
||||
public string SignedOutCallbackPath { get; set; }
|
||||
public string SpEntityId { get; set; }
|
||||
public string SpMetadataUrl { get; set; }
|
||||
public string SpAcsUrl { get; set; }
|
||||
}
|
||||
}
|
@ -51,7 +51,6 @@ namespace Bit.Core.Models.Api
|
||||
public bool Use2fa { get; set; }
|
||||
public bool UseApi { get; set; }
|
||||
public bool UseResetPassword { get; set; }
|
||||
public bool UseBusinessPortal => UsePolicies || UseSso; // TODO add events if needed
|
||||
public bool UsersGetPremium { get; set; }
|
||||
public bool SelfHost { get; set; }
|
||||
public int? Seats { get; set; }
|
||||
|
@ -16,7 +16,6 @@ namespace Bit.Core.Models.Data
|
||||
public bool Use2fa { get; set; }
|
||||
public bool UseApi{ get; set; }
|
||||
public bool UseResetPassword { get; set; }
|
||||
public bool UseBusinessPortal => UsePolicies || UseSso;
|
||||
public bool SelfHost { get; set; }
|
||||
public bool UsersGetPremium { get; set; }
|
||||
public int? Seats { get; set; }
|
||||
|
@ -6,7 +6,6 @@ namespace Bit.Core.Models.Data
|
||||
{
|
||||
public class Permissions
|
||||
{
|
||||
public bool AccessBusinessPortal { get; set; }
|
||||
public bool AccessEventLogs { get; set; }
|
||||
public bool AccessImportExport { get; set; }
|
||||
public bool AccessReports { get; set; }
|
||||
@ -29,7 +28,6 @@ namespace Bit.Core.Models.Data
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
public List<(bool Permission, string ClaimName)> ClaimsMap => new()
|
||||
{
|
||||
(AccessBusinessPortal, "accessbusinessportal"),
|
||||
(AccessEventLogs, "accesseventlogs"),
|
||||
(AccessImportExport, "accessimportexport"),
|
||||
(AccessReports, "accessreports"),
|
||||
|
@ -17,7 +17,6 @@ namespace Bit.Core.Models.Data
|
||||
public bool Use2fa { get; set; }
|
||||
public bool UseApi{ get; set; }
|
||||
public bool UseResetPassword { get; set; }
|
||||
public bool UseBusinessPortal => UsePolicies || UseSso;
|
||||
public bool SelfHost { get; set; }
|
||||
public bool UsersGetPremium { get; set; }
|
||||
public int? Seats { get; set; }
|
||||
|
@ -457,9 +457,6 @@
|
||||
<data name="Never">
|
||||
<value>Never</value>
|
||||
</data>
|
||||
<data name="WelcomeToBusinessPortal">
|
||||
<value>Welcome to the Bitwarden Business Portal</value>
|
||||
</data>
|
||||
<data name="IdpX509PublicCertValidationError" xml:space="preserve">
|
||||
<value>The IdP public certificate provided is invalid: {0}</value>
|
||||
</data>
|
||||
@ -642,9 +639,6 @@
|
||||
<value>Expected "acr" Claim Value In Response (acr validation)</value>
|
||||
<comment>'acr' is an explicit OIDC claim type, see https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.2 (acr). It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="LoggedOutMessage" xml:space="preserve">
|
||||
<value>You have been logged out of the Bitwarden Business Portal.</value>
|
||||
</data>
|
||||
<data name="AccessDeniedError" xml:space="preserve">
|
||||
<value>Access Denied to this resource.</value>
|
||||
</data>
|
||||
|
@ -70,7 +70,6 @@ namespace Bit.Core.Services
|
||||
Task<bool> CanAccessPremium(ITwoFactorProvidersUser user);
|
||||
Task<bool> TwoFactorIsEnabledAsync(ITwoFactorProvidersUser user);
|
||||
Task<bool> TwoFactorProviderIsEnabledAsync(TwoFactorProviderType provider, ITwoFactorProvidersUser user);
|
||||
Task<string> GenerateEnterprisePortalSignInTokenAsync(User user);
|
||||
Task<string> GenerateSignInTokenAsync(User user, string purpose);
|
||||
Task RotateApiKeyAsync(User user);
|
||||
string GetUserName(ClaimsPrincipal principal);
|
||||
|
@ -12,8 +12,6 @@ using System.Security.Claims;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Models.Business;
|
||||
using U2fLib = U2F.Core.Crypto.U2F;
|
||||
using U2F.Core.Models;
|
||||
using U2F.Core.Utils;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Utilities;
|
||||
@ -1222,15 +1220,6 @@ namespace Bit.Core.Services
|
||||
return await CanAccessPremium(user);
|
||||
}
|
||||
|
||||
//TODO refactor this to use the below method and enum
|
||||
public async Task<string> GenerateEnterprisePortalSignInTokenAsync(User user)
|
||||
{
|
||||
var token = await GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
|
||||
"EnterprisePortalTokenSignIn");
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
public async Task<string> GenerateSignInTokenAsync(User user, string purpose)
|
||||
{
|
||||
var token = await GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
|
||||
|
@ -117,14 +117,12 @@ namespace Bit.Core.Settings
|
||||
private string _admin;
|
||||
private string _notifications;
|
||||
private string _sso;
|
||||
private string _portal;
|
||||
private string _internalApi;
|
||||
private string _internalIdentity;
|
||||
private string _internalAdmin;
|
||||
private string _internalNotifications;
|
||||
private string _internalSso;
|
||||
private string _internalVault;
|
||||
private string _internalPortal;
|
||||
|
||||
public BaseServiceUriSettings(GlobalSettings globalSettings)
|
||||
{
|
||||
@ -159,11 +157,6 @@ namespace Bit.Core.Settings
|
||||
get => _globalSettings.BuildExternalUri(_sso, "sso");
|
||||
set => _sso = value;
|
||||
}
|
||||
public string Portal
|
||||
{
|
||||
get => _globalSettings.BuildExternalUri(_portal, "portal");
|
||||
set => _portal = value;
|
||||
}
|
||||
|
||||
public string InternalNotifications
|
||||
{
|
||||
@ -195,11 +188,6 @@ namespace Bit.Core.Settings
|
||||
get => _globalSettings.BuildInternalUri(_internalSso, "sso");
|
||||
set => _internalSso = value;
|
||||
}
|
||||
public string InternalPortal
|
||||
{
|
||||
get => _globalSettings.BuildInternalUri(_internalPortal, "portal");
|
||||
set => _internalPortal = value;
|
||||
}
|
||||
}
|
||||
|
||||
public class SqlSettings
|
||||
|
Reference in New Issue
Block a user