From 28ff17a07807d1660ee4f064d2dd062bda1bb348 Mon Sep 17 00:00:00 2001 From: Ryan Crisanti Date: Thu, 19 Dec 2024 08:44:37 -0500 Subject: [PATCH] Server: Allow self-signed certificate for ldap auth (#11531) Co-authored-by: Laurent Cozic --- packages/server/src/config.ts | 2 ++ packages/server/src/env.ts | 4 ++++ packages/server/src/routes/api/sessions.test.ts | 5 +++++ packages/server/src/utils/ldapLogin.ts | 11 +++++++++++ packages/server/src/utils/types.ts | 1 + packages/tools/cspell/dictionary4.txt | 3 ++- 6 files changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts index 051fd95e0..40d8f5096 100644 --- a/packages/server/src/config.ts +++ b/packages/server/src/config.ts @@ -125,6 +125,7 @@ function ldapConfigFromEnv(env: EnvVariables): LdapConfig[] { baseDN: env.LDAP_1_BASE_DN, bindDN: env.LDAP_1_BIND_DN, bindPW: env.LDAP_1_BIND_PW, + tlsCaFile: env.LDAP_1_TLS_CA_FILE, }); } @@ -138,6 +139,7 @@ function ldapConfigFromEnv(env: EnvVariables): LdapConfig[] { baseDN: env.LDAP_2_BASE_DN, bindDN: env.LDAP_2_BIND_DN, bindPW: env.LDAP_2_BIND_PW, + tlsCaFile: env.LDAP_2_TLS_CA_FILE, }); } return ldapConfig; diff --git a/packages/server/src/env.ts b/packages/server/src/env.ts index e59534851..188a1c609 100644 --- a/packages/server/src/env.ts +++ b/packages/server/src/env.ts @@ -137,6 +137,7 @@ const defaultEnvValues: EnvVariables = { LDAP_1_BASE_DN: '', LDAP_1_BIND_DN: '', // used for user search - leave empty if ldap server allows anonymous bind LDAP_1_BIND_PW: '', // used for user search - leave empty if ldap server allows anonymous bind + LDAP_1_TLS_CA_FILE: '', // used for self-signed certificate with ldaps - leave empty if using ldap or server uses CA-issued certificate LDAP_2_ENABLED: false, LDAP_2_USER_AUTO_CREATION: true, // if set to true, users will be created on the fly after ldap authentication @@ -146,6 +147,7 @@ const defaultEnvValues: EnvVariables = { LDAP_2_BASE_DN: '', LDAP_2_BIND_DN: '', // used for user search - leave empty if ldap server allows anonymous bind LDAP_2_BIND_PW: '', // used for user search - leave empty if ldap server allows anonymous bind + LDAP_2_TLS_CA_FILE: '', // used for self-signed certificate with ldaps - leave empty if using ldap or server uses CA-issued certificate }; @@ -228,6 +230,7 @@ export interface EnvVariables { LDAP_1_BASE_DN: string; LDAP_1_BIND_DN: string; LDAP_1_BIND_PW: string; + LDAP_1_TLS_CA_FILE: string; LDAP_2_ENABLED: boolean; LDAP_2_USER_AUTO_CREATION: boolean; @@ -237,6 +240,7 @@ export interface EnvVariables { LDAP_2_BASE_DN: string; LDAP_2_BIND_DN: string; LDAP_2_BIND_PW: string; + LDAP_2_TLS_CA_FILE: string; } const parseBoolean = (s: string): boolean => { diff --git a/packages/server/src/routes/api/sessions.test.ts b/packages/server/src/routes/api/sessions.test.ts index 1958616cf..a6b495248 100644 --- a/packages/server/src/routes/api/sessions.test.ts +++ b/packages/server/src/routes/api/sessions.test.ts @@ -86,6 +86,7 @@ describe('api/sessions', () => { baseDN: '', bindDN: '', bindPW: '', + tlsCaFile: '', }; { @@ -123,6 +124,7 @@ describe('api/sessions', () => { baseDN: '', bindDN: '', bindPW: '', + tlsCaFile: '', }; const context = await postSession(user.email, password); @@ -151,6 +153,7 @@ describe('api/sessions', () => { baseDN: '', bindDN: '', bindPW: '', + tlsCaFile: '', }; (ldapLogin as jest.Mock).mockResolvedValue(user); @@ -179,6 +182,7 @@ describe('api/sessions', () => { baseDN: '', bindDN: '', bindPW: '', + tlsCaFile: '', }; (ldapLogin as jest.Mock).mockImplementationOnce(() => { @@ -203,6 +207,7 @@ describe('api/sessions', () => { baseDN: '', bindDN: '', bindPW: '', + tlsCaFile: '', }; (ldapLogin as jest.Mock).mockImplementationOnce(() => { diff --git a/packages/server/src/utils/ldapLogin.ts b/packages/server/src/utils/ldapLogin.ts index f4b8ba813..d40dda6ea 100644 --- a/packages/server/src/utils/ldapLogin.ts +++ b/packages/server/src/utils/ldapLogin.ts @@ -3,6 +3,7 @@ import { User } from '../services/database/types'; import Logger from '@joplin/utils/Logger'; import { LdapConfig } from './types'; import { ErrorForbidden } from './errors'; +import { readFile } from 'fs/promises'; const logger = Logger.create('LDAP'); @@ -16,6 +17,7 @@ export default async function ldapLogin(email: string, password: string, user: U const baseDN = config.baseDN; const bindDN = config.bindDN; const bindPW = config.bindPW; + const tlsCaFile = config.tlsCaFile; logger.info(`Starting authentication with Server ${host}`); @@ -25,10 +27,19 @@ export default async function ldapLogin(email: string, password: string, user: U if (enabled) { let searchResults; + + let tlsOptions; + if (tlsCaFile.length !== 0) { + tlsOptions = { + ca: [await readFile(tlsCaFile)], + }; + } + const client = new Client({ url: host, timeout: 5000, connectTimeout: 1000, + tlsOptions: tlsOptions, }); if (bindDN.length !== 0) { diff --git a/packages/server/src/utils/types.ts b/packages/server/src/utils/types.ts index bd75ec13d..fdea8865c 100644 --- a/packages/server/src/utils/types.ts +++ b/packages/server/src/utils/types.ts @@ -141,6 +141,7 @@ export interface LdapConfig { baseDN: string; bindDN: string; bindPW: string; + tlsCaFile: string; } export interface Config extends EnvVariables { diff --git a/packages/tools/cspell/dictionary4.txt b/packages/tools/cspell/dictionary4.txt index 9ea008765..1874a48e2 100644 --- a/packages/tools/cspell/dictionary4.txt +++ b/packages/tools/cspell/dictionary4.txt @@ -153,4 +153,5 @@ tablist Favorite tablist Edubirdie -Useviral \ No newline at end of file +Useviral +ldaps