feat(integrations): add support for self-signed-cert-in-chain request error (#3399)
This commit is contained in:
@@ -30,6 +30,8 @@ export const CertificateErrorDetails = ({ error, url }: CertificateErrorDetailsP
|
|||||||
const { mutateAsync: trustHostnameAsync } = clientApi.certificates.trustHostnameMismatch.useMutation();
|
const { mutateAsync: trustHostnameAsync } = clientApi.certificates.trustHostnameMismatch.useMutation();
|
||||||
const { mutateAsync: addCertificateAsync } = clientApi.certificates.addCertificate.useMutation();
|
const { mutateAsync: addCertificateAsync } = clientApi.certificates.addCertificate.useMutation();
|
||||||
|
|
||||||
|
const rootCertificate = getHeighestCertificate(error.data.certificate);
|
||||||
|
|
||||||
const handleTrustHostname = () => {
|
const handleTrustHostname = () => {
|
||||||
const { hostname } = new URL(url);
|
const { hostname } = new URL(url);
|
||||||
openConfirmModal({
|
openConfirmModal({
|
||||||
@@ -72,7 +74,7 @@ export const CertificateErrorDetails = ({ error, url }: CertificateErrorDetailsP
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append(
|
formData.append(
|
||||||
"file",
|
"file",
|
||||||
new File([error.data.certificate.pem], `${hostname}-${createId()}.crt`, {
|
new File([rootCertificate.pem], `${hostname}-${createId()}.crt`, {
|
||||||
type: "application/x-x509-ca-cert",
|
type: "application/x-x509-ca-cert",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -110,11 +112,11 @@ export const CertificateErrorDetails = ({ error, url }: CertificateErrorDetailsP
|
|||||||
<>
|
<>
|
||||||
{description}
|
{description}
|
||||||
|
|
||||||
<CertificateDetailsCard certificate={error.data.certificate} />
|
<CertificateDetailsCard certificate={rootCertificate} />
|
||||||
|
|
||||||
{error.data.reason === "hostnameMismatch" && <HostnameMismatchAlert />}
|
{error.data.reason === "hostnameMismatch" && <HostnameMismatchAlert />}
|
||||||
|
|
||||||
{!error.data.certificate.isSelfSigned && error.data.reason === "untrusted" && <CertificateExtractAlert />}
|
{!rootCertificate.isSelfSigned && error.data.reason === "untrusted" && <CertificateExtractAlert />}
|
||||||
|
|
||||||
{showRetryButton && (
|
{showRetryButton && (
|
||||||
<Button
|
<Button
|
||||||
@@ -127,7 +129,7 @@ export const CertificateErrorDetails = ({ error, url }: CertificateErrorDetailsP
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(error.data.reason === "untrusted" && error.data.certificate.isSelfSigned) ||
|
{(error.data.reason === "untrusted" && rootCertificate.isSelfSigned) ||
|
||||||
error.data.reason === "hostnameMismatch" ? (
|
error.data.reason === "hostnameMismatch" ? (
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
@@ -137,7 +139,7 @@ export const CertificateErrorDetails = ({ error, url }: CertificateErrorDetailsP
|
|||||||
{tError("certificate.action.trust.label")}
|
{tError("certificate.action.trust.label")}
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
{error.data.reason === "untrusted" && !error.data.certificate.isSelfSigned ? (
|
{error.data.reason === "untrusted" && !rootCertificate.isSelfSigned ? (
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
fullWidth
|
fullWidth
|
||||||
@@ -325,3 +327,8 @@ const PemContentModal = createModal<{ content: string }>(({ actions, innerProps
|
|||||||
},
|
},
|
||||||
size: "lg",
|
size: "lg",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getHeighestCertificate = (certificate: MappedCertificate): MappedCertificate => {
|
||||||
|
if (certificate.issuerCertificate) return getHeighestCertificate(certificate.issuerCertificate);
|
||||||
|
return certificate;
|
||||||
|
};
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const mapError = (error: Error): MappedError => {
|
|||||||
export interface MappedCertificate {
|
export interface MappedCertificate {
|
||||||
isSelfSigned: boolean;
|
isSelfSigned: boolean;
|
||||||
issuer: string;
|
issuer: string;
|
||||||
|
issuerCertificate?: MappedCertificate;
|
||||||
subject: string;
|
subject: string;
|
||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
validFrom: Date;
|
validFrom: Date;
|
||||||
@@ -47,6 +48,7 @@ export interface MappedCertificate {
|
|||||||
const mapCertificate = (certificate: X509Certificate, code: RequestErrorCode): MappedCertificate => ({
|
const mapCertificate = (certificate: X509Certificate, code: RequestErrorCode): MappedCertificate => ({
|
||||||
isSelfSigned: certificate.ca || code === "DEPTH_ZERO_SELF_SIGNED_CERT",
|
isSelfSigned: certificate.ca || code === "DEPTH_ZERO_SELF_SIGNED_CERT",
|
||||||
issuer: certificate.issuer,
|
issuer: certificate.issuer,
|
||||||
|
issuerCertificate: certificate.issuerCertificate ? mapCertificate(certificate.issuerCertificate, code) : undefined,
|
||||||
subject: certificate.subject,
|
subject: certificate.subject,
|
||||||
serialNumber: certificate.serialNumber,
|
serialNumber: certificate.serialNumber,
|
||||||
validFrom: certificate.validFromDate,
|
validFrom: certificate.validFromDate,
|
||||||
|
|||||||
@@ -36,7 +36,12 @@ export const requestErrorMap = {
|
|||||||
expired: ["CERT_HAS_EXPIRED"],
|
expired: ["CERT_HAS_EXPIRED"],
|
||||||
hostnameMismatch: ["ERR_TLS_CERT_ALTNAME_INVALID", "CERT_COMMON_NAME_INVALID"],
|
hostnameMismatch: ["ERR_TLS_CERT_ALTNAME_INVALID", "CERT_COMMON_NAME_INVALID"],
|
||||||
notYetValid: ["CERT_NOT_YET_VALID"],
|
notYetValid: ["CERT_NOT_YET_VALID"],
|
||||||
untrusted: ["DEPTH_ZERO_SELF_SIGNED_CERT", "UNABLE_TO_VERIFY_LEAF_SIGNATURE", "UNABLE_TO_GET_ISSUER_CERT_LOCALLY"],
|
untrusted: [
|
||||||
|
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
||||||
|
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
||||||
|
"UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
||||||
|
"SELF_SIGNED_CERT_IN_CHAIN",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
connection: {
|
connection: {
|
||||||
hostUnreachable: "EHOSTUNREACH",
|
hostUnreachable: "EHOSTUNREACH",
|
||||||
|
|||||||
Reference in New Issue
Block a user