mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
perf(core): Lazyload LDAP during bootup (#15907)
This commit is contained in:
@@ -1,2 +1 @@
|
||||
export * from './methods/email';
|
||||
export * from './methods/ldap';
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import type { User } from '@n8n/db';
|
||||
import { Container } from '@n8n/di';
|
||||
|
||||
import { EventService } from '@/events/event.service';
|
||||
import {
|
||||
createLdapUserOnLocalDb,
|
||||
getUserByEmail,
|
||||
getAuthIdentityByLdapId,
|
||||
isLdapEnabled,
|
||||
mapLdapAttributesToUser,
|
||||
createLdapAuthIdentity,
|
||||
updateLdapUserOnLocalDb,
|
||||
} from '@/ldap.ee/helpers.ee';
|
||||
import { LdapService } from '@/ldap.ee/ldap.service.ee';
|
||||
|
||||
export const handleLdapLogin = async (
|
||||
loginId: string,
|
||||
password: string,
|
||||
): Promise<User | undefined> => {
|
||||
if (!isLdapEnabled()) return undefined;
|
||||
|
||||
const ldapService = Container.get(LdapService);
|
||||
|
||||
if (!ldapService.config.loginEnabled) return undefined;
|
||||
|
||||
const { loginIdAttribute, userFilter } = ldapService.config;
|
||||
|
||||
const ldapUser = await ldapService.findAndAuthenticateLdapUser(
|
||||
loginId,
|
||||
password,
|
||||
loginIdAttribute,
|
||||
userFilter,
|
||||
);
|
||||
|
||||
if (!ldapUser) return undefined;
|
||||
|
||||
const [ldapId, ldapAttributesValues] = mapLdapAttributesToUser(ldapUser, ldapService.config);
|
||||
|
||||
const { email: emailAttributeValue } = ldapAttributesValues;
|
||||
|
||||
if (!ldapId || !emailAttributeValue) return undefined;
|
||||
|
||||
const ldapAuthIdentity = await getAuthIdentityByLdapId(ldapId);
|
||||
if (!ldapAuthIdentity) {
|
||||
const emailUser = await getUserByEmail(emailAttributeValue);
|
||||
|
||||
// check if there is an email user with the same email as the authenticated LDAP user trying to log-in
|
||||
if (emailUser && emailUser.email === emailAttributeValue) {
|
||||
const identity = await createLdapAuthIdentity(emailUser, ldapId);
|
||||
await updateLdapUserOnLocalDb(identity, ldapAttributesValues);
|
||||
} else {
|
||||
const user = await createLdapUserOnLocalDb(ldapAttributesValues, ldapId);
|
||||
Container.get(EventService).emit('user-signed-up', {
|
||||
user,
|
||||
userType: 'ldap',
|
||||
wasDisabledLdapUser: false,
|
||||
});
|
||||
return user;
|
||||
}
|
||||
} else {
|
||||
if (ldapAuthIdentity.user) {
|
||||
if (ldapAuthIdentity.user.disabled) return undefined;
|
||||
await updateLdapUserOnLocalDb(ldapAuthIdentity, ldapAttributesValues);
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the user again as user's data might have been updated
|
||||
return (await getAuthIdentityByLdapId(ldapId))?.user;
|
||||
};
|
||||
@@ -10,6 +10,7 @@ import * as auth from '@/auth';
|
||||
import { AuthService } from '@/auth/auth.service';
|
||||
import config from '@/config';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import { LdapService } from '@/ldap.ee/ldap.service.ee';
|
||||
import { License } from '@/license';
|
||||
import { MfaService } from '@/mfa/mfa.service';
|
||||
import { PostHogClient } from '@/posthog';
|
||||
@@ -32,6 +33,7 @@ describe('AuthController', () => {
|
||||
mockInstance(UserRepository);
|
||||
mockInstance(PostHogClient);
|
||||
mockInstance(License);
|
||||
const ldapService = mockInstance(LdapService);
|
||||
const controller = Container.get(AuthController);
|
||||
const userService = Container.get(UserService);
|
||||
const authService = Container.get(AuthService);
|
||||
@@ -65,7 +67,7 @@ describe('AuthController', () => {
|
||||
|
||||
mockedAuth.handleEmailLogin.mockResolvedValue(member);
|
||||
|
||||
mockedAuth.handleLdapLogin.mockResolvedValue(member);
|
||||
ldapService.handleLdapLogin.mockResolvedValue(member);
|
||||
|
||||
config.set('userManagement.authenticationMethod', 'ldap');
|
||||
|
||||
@@ -79,7 +81,8 @@ describe('AuthController', () => {
|
||||
body.emailOrLdapLoginId,
|
||||
body.password,
|
||||
);
|
||||
expect(mockedAuth.handleLdapLogin).toHaveBeenCalledWith(
|
||||
|
||||
expect(ldapService.handleLdapLogin).toHaveBeenCalledWith(
|
||||
body.emailOrLdapLoginId,
|
||||
body.password,
|
||||
);
|
||||
|
||||
@@ -3,10 +3,11 @@ import { Logger } from '@n8n/backend-common';
|
||||
import type { User, PublicUser } from '@n8n/db';
|
||||
import { UserRepository } from '@n8n/db';
|
||||
import { Body, Get, Post, Query, RestController } from '@n8n/decorators';
|
||||
import { Container } from '@n8n/di';
|
||||
import { isEmail } from 'class-validator';
|
||||
import { Response } from 'express';
|
||||
|
||||
import { handleEmailLogin, handleLdapLogin } from '@/auth';
|
||||
import { handleEmailLogin } from '@/auth';
|
||||
import { AuthService } from '@/auth/auth.service';
|
||||
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
|
||||
import { AuthError } from '@/errors/response-errors/auth.error';
|
||||
@@ -73,7 +74,8 @@ export class AuthController {
|
||||
user = preliminaryUser;
|
||||
usedAuthenticationMethod = 'email';
|
||||
} else {
|
||||
user = await handleLdapLogin(emailOrLdapLoginId, password);
|
||||
const { LdapService } = await import('@/ldap.ee/ldap.service.ee');
|
||||
user = await Container.get(LdapService).handleLdapLogin(emailOrLdapLoginId, password);
|
||||
}
|
||||
} else {
|
||||
user = await handleEmailLogin(emailOrLdapLoginId, password);
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { LdapConfig } from '@n8n/constants';
|
||||
import { LDAP_FEATURE_NAME } from '@n8n/constants';
|
||||
import { SettingsRepository } from '@n8n/db';
|
||||
import type { User, RunningMode, SyncStatus } from '@n8n/db';
|
||||
import { Service } from '@n8n/di';
|
||||
import { Service, Container } from '@n8n/di';
|
||||
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
|
||||
import { QueryFailedError } from '@n8n/typeorm';
|
||||
import type { Entry as LdapUser, ClientOptions } from 'ldapts';
|
||||
@@ -17,14 +17,13 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import {
|
||||
getCurrentAuthenticationMethod,
|
||||
isEmailCurrentAuthenticationMethod,
|
||||
isLdapCurrentAuthenticationMethod,
|
||||
setCurrentAuthenticationMethod,
|
||||
} from '@/sso.ee/sso-helpers';
|
||||
|
||||
import { BINARY_AD_ATTRIBUTES, LDAP_LOGIN_ENABLED, LDAP_LOGIN_LABEL } from './constants';
|
||||
import {
|
||||
createLdapUserOnLocalDb,
|
||||
getUserByEmail,
|
||||
getAuthIdentityByLdapId,
|
||||
isLdapEnabled,
|
||||
mapLdapAttributesToUser,
|
||||
createLdapAuthIdentity,
|
||||
updateLdapUserOnLocalDb,
|
||||
createFilter,
|
||||
deleteAllLdapIdentities,
|
||||
escapeFilter,
|
||||
@@ -38,7 +37,15 @@ import {
|
||||
resolveEntryBinaryAttributes,
|
||||
saveLdapSynchronization,
|
||||
validateLdapConfigurationSchema,
|
||||
} from './helpers.ee';
|
||||
} from '@/ldap.ee/helpers.ee';
|
||||
import {
|
||||
getCurrentAuthenticationMethod,
|
||||
isEmailCurrentAuthenticationMethod,
|
||||
isLdapCurrentAuthenticationMethod,
|
||||
setCurrentAuthenticationMethod,
|
||||
} from '@/sso.ee/sso-helpers';
|
||||
|
||||
import { BINARY_AD_ATTRIBUTES, LDAP_LOGIN_ENABLED, LDAP_LOGIN_LABEL } from './constants';
|
||||
|
||||
@Service()
|
||||
export class LdapService {
|
||||
@@ -432,4 +439,54 @@ export class LdapService {
|
||||
const remoteAdUserIds = remoteAdUsers.map((adUser) => adUser[this.config.ldapIdAttribute]);
|
||||
return localLdapIds.filter((user) => !remoteAdUserIds.includes(user));
|
||||
}
|
||||
|
||||
async handleLdapLogin(loginId: string, password: string): Promise<User | undefined> {
|
||||
if (!isLdapEnabled()) return undefined;
|
||||
|
||||
if (!this.config.loginEnabled) return undefined;
|
||||
|
||||
const { loginIdAttribute, userFilter } = this.config;
|
||||
|
||||
const ldapUser = await this.findAndAuthenticateLdapUser(
|
||||
loginId,
|
||||
password,
|
||||
loginIdAttribute,
|
||||
userFilter,
|
||||
);
|
||||
|
||||
if (!ldapUser) return undefined;
|
||||
|
||||
const [ldapId, ldapAttributesValues] = mapLdapAttributesToUser(ldapUser, this.config);
|
||||
|
||||
const { email: emailAttributeValue } = ldapAttributesValues;
|
||||
|
||||
if (!ldapId || !emailAttributeValue) return undefined;
|
||||
|
||||
const ldapAuthIdentity = await getAuthIdentityByLdapId(ldapId);
|
||||
if (!ldapAuthIdentity) {
|
||||
const emailUser = await getUserByEmail(emailAttributeValue);
|
||||
|
||||
// check if there is an email user with the same email as the authenticated LDAP user trying to log-in
|
||||
if (emailUser && emailUser.email === emailAttributeValue) {
|
||||
const identity = await createLdapAuthIdentity(emailUser, ldapId);
|
||||
await updateLdapUserOnLocalDb(identity, ldapAttributesValues);
|
||||
} else {
|
||||
const user = await createLdapUserOnLocalDb(ldapAttributesValues, ldapId);
|
||||
Container.get(EventService).emit('user-signed-up', {
|
||||
user,
|
||||
userType: 'ldap',
|
||||
wasDisabledLdapUser: false,
|
||||
});
|
||||
return user;
|
||||
}
|
||||
} else {
|
||||
if (ldapAuthIdentity.user) {
|
||||
if (ldapAuthIdentity.user.disabled) return undefined;
|
||||
await updateLdapUserOnLocalDb(ldapAuthIdentity, ldapAttributesValues);
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the user again as user's data might have been updated
|
||||
return (await getAuthIdentityByLdapId(ldapId))?.user;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user