Files
Automata/packages/cli/src/sso/saml/saml-validator.ts
Iván Ovejero f667b384c9 refactor(core): Standardize filenames in cli (no-changelog) (#10484)
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
2024-08-22 11:10:37 +02:00

141 lines
3.8 KiB
TypeScript

import { Container } from 'typedi';
import type { XMLFileInfo } from 'xmllint-wasm';
import { Logger } from '@/logger';
let xml: XMLFileInfo;
let xmldsigCore: XMLFileInfo;
let xmlXenc: XMLFileInfo;
let xmlMetadata: XMLFileInfo;
let xmlAssertion: XMLFileInfo;
let xmlProtocol: XMLFileInfo;
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
let xmllintWasm: typeof import('xmllint-wasm') | undefined;
// dynamically load schema files
async function loadSchemas(): Promise<void> {
if (!xml || xml.contents === '') {
Container.get(Logger).debug('Loading XML schema files for SAML validation into memory');
const f = await import('./schema/xml.xsd');
xml = {
fileName: 'xml.xsd',
contents: f.xsdXml,
};
}
if (!xmldsigCore || xmldsigCore.contents === '') {
const f = await import('./schema/xmldsig-core-schema.xsd');
xmldsigCore = {
fileName: 'xmldsig-core-schema.xsd',
contents: f.xsdXmldsigCore,
};
}
if (!xmlXenc || xmlXenc.contents === '') {
const f = await import('./schema/xenc-schema.xsd');
xmlXenc = {
fileName: 'xenc-schema.xsd',
contents: f.xsdXenc,
};
}
if (!xmlMetadata || xmlMetadata.contents === '') {
const f = await import('./schema/saml-schema-metadata-2.0.xsd');
xmlMetadata = {
fileName: 'saml-schema-metadata-2.0.xsd',
contents: f.xsdSamlSchemaMetadata20,
};
}
if (!xmlAssertion || xmlAssertion.contents === '') {
const f = await import('./schema/saml-schema-assertion-2.0.xsd');
xmlAssertion = {
fileName: 'saml-schema-assertion-2.0.xsd',
contents: f.xsdSamlSchemaAssertion20,
};
}
if (!xmlProtocol || xmlProtocol.contents === '') {
const f = await import('./schema/saml-schema-protocol-2.0.xsd');
xmlProtocol = {
fileName: 'saml-schema-protocol-2.0.xsd',
contents: f.xsdSamlSchemaProtocol20,
};
}
}
// dynamically load xmllint-wasm
async function loadXmllintWasm(): Promise<void> {
if (xmllintWasm === undefined) {
Container.get(Logger).debug('Loading xmllint-wasm library into memory');
xmllintWasm = await import('xmllint-wasm');
}
}
export async function validateMetadata(metadata: string): Promise<boolean> {
const logger = Container.get(Logger);
try {
await loadXmllintWasm();
await loadSchemas();
const validationResult = await xmllintWasm?.validateXML({
xml: [
{
fileName: 'metadata.xml',
contents: metadata,
},
],
extension: 'schema',
schema: [xmlMetadata],
preload: [xmlProtocol, xmlAssertion, xmldsigCore, xmlXenc, xml],
});
if (validationResult?.valid) {
logger.debug('SAML Metadata is valid');
return true;
} else {
logger.warn('SAML Validate Metadata: Invalid metadata');
logger.warn(
validationResult
? validationResult.errors
.map((error) => `${error.message} - ${error.rawMessage}`)
.join('\n')
: '',
);
}
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
logger.warn(error);
}
return false;
}
export async function validateResponse(response: string): Promise<boolean> {
const logger = Container.get(Logger);
try {
await loadXmllintWasm();
await loadSchemas();
const validationResult = await xmllintWasm?.validateXML({
xml: [
{
fileName: 'response.xml',
contents: response,
},
],
extension: 'schema',
schema: [xmlProtocol],
preload: [xmlMetadata, xmlAssertion, xmldsigCore, xmlXenc, xml],
});
if (validationResult?.valid) {
logger.debug('SAML Response is valid');
return true;
} else {
logger.warn('SAML Validate Response: Failed');
logger.warn(
validationResult
? validationResult.errors
.map((error) => `${error.message} - ${error.rawMessage}`)
.join('\n')
: '',
);
}
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
logger.warn(error);
}
return false;
}