mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(core): Improve SAML connection test result views (#5981)
* improve test result views * refactor * lint fix
This commit is contained in:
parent
18d5156994
commit
4c994faec1
|
@ -13,7 +13,7 @@ import { getInitSSOFormView } from '../views/initSsoPost';
|
|||
import { issueCookie } from '@/auth/jwt';
|
||||
import { validate } from 'class-validator';
|
||||
import type { PostBindingContext } from 'samlify/types/src/entity';
|
||||
import { isSamlLicensedAndEnabled } from '../samlHelpers';
|
||||
import { isConnectionTestRequest, isSamlLicensedAndEnabled } from '../samlHelpers';
|
||||
import type { SamlLoginBinding } from '../types';
|
||||
import { AuthenticatedRequest } from '@/requests';
|
||||
import {
|
||||
|
@ -21,6 +21,8 @@ import {
|
|||
getServiceProviderEntityId,
|
||||
getServiceProviderReturnUrl,
|
||||
} from '../serviceProvider.ee';
|
||||
import { getSamlConnectionTestSuccessView } from '../views/samlConnectionTestSuccess';
|
||||
import { getSamlConnectionTestFailedView } from '../views/samlConnectionTestFailed';
|
||||
|
||||
@RestController('/sso/saml')
|
||||
export class SamlController {
|
||||
|
@ -106,11 +108,15 @@ export class SamlController {
|
|||
res: express.Response,
|
||||
binding: SamlLoginBinding,
|
||||
) {
|
||||
const loginResult = await this.samlService.handleSamlLogin(req, binding);
|
||||
if (loginResult) {
|
||||
// return attributes if this is a test connection
|
||||
if (req.body.RelayState && req.body.RelayState === getServiceProviderConfigTestReturnUrl()) {
|
||||
return res.status(202).send(loginResult.attributes);
|
||||
try {
|
||||
const loginResult = await this.samlService.handleSamlLogin(req, binding);
|
||||
// if RelayState is set to the test connection Url, this is a test connection
|
||||
if (isConnectionTestRequest(req)) {
|
||||
if (loginResult.authenticatedUser) {
|
||||
return res.send(getSamlConnectionTestSuccessView(loginResult.attributes));
|
||||
} else {
|
||||
return res.send(getSamlConnectionTestFailedView('', loginResult.attributes));
|
||||
}
|
||||
}
|
||||
if (loginResult.authenticatedUser) {
|
||||
// Only sign in user if SAML is enabled, otherwise treat as test connection
|
||||
|
@ -125,8 +131,13 @@ export class SamlController {
|
|||
return res.status(202).send(loginResult.attributes);
|
||||
}
|
||||
}
|
||||
throw new AuthError('SAML Authentication failed');
|
||||
} catch (error) {
|
||||
if (isConnectionTestRequest(req)) {
|
||||
return res.send(getSamlConnectionTestFailedView((error as Error).message));
|
||||
}
|
||||
throw new AuthError('SAML Authentication failed: ' + (error as Error).message);
|
||||
}
|
||||
throw new AuthError('SAML Authentication failed');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -139,14 +139,11 @@ export class SamlService {
|
|||
async handleSamlLogin(
|
||||
req: express.Request,
|
||||
binding: SamlLoginBinding,
|
||||
): Promise<
|
||||
| {
|
||||
authenticatedUser: User | undefined;
|
||||
attributes: SamlUserAttributes;
|
||||
onboardingRequired: boolean;
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
): Promise<{
|
||||
authenticatedUser: User | undefined;
|
||||
attributes: SamlUserAttributes;
|
||||
onboardingRequired: boolean;
|
||||
}> {
|
||||
const attributes = await this.getAttributesFromLoginResponse(req, binding);
|
||||
if (attributes.email) {
|
||||
const user = await Db.collections.User.findOne({
|
||||
|
@ -187,7 +184,11 @@ export class SamlService {
|
|||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
return {
|
||||
authenticatedUser: undefined,
|
||||
attributes,
|
||||
onboardingRequired: false,
|
||||
};
|
||||
}
|
||||
|
||||
async setSamlPreferences(prefs: SamlPreferences): Promise<SamlPreferences | undefined> {
|
||||
|
|
|
@ -18,6 +18,8 @@ import {
|
|||
isSamlCurrentAuthenticationMethod,
|
||||
setCurrentAuthenticationMethod,
|
||||
} from '../ssoHelpers';
|
||||
import { getServiceProviderConfigTestReturnUrl } from './serviceProvider.ee';
|
||||
import type { SamlConfiguration } from './types/requests';
|
||||
/**
|
||||
* Check whether the SAML feature is licensed and enabled in the instance
|
||||
*/
|
||||
|
@ -173,3 +175,7 @@ export function getMappedSamlAttributesFromFlowResult(
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function isConnectionTestRequest(req: SamlConfiguration.AcsRequest): boolean {
|
||||
return req.body.RelayState === getServiceProviderConfigTestReturnUrl();
|
||||
}
|
||||
|
|
42
packages/cli/src/sso/saml/views/samlConnectionTestFailed.ts
Normal file
42
packages/cli/src/sso/saml/views/samlConnectionTestFailed.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import type { SamlUserAttributes } from '../types/samlUserAttributes';
|
||||
|
||||
export function getSamlConnectionTestFailedView(
|
||||
message: string,
|
||||
attributes?: SamlUserAttributes,
|
||||
): string {
|
||||
return `
|
||||
<http>
|
||||
<head>
|
||||
<title>n8n - SAML Connection Test Result</title>
|
||||
<style>
|
||||
body { background: rgb(251,252,254); font-family: 'Open Sans', sans-serif; padding: 10px; margin: auto; width: 500px; top: 40%; position: relative; }
|
||||
h1 { color: rgb(240, 60, 60); font-size: 16px; font-weight: 400; margin: 0 0 10px 0; }
|
||||
h2 { color: rgb(0, 0, 0); font-size: 12px; font-weight: 400; margin: 0 0 10px 0; }
|
||||
button { border: 1px solid rgb(219, 223, 231); background: rgb(255, 255, 255); border-radius: 4px; padding: 10px; }
|
||||
ul { border: 1px solid rgb(219, 223, 231); border-radius: 4px; padding: 10px; }
|
||||
li { decoration: none; list-style: none; margin: 0 0 0px 0; color: rgb(125, 125, 125); font-size: 12px;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="text-align:center">
|
||||
<h1>SAML Connection Test failed</h1>
|
||||
<h2>${message ?? 'A common issue could be that no email attribute is set'}</h2>
|
||||
<button onclick="window.close()">You can close this window now</button>
|
||||
<p></p>
|
||||
${
|
||||
attributes
|
||||
? `
|
||||
<h2>Here are the attributes returned by your SAML IdP:</h2>
|
||||
<ul>
|
||||
<li><strong>Email:</strong> ${attributes?.email ?? '(n/a)'}</li>
|
||||
<li><strong>First Name:</strong> ${attributes?.firstName ?? '(n/a)'}</li>
|
||||
<li><strong>Last Name:</strong> ${attributes?.lastName ?? '(n/a)'}</li>
|
||||
<li><strong>UPN:</strong> ${attributes?.userPrincipalName ?? '(n/a)'}</li>
|
||||
</ul>`
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
</body>
|
||||
</http>
|
||||
`;
|
||||
}
|
33
packages/cli/src/sso/saml/views/samlConnectionTestSuccess.ts
Normal file
33
packages/cli/src/sso/saml/views/samlConnectionTestSuccess.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import type { SamlUserAttributes } from '../types/samlUserAttributes';
|
||||
|
||||
export function getSamlConnectionTestSuccessView(attributes: SamlUserAttributes): string {
|
||||
return `
|
||||
<http>
|
||||
<head>
|
||||
<title>n8n - SAML Connection Test Result</title>
|
||||
<style>
|
||||
body { background: rgb(251,252,254); font-family: 'Open Sans', sans-serif; padding: 10px; margin: auto; width: 500px; top: 40%; position: relative; }
|
||||
h1 { color: rgb(0, 0, 0); font-size: 16px; font-weight: 400; margin: 0 0 10px 0; }
|
||||
h2 { color: rgb(0, 0, 0); font-size: 12px; font-weight: 400; margin: 0 0 10px 0; }
|
||||
button { border: 1px solid rgb(219, 223, 231); background: rgb(255, 255, 255); border-radius: 4px; padding: 10px; }
|
||||
ul { border: 1px solid rgb(219, 223, 231); border-radius: 4px; padding: 10px; }
|
||||
li { decoration: none; list-style: none; margin: 0 0 0px 0; color: rgb(125, 125, 125); font-size: 12px;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="text-align:center">
|
||||
<h1>SAML Connection Test was successful</h1>
|
||||
<button onclick="window.close()">You can close this window now</button>
|
||||
<p></p>
|
||||
<h2>Here are the attributes returned by your SAML IdP:</h2>
|
||||
<ul>
|
||||
<li><strong>Email:</strong> ${attributes.email ?? '(n/a)'}</li>
|
||||
<li><strong>First Name:</strong> ${attributes.firstName ?? '(n/a)'}</li>
|
||||
<li><strong>Last Name:</strong> ${attributes.lastName ?? '(n/a)'}</li>
|
||||
<li><strong>UPN:</strong> ${attributes.userPrincipalName ?? '(n/a)'}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</http>
|
||||
`;
|
||||
}
|
Loading…
Reference in a new issue