mirror of
https://github.com/logto-io/logto.git
synced 2025-03-31 22:51:25 -05:00
feat(console): add signing certificate reader (#5009)
* feat(console): add signing certificate reader * fix(core): throw invalid certificate SsoConnectorError only when failed to construct a certificate
This commit is contained in:
parent
0bec4cd1d3
commit
3bab5351a6
60 changed files with 311 additions and 110 deletions
|
@ -63,8 +63,8 @@ export type ParsedSsoIdentityProviderConfig<T extends SsoProviderName> =
|
|||
entityId: string;
|
||||
signInEndpoint: string;
|
||||
x509Certificate: string;
|
||||
expiresAt: number;
|
||||
isValid: boolean;
|
||||
certificateExpiresAt: number;
|
||||
isCertificateValid: boolean;
|
||||
};
|
||||
}
|
||||
: never;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { type AdminConsoleKey } from '@logto/phrases';
|
||||
import { Theme } from '@logto/schemas';
|
||||
import { useCallback } from 'react';
|
||||
import { useDropzone, type FileRejection } from 'react-dropzone';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useDropzone, type FileRejection, type Accept } from 'react-dropzone';
|
||||
import { type FieldError } from 'react-hook-form';
|
||||
|
||||
import Delete from '@/assets/icons/delete.svg';
|
||||
import FileIconDark from '@/assets/icons/file-icon-dark.svg';
|
||||
|
@ -11,29 +12,29 @@ import Button from '@/ds-components/Button';
|
|||
import IconButton from '@/ds-components/IconButton';
|
||||
import useTheme from '@/hooks/use-theme';
|
||||
|
||||
import { type SamlGuideFormType } from '../../../EnterpriseSso/types';
|
||||
import { calculateXmlFileSize } from '../SamlMetadataForm/utils';
|
||||
import { calculateFileSize } from '../SamlMetadataForm/utils';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const xmlMimeTypes = ['application/xml', 'text/xml'];
|
||||
const xmlFileName = 'identity provider metadata.xml'; // Real file name does not matter, use a generic name.
|
||||
const xmlFileSizeLimit = 500 * 1024; // 500 KB
|
||||
const fileSizeLimit = 500 * 1024; // 500 KB
|
||||
|
||||
type Props = {
|
||||
onChange: (xmlContent?: string) => void;
|
||||
export type Props = {
|
||||
onChange: (fileContent?: string) => void;
|
||||
value?: string;
|
||||
attributes: {
|
||||
accept: Accept; // File reader accepted file types.
|
||||
buttonTitle: AdminConsoleKey; // I18n key for the button title.
|
||||
defaultFilename: string; // Default file name.
|
||||
defaultFileMimeType: string; // Default file MIME type when calculating the file size.
|
||||
};
|
||||
fieldError?: FieldError;
|
||||
setError: (error: FieldError) => void;
|
||||
};
|
||||
|
||||
function XmlFileReader({ onChange, value }: Props) {
|
||||
function FileReader({ onChange, value, attributes, fieldError, setError }: Props) {
|
||||
const theme = useTheme();
|
||||
|
||||
const {
|
||||
setError,
|
||||
formState: {
|
||||
errors: { metadata: metadataError },
|
||||
},
|
||||
} = useFormContext<SamlGuideFormType>();
|
||||
const { accept, buttonTitle, defaultFilename, defaultFileMimeType } = attributes;
|
||||
|
||||
/**
|
||||
* As you can see, per `useDropzone` hook's config, there are at most one file, if file is rejected, then we can return as long as we get the error message.
|
||||
|
@ -43,7 +44,7 @@ function XmlFileReader({ onChange, value }: Props) {
|
|||
if (fileRejection.length > 0) {
|
||||
const fileErrors = fileRejection[0]?.errors;
|
||||
if (fileErrors?.[0]?.message) {
|
||||
setError('metadata', {
|
||||
setError({
|
||||
type: 'custom',
|
||||
message: fileErrors[0]?.message,
|
||||
});
|
||||
|
@ -56,8 +57,8 @@ function XmlFileReader({ onChange, value }: Props) {
|
|||
return;
|
||||
}
|
||||
|
||||
const xmlContent = await acceptedFile.text();
|
||||
onChange(xmlContent);
|
||||
const fileContent = await acceptedFile.text();
|
||||
onChange(fileContent);
|
||||
},
|
||||
[onChange, setError]
|
||||
);
|
||||
|
@ -70,22 +71,22 @@ function XmlFileReader({ onChange, value }: Props) {
|
|||
onDrop,
|
||||
noDrag: true, // Only allow file selection via the file input.
|
||||
maxFiles: 1,
|
||||
maxSize: xmlFileSizeLimit,
|
||||
maxSize: fileSizeLimit,
|
||||
multiple: false, // Upload only one file at a time.
|
||||
accept: Object.fromEntries(xmlMimeTypes.map((mimeType) => [mimeType, []])),
|
||||
accept,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{value ? (
|
||||
<div className={styles.preview}>
|
||||
{theme === Theme.Dark ? <FileIcon /> : <FileIconDark />}
|
||||
{theme === Theme.Dark ? <FileIconDark /> : <FileIcon />}
|
||||
<div className={styles.fileInfo}>
|
||||
<span className={styles.fileName}>{xmlFileName}</span>
|
||||
<span className={styles.fileName}>{defaultFilename}</span>
|
||||
{/* Not using `File.size` since the file content (variable `value` in this case) is stored in DB in string type */}
|
||||
<span className={styles.fileSize}>{`${(calculateXmlFileSize(value) / 1024).toFixed(
|
||||
2
|
||||
)} KB`}</span>
|
||||
<span className={styles.fileSize}>{`${(
|
||||
calculateFileSize(value, defaultFilename, defaultFileMimeType) / 1024
|
||||
).toFixed(2)} KB`}</span>
|
||||
</div>
|
||||
<IconButton
|
||||
className={styles.delete}
|
||||
|
@ -99,18 +100,14 @@ function XmlFileReader({ onChange, value }: Props) {
|
|||
) : (
|
||||
<>
|
||||
<div {...getRootProps()}>
|
||||
<Button
|
||||
icon={<UploaderIcon />}
|
||||
title="enterprise_sso_details.upload_idp_metadata_button_text"
|
||||
size="large"
|
||||
/>
|
||||
<Button icon={<UploaderIcon />} title={buttonTitle} size="large" />
|
||||
<input {...getInputProps({ className: styles.fileInput })} />
|
||||
</div>
|
||||
{Boolean(metadataError) && <div className={styles.error}>{metadataError?.message}</div>}
|
||||
{Boolean(fieldError) && <div className={styles.error}>{fieldError?.message}</div>}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default XmlFileReader;
|
||||
export default FileReader;
|
|
@ -19,9 +19,6 @@
|
|||
font: var(--font-body-2);
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,18 +27,25 @@
|
|||
}
|
||||
}
|
||||
|
||||
.indicator {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: _.unit(2);
|
||||
border-radius: 50%;
|
||||
background: var(--color-on-success-container);
|
||||
}
|
||||
.certificatePreview {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font: var(--font-body-2);
|
||||
|
||||
.errorStatus {
|
||||
background: var(--color-on-error-container);
|
||||
}
|
||||
.indicator {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: _.unit(2);
|
||||
border-radius: 50%;
|
||||
background: var(--color-on-success-container);
|
||||
}
|
||||
|
||||
.copyToClipboard {
|
||||
margin-left: _.unit(1);
|
||||
.errorStatus {
|
||||
background: var(--color-on-error-container);
|
||||
}
|
||||
|
||||
.copyToClipboard {
|
||||
margin-left: _.unit(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,51 +15,66 @@ type Props = {
|
|||
identityProviderConfig: ParsedSsoIdentityProviderConfig<SsoProviderName.SAML>['identityProvider'];
|
||||
};
|
||||
|
||||
type CertificatePreviewProps = {
|
||||
identityProviderConfig: {
|
||||
x509Certificate: string;
|
||||
certificateExpiresAt: number;
|
||||
isCertificateValid: boolean;
|
||||
};
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function CertificatePreview({
|
||||
identityProviderConfig: { x509Certificate, certificateExpiresAt, isCertificateValid },
|
||||
className,
|
||||
}: CertificatePreviewProps) {
|
||||
const { language } = i18next;
|
||||
return (
|
||||
<div className={classNames(styles.certificatePreview, className)}>
|
||||
<div className={classNames(styles.indicator, !isCertificateValid && styles.errorStatus)} />
|
||||
<DynamicT
|
||||
forKey="enterprise_sso_details.saml_preview.certificate_content"
|
||||
interpolation={{
|
||||
date: new Date(certificateExpiresAt).toLocaleDateString(
|
||||
// TODO: @darcyYe check whether can use date-fns later, may need a Logto locale to date-fns locale mapping.
|
||||
conditional(isLanguageTag(language) && language) ?? 'en',
|
||||
{
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
}
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<CopyToClipboard className={styles.copyToClipboard} variant="icon" value={x509Certificate} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ParsedConfigPreview({ identityProviderConfig }: Props) {
|
||||
const { t } = useTranslation(undefined, {
|
||||
keyPrefix: 'admin_console.enterprise_sso_details.saml_preview',
|
||||
});
|
||||
const { language } = i18next;
|
||||
|
||||
if (!identityProviderConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { entityId, signInEndpoint, x509Certificate, expiresAt, isValid } = identityProviderConfig;
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div>
|
||||
<div className={styles.title}>{t('sign_on_url')}</div>
|
||||
<div className={styles.content}>{signInEndpoint}</div>
|
||||
<div className={styles.content}>{identityProviderConfig.signInEndpoint}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>{t('entity_id')}</div>
|
||||
<div className={styles.content}>{entityId}</div>
|
||||
<div className={styles.content}>{identityProviderConfig.entityId}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>{t('x509_certificate')}</div>
|
||||
<div className={styles.content}>
|
||||
<div className={classNames(styles.indicator, !isValid && styles.errorStatus)} />
|
||||
<DynamicT
|
||||
forKey="enterprise_sso_details.saml_preview.certificate_content"
|
||||
interpolation={{
|
||||
date: new Date(expiresAt).toLocaleDateString(
|
||||
// TODO: check if Logto's language tags are compatible.
|
||||
conditional(isLanguageTag(language) && language) ?? 'en',
|
||||
{
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
}
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<CopyToClipboard
|
||||
className={styles.copyToClipboard}
|
||||
variant="icon"
|
||||
value={x509Certificate}
|
||||
/>
|
||||
<CertificatePreview identityProviderConfig={identityProviderConfig} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,3 +5,7 @@
|
|||
font: var(--font-body-2);
|
||||
margin-top: _.unit(0.5);
|
||||
}
|
||||
|
||||
.certificatePreview {
|
||||
margin-top: _.unit(1);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import { useTranslation } from 'react-i18next';
|
|||
import FormField from '@/ds-components/FormField';
|
||||
import InlineNotification from '@/ds-components/InlineNotification';
|
||||
import TextInput from '@/ds-components/TextInput';
|
||||
import Textarea from '@/ds-components/Textarea';
|
||||
import {
|
||||
type ParsedSsoIdentityProviderConfig,
|
||||
type SamlGuideFormType,
|
||||
|
@ -14,9 +13,9 @@ import {
|
|||
} from '@/pages/EnterpriseSso/types.js';
|
||||
import { uriValidator } from '@/utils/validator';
|
||||
|
||||
import XmlFileReader from '../XmlFileReader';
|
||||
import FileReader, { type Props as FileReaderProps } from '../FileReader';
|
||||
|
||||
import ParsedConfigPreview from './ParsedConfigPreview';
|
||||
import ParsedConfigPreview, { CertificatePreview } from './ParsedConfigPreview';
|
||||
import SwitchFormatButton, { FormFormat } from './SwitchFormatButton';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
|
@ -30,6 +29,30 @@ type SamlMetadataFormProps = {
|
|||
providerConfig?: ParsedSsoIdentityProviderConfig<SsoProviderName.SAML>;
|
||||
};
|
||||
|
||||
type KeyType = keyof Pick<SamlGuideFormType, 'metadata' | 'x509Certificate'>; // I.e. 'metadata' | 'x509Certificate'.
|
||||
const keyToAttributes: Record<KeyType, FileReaderProps['attributes']> = {
|
||||
// Accept xml file.
|
||||
metadata: {
|
||||
buttonTitle: 'enterprise_sso.metadata.saml.metadata_xml_uploader_text',
|
||||
accept: {
|
||||
'application/xml': [],
|
||||
'text/xml': [],
|
||||
},
|
||||
defaultFilename: 'identity provider metadata.xml',
|
||||
defaultFileMimeType: 'application/xml',
|
||||
},
|
||||
x509Certificate: {
|
||||
buttonTitle: 'enterprise_sso_details.upload_signing_certificate_button_text',
|
||||
accept: {
|
||||
'application/x-x509-user-cert': ['.crt', '.cer'],
|
||||
'application/x-x509-ca-cert': ['.crt', '.cer'],
|
||||
'application/x-pem-file': ['.pem'],
|
||||
},
|
||||
defaultFilename: 'signing certificate.cer',
|
||||
defaultFileMimeType: 'application/x-x509-user-cert',
|
||||
},
|
||||
};
|
||||
|
||||
function SamlMetadataFormFields({
|
||||
formFormat,
|
||||
identityProviderConfig,
|
||||
|
@ -37,6 +60,7 @@ function SamlMetadataFormFields({
|
|||
}: SamlMetadataFormFieldsProps) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const {
|
||||
setError,
|
||||
control,
|
||||
register,
|
||||
formState: { errors },
|
||||
|
@ -69,10 +93,36 @@ function SamlMetadataFormFields({
|
|||
/>
|
||||
</FormField>
|
||||
<FormField isRequired title="enterprise_sso.metadata.saml.certificate_field_name">
|
||||
<Textarea
|
||||
{...register('x509Certificate', { required: true })}
|
||||
placeholder={t('enterprise_sso.metadata.saml.certificate_placeholder')}
|
||||
error={Boolean(errors.x509Certificate)}
|
||||
<Controller
|
||||
control={control}
|
||||
name="x509Certificate"
|
||||
rules={{
|
||||
validate: (value) => {
|
||||
if (!value) {
|
||||
return t('enterprise_sso.metadata.saml.certificate_required');
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<>
|
||||
<FileReader
|
||||
attributes={keyToAttributes.x509Certificate}
|
||||
value={value}
|
||||
fieldError={errors.x509Certificate}
|
||||
setError={(error) => {
|
||||
setError('x509Certificate', error);
|
||||
}}
|
||||
onChange={onChange}
|
||||
/>
|
||||
{value && identityProviderConfig && (
|
||||
<CertificatePreview
|
||||
className={styles.certificatePreview}
|
||||
identityProviderConfig={identityProviderConfig}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</FormField>
|
||||
</>
|
||||
|
@ -86,7 +136,15 @@ function SamlMetadataFormFields({
|
|||
control={control}
|
||||
name="metadata"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<XmlFileReader value={value} onChange={onChange} />
|
||||
<FileReader
|
||||
attributes={keyToAttributes.metadata}
|
||||
value={value}
|
||||
fieldError={errors.metadata}
|
||||
setError={(error) => {
|
||||
setError('metadata', error);
|
||||
}}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormField>
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
// In Bytes.
|
||||
export const calculateXmlFileSize = (xmlContent: string): number => {
|
||||
const blob = new Blob([xmlContent], { type: 'application/xml' });
|
||||
const file = new File([blob], 'identity provider metadata.xml');
|
||||
export const calculateFileSize = (
|
||||
xmlContent: string,
|
||||
fileName: string,
|
||||
mimeType: string
|
||||
): number => {
|
||||
const blob = new Blob([xmlContent], { type: mimeType });
|
||||
const file = new File([blob], fileName);
|
||||
return file.size;
|
||||
};
|
||||
|
|
|
@ -33,13 +33,7 @@ type Props<T extends SsoProviderName> = {
|
|||
// This component contains only `data.config`.
|
||||
function Connection<T extends SsoProviderName>({ isDeleted, data, onUpdated }: Props<T>) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const {
|
||||
id: ssoConnectorId,
|
||||
connectorName: ssoConnectorName,
|
||||
providerName,
|
||||
providerConfig,
|
||||
config,
|
||||
} = data;
|
||||
const { id: ssoConnectorId, providerName, providerConfig, config } = data;
|
||||
|
||||
const api = useApi();
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ export default function koaConnectorErrorHandler<StateT, ContextT>(): Middleware
|
|||
case ConnectorErrorCodes.InvalidRequestParameters:
|
||||
case ConnectorErrorCodes.InsufficientRequestParameters:
|
||||
case ConnectorErrorCodes.InvalidConfig:
|
||||
case ConnectorErrorCodes.InvalidCertificate:
|
||||
case ConnectorErrorCodes.InvalidResponse: {
|
||||
throw new RequestError({ code: `connector.${code}`, status: 400 }, data);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { X509Certificate } from 'node:crypto';
|
||||
|
||||
import * as validator from '@authenio/samlify-node-xmllint';
|
||||
import { type Optional, appendPath } from '@silverhand/essentials';
|
||||
import { type Optional, appendPath, tryThat } from '@silverhand/essentials';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { HTTPError, got } from 'got';
|
||||
import * as saml from 'samlify';
|
||||
|
@ -62,24 +62,36 @@ export const parseXmlMetadata = (
|
|||
saml.Constants.wording.certUse.signing
|
||||
);
|
||||
|
||||
try {
|
||||
const certificate = getPemCertificate(rawX509Certificate);
|
||||
const expiresAt = new Date(certificate.validTo).getTime();
|
||||
const certificate = tryThat(
|
||||
() => getPemCertificate(rawX509Certificate),
|
||||
(error) => {
|
||||
throw new SsoConnectorError(SsoConnectorErrorCodes.InvalidCertificate, {
|
||||
config: { ...rawSamlMetadata, x509Certificate: rawX509Certificate },
|
||||
error,
|
||||
});
|
||||
}
|
||||
);
|
||||
const certificateExpiresAt = new Date(certificate.validTo).getTime();
|
||||
|
||||
// The return type of `samlify`
|
||||
return samlIdentityProviderMetadataGuard.parse({
|
||||
...rawSamlMetadata,
|
||||
expiresAt,
|
||||
isValid: expiresAt > Date.now(),
|
||||
x509Certificate: certificate.toJSON(), // This returns the parsed certificate in string-type.
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
const payload = {
|
||||
...rawSamlMetadata,
|
||||
certificateExpiresAt,
|
||||
isCertificateValid: certificateExpiresAt > Date.now(),
|
||||
x509Certificate: certificate.toJSON(), // This returns the parsed certificate in string-type.
|
||||
};
|
||||
|
||||
// The return type of `samlify`
|
||||
const result = samlIdentityProviderMetadataGuard.safeParse(payload);
|
||||
|
||||
if (!result.success) {
|
||||
throw new SsoConnectorError(SsoConnectorErrorCodes.InvalidMetadata, {
|
||||
message: SsoConnectorConfigErrorCodes.InvalidConnectorConfig,
|
||||
metadata: rawSamlMetadata,
|
||||
error,
|
||||
metadata: payload,
|
||||
error: result.error,
|
||||
});
|
||||
}
|
||||
|
||||
return result.data;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,7 @@ import { type JsonObject } from '@logto/schemas';
|
|||
export enum SsoConnectorErrorCodes {
|
||||
InvalidMetadata = 'invalid_metadata',
|
||||
InvalidConfig = 'invalid_config',
|
||||
InvalidCertificate = 'invalid_certificate',
|
||||
AuthorizationFailed = 'authorization_failed',
|
||||
InvalidResponse = 'invalid_response',
|
||||
InvalidRequestParameters = 'invalid_request_parameters',
|
||||
|
@ -18,6 +19,7 @@ export enum SsoConnectorConfigErrorCodes {
|
|||
const connectorErrorCodeMap: { [key in SsoConnectorErrorCodes]: ConnectorErrorCodes } = {
|
||||
[SsoConnectorErrorCodes.InvalidMetadata]: ConnectorErrorCodes.InvalidMetadata,
|
||||
[SsoConnectorErrorCodes.InvalidConfig]: ConnectorErrorCodes.InvalidConfig,
|
||||
[SsoConnectorErrorCodes.InvalidCertificate]: ConnectorErrorCodes.InvalidCertificate,
|
||||
[SsoConnectorErrorCodes.InvalidResponse]: ConnectorErrorCodes.InvalidResponse,
|
||||
[SsoConnectorErrorCodes.InvalidRequestParameters]: ConnectorErrorCodes.InvalidRequestParameters,
|
||||
[SsoConnectorErrorCodes.AuthorizationFailed]: ConnectorErrorCodes.AuthorizationFailed,
|
||||
|
@ -38,6 +40,14 @@ export class SsoConnectorError extends ConnectorError {
|
|||
}
|
||||
);
|
||||
|
||||
constructor(
|
||||
code: SsoConnectorErrorCodes.InvalidCertificate,
|
||||
data: {
|
||||
config: JsonObject | undefined;
|
||||
error?: unknown;
|
||||
}
|
||||
);
|
||||
|
||||
constructor(
|
||||
code: SsoConnectorErrorCodes.InvalidRequestParameters,
|
||||
data: { url: string; params: unknown; error?: unknown }
|
||||
|
|
|
@ -40,8 +40,8 @@ export const samlIdentityProviderMetadataGuard = z.object({
|
|||
entityId: z.string(),
|
||||
signInEndpoint: z.string(),
|
||||
x509Certificate: z.string(),
|
||||
expiresAt: z.number(), // Timestamp in milliseconds.
|
||||
isValid: z.boolean(),
|
||||
certificateExpiresAt: z.number(), // Timestamp in milliseconds.
|
||||
isCertificateValid: z.boolean(),
|
||||
});
|
||||
export type SamlIdentityProviderMetadata = z.infer<typeof samlIdentityProviderMetadataGuard>;
|
||||
|
||||
|
|
|
@ -9,6 +9,9 @@ const connector = {
|
|||
insufficient_request_parameters:
|
||||
'Die Anfrage enthält möglicherweise nicht alle erforderlichen Eingabeparameter.',
|
||||
invalid_config: 'Die Konfiguration des Connectors ist ungültig.',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: 'Die Antwort des Connectors ist ungültig.',
|
||||
template_not_found:
|
||||
'Die richtige Vorlage in der Connector-Konfiguration konnte nicht gefunden werden.',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -88,6 +88,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -8,6 +8,8 @@ const connector = {
|
|||
invalid_request_parameters: 'The request is with wrong input parameter(s).',
|
||||
insufficient_request_parameters: 'The request might miss some input parameters.',
|
||||
invalid_config: "The connector's config is invalid.",
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: "The connector's response is invalid.",
|
||||
template_not_found: 'Unable to find correct template in connector config.',
|
||||
template_not_supported: 'The connector does not support this template type.',
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const single_sign_on = {
|
||||
forbidden_domains: 'Public email domains are not allowed.',
|
||||
duplicated_domains: 'There are duplicate domains.',
|
||||
/** UNTRANSLATED */
|
||||
invalid_domain_format: 'Invalid domain format.',
|
||||
duplicate_connector_name: 'Connector name already exists. Please choose a different name.',
|
||||
};
|
||||
|
|
|
@ -43,6 +43,7 @@ const enterprise_sso_details = {
|
|||
upload_idp_metadata_description_oidc:
|
||||
'Configure the credentials and OIDC token information copied from the identity provider.',
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
email_domain_field_required: 'Email domain is required to enable enterprise SSO.',
|
||||
|
|
|
@ -59,6 +59,7 @@ const enterprise_sso = {
|
|||
idp_entity_id_field_name: 'IdP entity ID (Issuer)',
|
||||
certificate_field_name: 'Signing certificate',
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
client_id_field_name: 'Client ID',
|
||||
|
|
|
@ -8,6 +8,9 @@ const connector = {
|
|||
invalid_request_parameters: 'La solicitud contiene parámetros de entrada incorrectos.',
|
||||
insufficient_request_parameters: 'La solicitud puede faltar algunos parámetros de entrada.',
|
||||
invalid_config: 'La configuración del conector es inválida.',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: 'La respuesta del conector es inválida.',
|
||||
template_not_found:
|
||||
'No se puede encontrar la plantilla correcta en la configuración del conector.',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -88,6 +88,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -8,6 +8,9 @@ const connector = {
|
|||
invalid_request_parameters: "La requête contient des paramètres d'entrée incorrects.",
|
||||
insufficient_request_parameters: 'Certains paramètres peuvent manquer dans la requête.',
|
||||
invalid_config: "La configuration du connecteur n'est pas valide.",
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: "La réponse du connecteur n'est pas valide.",
|
||||
template_not_found: 'Impossible de trouver le bon modèle dans la configuration du connecteur.',
|
||||
template_not_supported: 'Le connecteur ne prend pas en charge ce type de modèle.',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -89,6 +89,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -8,6 +8,9 @@ const connector = {
|
|||
invalid_request_parameters: 'La richiesta contiene parametri di input errati.',
|
||||
insufficient_request_parameters: 'La richiesta potrebbe mancare di alcuni parametri di input.',
|
||||
invalid_config: 'La configurazione del connettore non è valida.',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: 'La risposta del connettore non è valida.',
|
||||
template_not_found:
|
||||
'Impossibile trovare il modello corretto nella configurazione del connettore.',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -88,6 +88,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -8,6 +8,9 @@ const connector = {
|
|||
invalid_request_parameters: 'リクエストに誤った入力パラメータが含まれています。',
|
||||
insufficient_request_parameters: 'リクエストには、入力パラメータが不足している可能性があります。',
|
||||
invalid_config: 'コネクタの設定が無効です。',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: 'コネクタのレスポンスが無効です。',
|
||||
template_not_found: 'コネクタ構成から正しいテンプレートを見つけることができませんでした。',
|
||||
template_not_supported: 'コネクタはこのテンプレートタイプをサポートしていません。',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -88,6 +88,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -8,6 +8,9 @@ const connector = {
|
|||
invalid_request_parameters: '잘못된 요청 파라미터가 있어요.',
|
||||
insufficient_request_parameters: '요청 데이터에서 일부 정보가 없어요.',
|
||||
invalid_config: '연동 설정이 유효하지 않아요.',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: '연동 응답이 유효하지 않아요.',
|
||||
template_not_found: '연동 예제 설정을 찾을 수 없어요.',
|
||||
template_not_supported: '연동이 이 템플릿 타입을 지원하지 않아요.',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -87,6 +87,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -9,6 +9,9 @@ const connector = {
|
|||
'Żądanie jest z nieprawidłowym parametrem wejściowym/lub parametrami wejściowymi.',
|
||||
insufficient_request_parameters: 'Żądanie może nie zawierać niektórych parametrów wejściowych.',
|
||||
invalid_config: 'Konfiguracja łącznika jest nieprawidłowa.',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: 'Odpowiedź łącznika jest nieprawidłowa.',
|
||||
template_not_found: 'Nie można znaleźć poprawnego szablonu w konfiguracji łącznika.',
|
||||
template_not_supported: 'Łącznik nie obsługuje tego typu szablonu.',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -88,6 +88,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -8,6 +8,9 @@ const connector = {
|
|||
invalid_request_parameters: 'A solicitação está com parâmetro(s) de entrada incorreto(s).',
|
||||
insufficient_request_parameters: 'A solicitação pode perder alguns parâmetros de entrada.',
|
||||
invalid_config: 'A configuração do conector é inválida.',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: 'A resposta do conector é inválida.',
|
||||
template_not_found: 'Não foi possível encontrar o modelo correto na configuração do conector.',
|
||||
template_not_supported: 'O conector não suporta esse tipo de modelo.',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -88,6 +88,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -8,6 +8,9 @@ const connector = {
|
|||
invalid_request_parameters: 'O pedido tem parâmetros de entrada inválidos.',
|
||||
insufficient_request_parameters: 'A solicitação pode perder alguns parâmetros de entrada.',
|
||||
invalid_config: 'A configuração do conector é inválida.',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: 'A resposta do conector é inválida.',
|
||||
template_not_found: 'Não foi possível encontrar o modelo correto na configuração do conector.',
|
||||
template_not_supported: 'O conector não suporta este tipo de modelo.',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -88,6 +88,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -8,6 +8,9 @@ const connector = {
|
|||
invalid_request_parameters: 'Запрос содержит неверный входной параметр (ы).',
|
||||
insufficient_request_parameters: 'В запросе может не хватать некоторых входных параметров.',
|
||||
invalid_config: 'Конфигурация коннектора недействительна.',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: 'Ответ коннектора недействителен.',
|
||||
template_not_found: 'Невозможно найти правильный шаблон в конфигурации коннектора.',
|
||||
template_not_supported: 'Коннектор не поддерживает этот тип шаблона.',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -88,6 +88,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -8,6 +8,9 @@ const connector = {
|
|||
invalid_request_parameters: 'İstek yanlış girdi parametreleri ile gönderildi.',
|
||||
insufficient_request_parameters: 'İstek, bazı input parametrelerini atlayabilir.',
|
||||
invalid_config: 'Bağlayıcının ayarları geçersiz.',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: 'Bağlayıcının yanıtı geçersiz.',
|
||||
template_not_found: 'Bağlayıcı yapılandırmasında doğru şablon bulunamıyor.',
|
||||
template_not_supported: 'Bağlayıcı bu şablon türünü desteklemiyor.',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -88,6 +88,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -8,6 +8,9 @@ const connector = {
|
|||
invalid_request_parameters: '请求参数错误',
|
||||
insufficient_request_parameters: '请求参数缺失',
|
||||
invalid_config: '连接器配置错误',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: '连接器错误响应',
|
||||
template_not_found: '无法从连接器配置中找到对应的模板',
|
||||
template_not_supported: '连接器不支持此模板类型。',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -87,6 +87,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -8,6 +8,9 @@ const connector = {
|
|||
invalid_request_parameters: '請求參數錯誤',
|
||||
insufficient_request_parameters: '請求參數缺失',
|
||||
invalid_config: '連接器配置錯誤',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: '連接器錯誤響應',
|
||||
template_not_found: '無法從連接器配置中找到對應的模板',
|
||||
template_not_supported: '連接器不支援此模板類型。',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -87,6 +87,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -8,6 +8,9 @@ const connector = {
|
|||
invalid_request_parameters: '請求參數錯誤',
|
||||
insufficient_request_parameters: '請求參數缺失',
|
||||
invalid_config: '連接器配置錯誤',
|
||||
/** UNTRANSLATED */
|
||||
invalid_certificate:
|
||||
"The connector's certificate is invalid, please make sure the certificate is in PEM encoding.",
|
||||
invalid_response: '連接器錯誤響應',
|
||||
template_not_found: '無法從連接器配置中找到對應的模板',
|
||||
template_not_supported: '連接器不支援此模板類型',
|
||||
|
|
|
@ -82,6 +82,8 @@ const enterprise_sso_details = {
|
|||
/** UNTRANSLATED */
|
||||
upload_idp_metadata_button_text: 'Upload metadata XML file',
|
||||
/** UNTRANSLATED */
|
||||
upload_signing_certificate_button_text: 'Upload signing certificate file',
|
||||
/** UNTRANSLATED */
|
||||
configure_domain_field_info_text:
|
||||
'Add email domain to guide enterprise users to their identity provider for Single Sign-on.',
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -87,6 +87,8 @@ const enterprise_sso = {
|
|||
certificate_field_name: 'Signing certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_placeholder: 'Copy and paste the x509 certificate',
|
||||
/** UNTRANSLATED */
|
||||
certificate_required: 'Signing certificate is required.',
|
||||
},
|
||||
oidc: {
|
||||
/** UNTRANSLATED */
|
||||
|
|
|
@ -6,6 +6,7 @@ export enum ConnectorErrorCodes {
|
|||
InvalidRequestParameters = 'invalid_request_parameters',
|
||||
InsufficientRequestParameters = 'insufficient_request_parameters',
|
||||
InvalidConfig = 'invalid_config',
|
||||
InvalidCertificate = 'invalid_certificate',
|
||||
InvalidResponse = 'invalid_response',
|
||||
/** The template is not found for the given type. */
|
||||
TemplateNotFound = 'template_not_found',
|
||||
|
|
Loading…
Add table
Reference in a new issue