1
0
mirror of https://github.com/bitwarden/server.git synced 2025-01-10 20:07:56 +01:00

Added X.509 cert validation copy value buttons (#923)

This commit is contained in:
Chad Scharf 2020-09-09 11:32:33 -04:00 committed by GitHub
parent 1c3ba46246
commit b429f6908d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 160 additions and 27 deletions

View File

@ -6,6 +6,10 @@ using Bit.Core;
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.RegularExpressions;
namespace Bit.Portal.Models
{
@ -144,6 +148,35 @@ namespace Bit.Portal.Models
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("IdpSingleSignOnServiceUrlValidationError"),
new[] { nameof(model.IdpSingleSignOnServiceUrl) });
}
if (!string.IsNullOrWhiteSpace(model.IdpX509PublicCert))
{
// Validate the certificate is in a valid format
ValidationResult failedResult = null;
try
{
var certData = StripPemCertificateElements(model.IdpX509PublicCert).Base64StringToByteArray();
new X509Certificate2(certData);
}
catch (FormatException)
{
failedResult = new ValidationResult(i18nService.GetLocalizedHtmlString("IdpX509PublicCertInvalidFormatValidationError"),
new[] { nameof(model.IdpX509PublicCert) });
}
catch (CryptographicException cryptoEx)
{
failedResult = new ValidationResult(i18nService.GetLocalizedHtmlString("IdpX509PublicCertCryptographicExceptionValidationError", cryptoEx.Message),
new[] { nameof(model.IdpX509PublicCert) });
}
catch (Exception ex)
{
failedResult = new ValidationResult(i18nService.GetLocalizedHtmlString("IdpX509PublicCertValidationError", ex.Message),
new[] { nameof(model.IdpX509PublicCert) });
}
if (failedResult != null)
{
yield return failedResult;
}
}
}
}
@ -162,7 +195,7 @@ namespace Bit.Portal.Models
IdpSingleSignOnServiceUrl = IdpSingleSignOnServiceUrl,
IdpSingleLogoutServiceUrl = IdpSingleLogoutServiceUrl,
IdpArtifactResolutionServiceUrl = IdpArtifactResolutionServiceUrl,
IdpX509PublicCert = IdpX509PublicCert,
IdpX509PublicCert = StripPemCertificateElements(IdpX509PublicCert),
IdpOutboundSigningAlgorithm = IdpOutboundSigningAlgorithm,
IdpAllowUnsolicitedAuthnResponse = IdpAllowUnsolicitedAuthnResponse,
IdpDisableOutboundLogoutRequests = IdpDisableOutboundLogoutRequests,
@ -174,5 +207,13 @@ namespace Bit.Portal.Models
SpValidateCertificates = SpValidateCertificates,
};
}
private string StripPemCertificateElements(string certificateText)
{
return Regex.Replace(certificateText,
@"(((BEGIN|END) CERTIFICATE)|([\-\n\r\t\s\f]))",
string.Empty,
RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
}
}
}

View File

@ -224,3 +224,8 @@ input, select, textarea {
.alert.validation-summary-errors > ul {
margin-bottom: 0;
}
.input-validation-error {
border: solid 1px $danger;
border-color: $danger;
}

View File

@ -25,6 +25,16 @@
$('#Data_ConfigType').change(function () {
toggleVisibility();
});
$('.copy-button').on('click', function () {
const $control = $(this).closest('.form-group').find('input[type="text"], textarea');
if ($control.length > 0) {
const elem = $control[0];
elem.select();
elem.setSelectionRange(0, $control.val().length);
document.execCommand('copy');
}
});
});
</script>
}
@ -42,7 +52,7 @@
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.ConfigType"></label>
<select asp-for="Data.ConfigType" asp-items="Model.ConfigTypes" class="form-control"></select>
</div>
@ -53,47 +63,64 @@
<div class="config-section">
<h2>@i18nService.T("OpenIdConnectConfig")</h2>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.CallbackPath">@i18nService.T("CallbackPath")</label>
<input asp-for="Data.CallbackPath" class="form-control" readonly>
<div class="input-group">
<input asp-for="Data.CallbackPath" class="form-control" readonly>
<div class="input-group-append">
<button type="button" class="btn btn-outline-secondary copy-button"
aria-label="@i18nService.T("CopyCallbackPath")" title="@i18nService.T("CopyCallbackPath")"
tabindex="-1">
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.SignedOutCallbackPath">@i18nService.T("SignedOutCallbackPath")</label>
<input asp-for="Data.SignedOutCallbackPath" class="form-control" readonly>
<div class="input-group">
<input asp-for="Data.SignedOutCallbackPath" class="form-control" readonly>
<div class="input-group-append">
<button type="button" class="btn btn-outline-secondary copy-button"
aria-label="@i18nService.T("CopySignedOutCallbackPath")" title="@i18nService.T("CopySignedOutCallbackPath")"
tabindex="-1">
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.Authority">@i18nService.T("Authority")</label>
<input asp-for="Data.Authority" class="form-control">
<span asp-validation-for="Data.Authority" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.ClientId">@i18nService.T("ClientId")</label>
<input asp-for="Data.ClientId" class="form-control">
<span asp-validation-for="Data.ClientId" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.ClientSecret">@i18nService.T("ClientSecret")</label>
<input asp-for="Data.ClientSecret" class="form-control">
<span asp-validation-for="Data.ClientSecret" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.MetadataAddress">@i18nService.T("MetadataAddress")</label>
<input asp-for="Data.MetadataAddress" class="form-control">
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<div class="form-check">
<input asp-for="Data.GetClaimsFromUserInfoEndpoint" type="checkbox" class="form-check-input">
<label asp-for="Data.GetClaimsFromUserInfoEndpoint" class="form-check-label"></label>
@ -108,33 +135,53 @@
<div class="config-section">
<h2>@i18nService.T("SamlSpConfig")</h2>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.SpEntityId">@i18nService.T("SpEntityId")</label>
<input asp-for="Data.SpEntityId" class="form-control" readonly>
<div class="input-group">
<input asp-for="Data.SpEntityId" class="form-control" readonly>
<div class="input-group-append">
<button type="button" class="btn btn-outline-secondary copy-button"
aria-label="@i18nService.T("CopySpEntityId")" title="@i18nService.T("CopySpEntityId")"
tabindex="-1">
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.SpAcsUrl">@i18nService.T("SpAcsUrl")</label>
<input asp-for="Data.SpAcsUrl" class="form-control" readonly>
<div class="input-group">
<input asp-for="Data.SpAcsUrl" class="form-control" readonly>
<div class="input-group-append">
<button type="button" class="btn btn-outline-secondary copy-button"
aria-label="@i18nService.T("CopySpAcsUrl")" title="@i18nService.T("CopySpAcsUrl")"
tabindex="-1">
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
<div class="col-1">
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.SpNameIdFormat">@i18nService.T("NameIdFormat")</label>
<select asp-for="Data.SpNameIdFormat" asp-items="Model.SpNameIdFormats"
class="form-control"></select>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.SpOutboundSigningAlgorithm">@i18nService.T("OutboundSigningAlgorithm")</label>
<select asp-for="Data.SpOutboundSigningAlgorithm" asp-items="Model.SigningAlgorithms"
class="form-control"></select>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.SpSigningBehavior">@i18nService.T("SigningBehavior")</label>
<select asp-for="Data.SpSigningBehavior" asp-items="Model.SigningBehaviors"
class="form-control"></select>
@ -159,46 +206,47 @@
<h2>@i18nService.T("SamlIdpConfig")</h2>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.IdpEntityId">@i18nService.T("EntityId")</label>
<input asp-for="Data.IdpEntityId" class="form-control">
<span asp-validation-for="Data.IdpEntityId" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.IdpBindingType"></label>
<select asp-for="Data.IdpBindingType" asp-items="Model.BindingTypes" class="form-control"></select>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.IdpSingleSignOnServiceUrl">@i18nService.T("SingleSignOnServiceUrl")</label>
<input asp-for="Data.IdpSingleSignOnServiceUrl" class="form-control">
<span asp-validation-for="Data.IdpSingleSignOnServiceUrl" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.IdpSingleLogoutServiceUrl">@i18nService.T("SingleLogoutServiceUrl")</label>
<input asp-for="Data.IdpSingleLogoutServiceUrl" class="form-control">
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.IdpArtifactResolutionServiceUrl"></label>
<input asp-for="Data.IdpArtifactResolutionServiceUrl" class="form-control">
<span asp-validation-for="Data.IdpArtifactResolutionServiceUrl" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.IdpX509PublicCert">@i18nService.T("X509PublicCert")</label>
<textarea asp-for="Data.IdpX509PublicCert" class="form-control"></textarea>
<textarea asp-for="Data.IdpX509PublicCert" class="form-control form-control-sm text-monospace" rows="6"></textarea>
<span asp-validation-for="Data.IdpX509PublicCert" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="col-7 form-group">
<label asp-for="Data.IdpOutboundSigningAlgorithm"></label>
<select asp-for="Data.IdpOutboundSigningAlgorithm" asp-items="Model.SigningAlgorithms"
class="form-control"></select>

View File

@ -276,15 +276,19 @@
</data>
<data name="NotConfigured" xml:space="preserve">
<value>Not Configured</value>
<comment>A SAML Name ID format</comment>
</data>
<data name="Unspecified" xml:space="preserve">
<value>Unspecified</value>
<comment>A SAML Name ID format</comment>
</data>
<data name="EmailAddress" xml:space="preserve">
<value>Email Address</value>
<comment>A SAML Name ID format</comment>
</data>
<data name="X509SubjectName" xml:space="preserve">
<value>X.509 Subject Name</value>
<comment>A SAML Name ID format</comment>
</data>
<data name="WindowsDomainQualifiedName" xml:space="preserve">
<value>Windows Domain Qualified Name</value>
@ -297,9 +301,11 @@
</data>
<data name="Persistent" xml:space="preserve">
<value>Persistent</value>
<comment>A SAML Name ID format</comment>
</data>
<data name="Transient" xml:space="preserve">
<value>Transient</value>
<comment>A SAML Name ID format</comment>
</data>
<data name="PrivateKey" xml:space="preserve">
<value>Private Key</value>
@ -448,4 +454,37 @@
<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>
<data name="IdpX509PublicCertInvalidFormatValidationError" xml:space="preserve">
<value>The IdP public certificate provided is not a valid Base64 encoded string, contains illegal characters or whitespace, or is incomplete.</value>
</data>
<data name="IdpX509PublicCertCryptographicExceptionValidationError" xml:space="preserve">
<value>The IdP public certificate provided does not appear to be a valid certificate, please ensure this is a valid, Base64 encoded PEM or CER format public certificate valid for signing: {0}</value>
</data>
<data name="CopyCallbackPath">
<value>Copy the OIDC callback path to your clipboard</value>
</data>
<data name="CopySignedOutCallbackPath">
<value>Copy the OIDC signed out callback path to your clipboard</value>
</data>
<data name="CopySpEntityId">
<value>Copy the SP Entity Id to your clipboard</value>
</data>
<data name="CopySpAcsUrl">
<value>Copy the Assertion Consumer Service (ACS) URL to your clipboard</value>
</data>
<data name="HttpRedirect">
<value>Redirect</value>
<comment>A SAML binding type, Redirect</comment>
</data>
<data name="HttpPost">
<value>HTTP POST</value>
<comment>A SAML binding type, HTTP POST</comment>
</data>
<data name="Artifact">
<value>Artifact</value>
<comment>A SAML binding type, Artifact</comment>
</data>
</root>