From f6ce27b61e2a14f27b9c38e72ffa037b3302d738 Mon Sep 17 00:00:00 2001 From: Antoine Moreaux Date: Wed, 5 Feb 2025 21:46:38 +0100 Subject: [PATCH] refactor(security): extend SAML prefix handling (#10047) Revised parsing logic to handle multiple XML prefixes for SAML metadata, improving flexibility in handling diverse metadata structures. Added corresponding test case to ensure robustness of the implementation. --- .../parseSAMLMetadataFromXMLFile.test.ts | 25 +++++++++++++++++ .../utils/parseSAMLMetadataFromXMLFile.ts | 27 ++++++++++++------- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/security/utils/__tests__/parseSAMLMetadataFromXMLFile.test.ts b/packages/twenty-front/src/modules/settings/security/utils/__tests__/parseSAMLMetadataFromXMLFile.test.ts index e1d79168e823..0bf18a404fbc 100644 --- a/packages/twenty-front/src/modules/settings/security/utils/__tests__/parseSAMLMetadataFromXMLFile.test.ts +++ b/packages/twenty-front/src/modules/settings/security/utils/__tests__/parseSAMLMetadataFromXMLFile.test.ts @@ -28,6 +28,31 @@ describe('parseSAMLMetadataFromXMLFile', () => { }, }); }); + it('should parse SAML metadata from XML file with prefix', () => { + const xmlString = ` + + + + + test + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + + + +`; + const result = parseSAMLMetadataFromXMLFile(xmlString); + expect(result).toEqual({ + success: true, + data: { + entityID: 'https://test.com', + ssoUrl: 'https://test.com', + certificate: 'test', + }, + }); + }); it('should return error if XML is invalid', () => { const xmlString = 'invalid xml'; const result = parseSAMLMetadataFromXMLFile(xmlString); diff --git a/packages/twenty-front/src/modules/settings/security/utils/parseSAMLMetadataFromXMLFile.ts b/packages/twenty-front/src/modules/settings/security/utils/parseSAMLMetadataFromXMLFile.ts index b67d9105970f..39c1469f3b82 100644 --- a/packages/twenty-front/src/modules/settings/security/utils/parseSAMLMetadataFromXMLFile.ts +++ b/packages/twenty-front/src/modules/settings/security/utils/parseSAMLMetadataFromXMLFile.ts @@ -8,26 +8,36 @@ const validator = z.object({ certificate: z.string().min(1), }); +const allPrefix = ['md', 'ns0', 'ns2', 'dsig', 'ds']; + const getByPrefixAndKey = ( xmlDoc: Document | Element, key: string, - prefix = 'md', + prefixList = [...allPrefix], ): Element | undefined => { + if (prefixList.length === 0) return undefined; return ( - xmlDoc.getElementsByTagName(`${prefix}:${key}`)?.[0] ?? - xmlDoc.getElementsByTagName(`${key}`)?.[0] + xmlDoc.getElementsByTagName(`${prefixList[0]}:${key}`)?.[0] ?? + getByPrefixAndKey(xmlDoc, key, prefixList.slice(1)) ?? + xmlDoc.getElementsByTagName(key)?.[0] ); }; const getAllByPrefixAndKey = ( xmlDoc: Document | Element, key: string, - prefix = 'md', -) => { - const withPrefix = xmlDoc.getElementsByTagName(`${prefix}:${key}`); + prefixList = [...allPrefix], +): Array => { + const withPrefix = xmlDoc.getElementsByTagName(`${prefixList[0]}:${key}`); + if (withPrefix.length !== 0) { return Array.from(withPrefix); } + + if (prefixList.length > 0) { + return getAllByPrefixAndKey(xmlDoc, key, prefixList.slice(1)); + } + return Array.from(xmlDoc.getElementsByTagName(`${key}`)); }; @@ -52,16 +62,15 @@ export const parseSAMLMetadataFromXMLFile = ( const keyDescriptors = getByPrefixAndKey(IDPSSODescriptor, 'KeyDescriptor'); if (!keyDescriptors) throw new Error('No KeyDescriptor found'); - const keyInfo = getByPrefixAndKey(keyDescriptors, 'KeyInfo', 'ds'); + const keyInfo = getByPrefixAndKey(keyDescriptors, 'KeyInfo'); if (!keyInfo) throw new Error('No KeyInfo found'); - const x509Data = getByPrefixAndKey(keyInfo, 'X509Data', 'ds'); + const x509Data = getByPrefixAndKey(keyInfo, 'X509Data'); if (!x509Data) throw new Error('No X509Data found'); const x509Certificate = getByPrefixAndKey( x509Data, 'X509Certificate', - 'ds', )?.textContent?.trim(); if (!x509Certificate) throw new Error('No X509Certificate found');