fix(auth): Improve error management with sso + fix microsoft saml (#9799)
Fix #9760 #9758
This commit is contained in:
@ -62,12 +62,12 @@ export const SettingsSSOSAMLForm = () => {
|
||||
if (isDefined(e.target.files)) {
|
||||
const text = await e.target.files[0].text();
|
||||
const samlMetadataParsed = parseSAMLMetadataFromXMLFile(text);
|
||||
e.target.value = '';
|
||||
if (!samlMetadataParsed.success) {
|
||||
enqueueSnackBar('Invalid File', {
|
||||
return enqueueSnackBar('Invalid File', {
|
||||
variant: SnackBarVariant.Error,
|
||||
duration: 2000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
setValue('ssoURL', samlMetadataParsed.data.ssoUrl);
|
||||
setValue('certificate', samlMetadataParsed.data.certificate);
|
||||
|
||||
@ -8,6 +8,29 @@ const validator = z.object({
|
||||
certificate: z.string().min(1),
|
||||
});
|
||||
|
||||
const getByPrefixAndKey = (
|
||||
xmlDoc: Document | Element,
|
||||
key: string,
|
||||
prefix = 'md',
|
||||
): Element | undefined => {
|
||||
return (
|
||||
xmlDoc.getElementsByTagName(`${prefix}:${key}`)?.[0] ??
|
||||
xmlDoc.getElementsByTagName(`${key}`)?.[0]
|
||||
);
|
||||
};
|
||||
|
||||
const getAllByPrefixAndKey = (
|
||||
xmlDoc: Document | Element,
|
||||
key: string,
|
||||
prefix = 'md',
|
||||
) => {
|
||||
const withPrefix = xmlDoc.getElementsByTagName(`${prefix}:${key}`);
|
||||
if (withPrefix.length !== 0) {
|
||||
return Array.from(withPrefix);
|
||||
}
|
||||
return Array.from(xmlDoc.getElementsByTagName(`${key}`));
|
||||
};
|
||||
|
||||
export const parseSAMLMetadataFromXMLFile = (
|
||||
xmlString: string,
|
||||
):
|
||||
@ -20,33 +43,44 @@ export const parseSAMLMetadataFromXMLFile = (
|
||||
throw new Error('Error parsing XML');
|
||||
}
|
||||
|
||||
const entityDescriptor = xmlDoc.getElementsByTagName(
|
||||
'md:EntityDescriptor',
|
||||
)?.[0];
|
||||
const idpSSODescriptor = xmlDoc.getElementsByTagName(
|
||||
'md:IDPSSODescriptor',
|
||||
)?.[0];
|
||||
const keyDescriptor = xmlDoc.getElementsByTagName('md:KeyDescriptor')[0];
|
||||
const keyInfo = keyDescriptor?.getElementsByTagName('ds:KeyInfo')[0];
|
||||
const x509Data = keyInfo?.getElementsByTagName('ds:X509Data')[0];
|
||||
const x509Certificate = x509Data
|
||||
?.getElementsByTagName('ds:X509Certificate')?.[0]
|
||||
.textContent?.trim();
|
||||
const entityDescriptor = getByPrefixAndKey(xmlDoc, 'EntityDescriptor');
|
||||
if (!entityDescriptor) throw new Error('No EntityDescriptor found');
|
||||
|
||||
const singleSignOnServices = Array.from(
|
||||
idpSSODescriptor.getElementsByTagName('md:SingleSignOnService'),
|
||||
).map((service) => ({
|
||||
Binding: service.getAttribute('Binding'),
|
||||
Location: service.getAttribute('Location'),
|
||||
}));
|
||||
const IDPSSODescriptor = getByPrefixAndKey(xmlDoc, 'IDPSSODescriptor');
|
||||
if (!IDPSSODescriptor) throw new Error('No IDPSSODescriptor found');
|
||||
|
||||
const keyDescriptors = getByPrefixAndKey(IDPSSODescriptor, 'KeyDescriptor');
|
||||
if (!keyDescriptors) throw new Error('No KeyDescriptor found');
|
||||
|
||||
const keyInfo = getByPrefixAndKey(keyDescriptors, 'KeyInfo', 'ds');
|
||||
if (!keyInfo) throw new Error('No KeyInfo found');
|
||||
|
||||
const x509Data = getByPrefixAndKey(keyInfo, 'X509Data', 'ds');
|
||||
if (!x509Data) throw new Error('No X509Data found');
|
||||
|
||||
const x509Certificate = getByPrefixAndKey(
|
||||
x509Data,
|
||||
'X509Certificate',
|
||||
'ds',
|
||||
)?.textContent?.trim();
|
||||
if (!x509Certificate) throw new Error('No X509Certificate found');
|
||||
|
||||
const singleSignOnServices = getAllByPrefixAndKey(
|
||||
IDPSSODescriptor,
|
||||
'SingleSignOnService',
|
||||
);
|
||||
|
||||
const result = {
|
||||
ssoUrl: singleSignOnServices.find((singleSignOnService) => {
|
||||
return (
|
||||
singleSignOnService.Binding ===
|
||||
'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
|
||||
);
|
||||
})?.Location,
|
||||
ssoUrl: singleSignOnServices
|
||||
.map((service) => ({
|
||||
Binding: service.getAttribute('Binding'),
|
||||
Location: service.getAttribute('Location'),
|
||||
}))
|
||||
.find(
|
||||
(singleSignOnService) =>
|
||||
singleSignOnService.Binding ===
|
||||
'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
|
||||
)?.Location,
|
||||
certificate: x509Certificate,
|
||||
entityID: entityDescriptor?.getAttribute('entityID'),
|
||||
};
|
||||
|
||||
@ -5,17 +5,17 @@ import { z } from 'zod';
|
||||
export const SSOIdentitiesProvidersOIDCParamsSchema = z
|
||||
.object({
|
||||
type: z.literal('OIDC'),
|
||||
clientID: z.string().optional(),
|
||||
clientSecret: z.string().optional(),
|
||||
clientID: z.string().nonempty(),
|
||||
clientSecret: z.string().nonempty(),
|
||||
})
|
||||
.required();
|
||||
|
||||
export const SSOIdentitiesProvidersSAMLParamsSchema = z
|
||||
.object({
|
||||
type: z.literal('SAML'),
|
||||
id: z.string().optional(),
|
||||
ssoURL: z.string().url().optional(),
|
||||
certificate: z.string().optional(),
|
||||
id: z.string().nonempty(),
|
||||
ssoURL: z.string().url().nonempty(),
|
||||
certificate: z.string().nonempty(),
|
||||
})
|
||||
.required();
|
||||
|
||||
@ -27,8 +27,8 @@ export const SSOIdentitiesProvidersParamsSchema = z
|
||||
.and(
|
||||
z
|
||||
.object({
|
||||
name: z.string().min(1),
|
||||
issuer: z.string().url().optional(),
|
||||
name: z.string().nonempty(),
|
||||
issuer: z.string().url().nonempty(),
|
||||
})
|
||||
.required(),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user