mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
chore(core): Show error message for unsupported SAML bindings (#15461)
This commit is contained in:
@@ -48,6 +48,9 @@ const InvalidSamlSetting: Settings = {
|
||||
}),
|
||||
};
|
||||
|
||||
const SamlMetadataWithoutRedirectBinding =
|
||||
'<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://saml.example.com/entityid" validUntil="2035-05-07T13:33:47.181Z">\n <md:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">\n <md:KeyDescriptor use="signing">\n <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">\n <ds:X509Data>\n <ds:X509Certificate>MIIC4jCCAcoCCQC33wnybT5QZDANBgkqhkiG9w0BAQsFADAyMQswCQYDVQQGEwJV\nSzEPMA0GA1UECgwGQm94eUhRMRIwEAYDVQQDDAlNb2NrIFNBTUwwIBcNMjIwMjI4\nMjE0NjM4WhgPMzAyMTA3MDEyMTQ2MzhaMDIxCzAJBgNVBAYTAlVLMQ8wDQYDVQQK\nDAZCb3h5SFExEjAQBgNVBAMMCU1vY2sgU0FNTDCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBALGfYettMsct1T6tVUwTudNJH5Pnb9GGnkXi9Zw/e6x45DD0\nRuRONbFlJ2T4RjAE/uG+AjXxXQ8o2SZfb9+GgmCHuTJFNgHoZ1nFVXCmb/Hg8Hpd\n4vOAGXndixaReOiq3EH5XvpMjMkJ3+8+9VYMzMZOjkgQtAqO36eAFFfNKX7dTj3V\npwLkvz6/KFCq8OAwY+AUi4eZm5J57D31GzjHwfjH9WTeX0MyndmnNB1qV75qQR3b\n2/W5sGHRv+9AarggJkF+ptUkXoLtVA51wcfYm6hILptpde5FQC8RWY1YrswBWAEZ\nNfyrR4JeSweElNHg4NVOs4TwGjOPwWGqzTfgTlECAwEAATANBgkqhkiG9w0BAQsF\nAAOCAQEAAYRlYflSXAWoZpFfwNiCQVE5d9zZ0DPzNdWhAybXcTyMf0z5mDf6FWBW\n5Gyoi9u3EMEDnzLcJNkwJAAc39Apa4I2/tml+Jy29dk8bTyX6m93ngmCgdLh5Za4\nkhuU3AM3L63g7VexCuO7kwkjh/+LqdcIXsVGO6XDfu2QOs1Xpe9zIzLpwm/RNYeX\nUjbSj5ce/jekpAw7qyVVL4xOyh8AtUW1ek3wIw1MJvEgEPt0d16oshWJpoS1OT8L\nr/22SvYEo3EmSGdTVGgk3x3s+A0qWAqTcyjr7Q4s/GKYRFfomGwz0TZ4Iw1ZN99M\nm0eo2USlSRTVl7QHRTuiuSThHpLKQQ==</ds:X509Certificate>\n </ds:X509Data>\n </ds:KeyInfo>\n </md:KeyDescriptor>\n <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>\n <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://mocksaml.com/api/saml/sso"/>\n </md:IDPSSODescriptor>\n</md:EntityDescriptor>';
|
||||
|
||||
const SamlSettingWithInvalidUrl: Settings = {
|
||||
loadOnStartup: true,
|
||||
key: SAML_PREFERENCES_DB_KEY,
|
||||
@@ -342,6 +345,14 @@ describe('SamlService', () => {
|
||||
expect(samlService.samlPreferences.metadataUrl).toBe(metadataUrlTestData);
|
||||
});
|
||||
|
||||
test('does throw `InvalidSamlMetadataError` when a metadata does not contain redirect binding', async () => {
|
||||
await expect(
|
||||
samlService.setSamlPreferences({
|
||||
metadata: SamlMetadataWithoutRedirectBinding,
|
||||
}),
|
||||
).rejects.toThrowError(InvalidSamlMetadataError);
|
||||
});
|
||||
|
||||
test('does throw `InvalidSamlMetadataError` when a metadata url is not a valid url', async () => {
|
||||
await expect(
|
||||
samlService.setSamlPreferences({
|
||||
@@ -396,6 +407,17 @@ describe('SamlService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getIdentityProviderInstance', () => {
|
||||
test('does throw `InvalidSamlMetadataError` when a metadata does not contain redirect binding', async () => {
|
||||
await samlService.loadPreferencesWithoutValidation({
|
||||
metadata: SamlMetadataWithoutRedirectBinding,
|
||||
});
|
||||
expect(() => samlService.getIdentityProviderInstance(true)).toThrowError(
|
||||
InvalidSamlMetadataError,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reset', () => {
|
||||
test('disables saml login and deletes the saml `features.saml` key in the db', async () => {
|
||||
// ARRANGE
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { UserError } from 'n8n-workflow';
|
||||
|
||||
export class InvalidSamlMetadataError extends UserError {
|
||||
constructor() {
|
||||
super('Invalid SAML metadata');
|
||||
constructor(detail: string = '') {
|
||||
super(`Invalid SAML metadata${detail ? ': ' + detail : ''}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { Service } from '@n8n/di';
|
||||
import { Logger } from 'n8n-core';
|
||||
import { Constants, IdentityProvider } from 'samlify';
|
||||
import type { IdentityProviderInstance } from 'samlify';
|
||||
import type { XMLFileInfo, XMLLintOptions, XMLValidationResult } from 'xmllint-wasm';
|
||||
|
||||
import { InvalidSamlMetadataError } from './errors/invalid-saml-metadata.error';
|
||||
|
||||
@Service()
|
||||
export class SamlValidator {
|
||||
private xmlMetadata: XMLFileInfo;
|
||||
@@ -21,8 +25,24 @@ export class SamlValidator {
|
||||
this.xmllint = await import('xmllint-wasm');
|
||||
}
|
||||
|
||||
validateIdentiyProvider(idp: IdentityProviderInstance) {
|
||||
const binding = idp.entityMeta.getSingleSignOnService(Constants.wording.binding.redirect);
|
||||
if (typeof binding !== 'string') {
|
||||
throw new InvalidSamlMetadataError('only SAML redirect binding is supported.');
|
||||
}
|
||||
}
|
||||
|
||||
async validateMetadata(metadata: string): Promise<boolean> {
|
||||
return await this.validateXml('metadata', metadata);
|
||||
const validXML = await this.validateXml('metadata', metadata);
|
||||
|
||||
if (validXML) {
|
||||
const idp = IdentityProvider({
|
||||
metadata,
|
||||
});
|
||||
this.validateIdentiyProvider(idp);
|
||||
}
|
||||
|
||||
return validXML;
|
||||
}
|
||||
|
||||
async validateResponse(response: string): Promise<boolean> {
|
||||
|
||||
@@ -7,7 +7,7 @@ import type express from 'express';
|
||||
import https from 'https';
|
||||
import { Logger } from 'n8n-core';
|
||||
import { jsonParse, UnexpectedError } from 'n8n-workflow';
|
||||
import type { IdentityProviderInstance, ServiceProviderInstance } from 'samlify';
|
||||
import { type IdentityProviderInstance, type ServiceProviderInstance } from 'samlify';
|
||||
import type { BindingContext, PostBindingContext } from 'samlify/types/src/entity';
|
||||
|
||||
import { AuthError } from '@/errors/response-errors/auth.error';
|
||||
@@ -135,6 +135,8 @@ export class SamlService {
|
||||
});
|
||||
}
|
||||
|
||||
this.validator.validateIdentiyProvider(this.identityProviderInstance);
|
||||
|
||||
return this.identityProviderInstance;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user