From 504c845812fceb606d6b2901e03865977e6f43a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20B=C3=A9guin?= Date: Thu, 10 Nov 2022 13:23:23 +0100 Subject: [PATCH] version: 6.0.0 --- index.d.ts | 4 ++ index.js | 7 ++++ lib/configuration.d.ts | 6 +++ lib/configuration.js | 2 + lib/ldapTypes.d.ts | 35 ++++++++++++++++ lib/ldapTypes.js | 2 + lib/logger.d.ts | 3 ++ lib/logger.js | 9 +++++ lib/server.d.ts | 16 ++++++++ lib/server.js | 66 ++++++++++++++++++++++++++++++ lib/user.d.ts | 6 +++ lib/user.js | 2 + server.d.ts | 2 + server.js | 92 ++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 252 insertions(+) create mode 100644 index.d.ts create mode 100644 index.js create mode 100644 lib/configuration.d.ts create mode 100644 lib/configuration.js create mode 100644 lib/ldapTypes.d.ts create mode 100644 lib/ldapTypes.js create mode 100644 lib/logger.d.ts create mode 100644 lib/logger.js create mode 100644 lib/server.d.ts create mode 100644 lib/server.js create mode 100644 lib/user.d.ts create mode 100644 lib/user.js create mode 100644 server.d.ts create mode 100755 server.js diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..cd53a0e --- /dev/null +++ b/index.d.ts @@ -0,0 +1,4 @@ +export { LdapServerMockConfiguration } from './lib/configuration'; +export { LdapServerMockLogger } from './lib/logger'; +export { LdapServerMock } from './lib/server'; +export { LdapUser } from './lib/user'; diff --git a/index.js b/index.js new file mode 100644 index 0000000..198bae1 --- /dev/null +++ b/index.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LdapServerMock = exports.LdapServerMockLogger = void 0; +var logger_1 = require("./lib/logger"); +Object.defineProperty(exports, "LdapServerMockLogger", { enumerable: true, get: function () { return logger_1.LdapServerMockLogger; } }); +var server_1 = require("./lib/server"); +Object.defineProperty(exports, "LdapServerMock", { enumerable: true, get: function () { return server_1.LdapServerMock; } }); diff --git a/lib/configuration.d.ts b/lib/configuration.d.ts new file mode 100644 index 0000000..75eb009 --- /dev/null +++ b/lib/configuration.d.ts @@ -0,0 +1,6 @@ +export interface LdapServerMockConfiguration { + readonly certPath?: string; + readonly certKeyPath?: string; + readonly port?: number; + readonly searchBase?: string; +} diff --git a/lib/configuration.js b/lib/configuration.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/lib/configuration.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/ldapTypes.d.ts b/lib/ldapTypes.d.ts new file mode 100644 index 0000000..6c71846 --- /dev/null +++ b/lib/ldapTypes.d.ts @@ -0,0 +1,35 @@ +/// +import { Socket } from 'node:net'; +import { DN, Filter } from 'ldapjs'; +export interface LdapState { + readonly bindDN: DN; +} +export interface LdapRequest { + readonly connection: Socket & LdapState; + readonly dn: DN; + readonly logId: string; +} +export interface LdapResponse { + readonly end: (code?: number) => void; +} +export interface LdapBindRequest extends LdapRequest { + readonly authentication: string; + readonly credentials: string; + readonly name: DN; + readonly version: string; +} +export interface LdapSearchRequest extends LdapRequest { + readonly attributes: string[]; + readonly baseObject: DN; + readonly filter: Filter; + readonly scope: 'base' | 'one' | 'sub'; + readonly sizeLimit: number; + readonly timeLimit: number; + readonly typesOnly: boolean; +} +export interface LdapBindResponse extends LdapResponse { +} +export interface LdapSearchResponse extends LdapResponse { + readonly send: (entry: any) => void; +} +export declare type LdapNext = (error?: Error) => void; diff --git a/lib/ldapTypes.js b/lib/ldapTypes.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/lib/ldapTypes.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/logger.d.ts b/lib/logger.d.ts new file mode 100644 index 0000000..8403ebc --- /dev/null +++ b/lib/logger.d.ts @@ -0,0 +1,3 @@ +export declare class LdapServerMockLogger { + info(...args: any[]): void; +} diff --git a/lib/logger.js b/lib/logger.js new file mode 100644 index 0000000..64126d8 --- /dev/null +++ b/lib/logger.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LdapServerMockLogger = void 0; +class LdapServerMockLogger { + info(...args) { + console.info(...args); + } +} +exports.LdapServerMockLogger = LdapServerMockLogger; diff --git a/lib/server.d.ts b/lib/server.d.ts new file mode 100644 index 0000000..d4d96ef --- /dev/null +++ b/lib/server.d.ts @@ -0,0 +1,16 @@ +/// +import { LdapServerMockConfiguration } from './configuration'; +import { LdapServerMockLogger } from './logger'; +import { LdapUser } from './user'; +export declare class LdapServerMock { + private _users; + private _connectedSockets; + private _port; + private _ldapServer; + private _logger; + private _searchBase; + private _server?; + constructor(_users: LdapUser[], serverConfiguration: LdapServerMockConfiguration, certificatePublicKey?: Buffer, certificatePrivateKey?: Buffer, logger?: LdapServerMockLogger); + start(): Promise; + stop(): Promise; +} diff --git a/lib/server.js b/lib/server.js new file mode 100644 index 0000000..c3b2d81 --- /dev/null +++ b/lib/server.js @@ -0,0 +1,66 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LdapServerMock = void 0; +const ldapjs_1 = require("ldapjs"); +const logger_1 = require("./logger"); +class LdapServerMock { + constructor(_users, serverConfiguration, certificatePublicKey, certificatePrivateKey, logger) { + this._users = _users; + this._connectedSockets = new Set(); + this._port = serverConfiguration.port ?? 3004; + this._searchBase = serverConfiguration.searchBase ?? 'dc=test'; + this._ldapServer = (0, ldapjs_1.createServer)({ + ...(certificatePublicKey && { certificate: certificatePublicKey }), + ...(certificatePrivateKey && { key: certificatePrivateKey }) + }); + this._logger = logger ?? new logger_1.LdapServerMockLogger(); + } + async start() { + return new Promise((resolve, reject) => { + this._ldapServer.bind(this._searchBase, (request, response, next) => { + response.end(); + }); + this._ldapServer.search(this._searchBase, (request, response, next) => { + for (const user of this._users) { + if (request.filter.matches(user.attributes)) { + response.send(user); + } + } + response.end(); + }); + this._logger.info('starting'); + this._server = this._ldapServer.listen(this._port, () => { + const info = this._server?.address(); + this._logger.info('started on port %i', info.port); + if (process.connected && process.send) { + process.send({ status: 'started' }); + } + resolve(); + }); + this._server.on('connection', (socket) => { + this._connectedSockets.add(socket); + socket.on('close', () => { + this._connectedSockets.delete(socket); + }); + }); + }); + } + async stop() { + return new Promise((resolve, reject) => { + this._logger.info('stopping'); + if (this._server) { + for (const socket of this._connectedSockets.values()) { + socket.destroy(); + } + this._server.close(() => { + this._logger.info('stopped'); + resolve(); + }); + } + else { + resolve(); + } + }); + } +} +exports.LdapServerMock = LdapServerMock; diff --git a/lib/user.d.ts b/lib/user.d.ts new file mode 100644 index 0000000..6c7395d --- /dev/null +++ b/lib/user.d.ts @@ -0,0 +1,6 @@ +export interface LdapUser { + readonly dn: string; + readonly attributes: { + [key: string]: boolean | string | number | string[] | number[] | boolean[]; + }; +} diff --git a/lib/user.js b/lib/user.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/lib/user.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/server.d.ts b/server.d.ts new file mode 100644 index 0000000..b798801 --- /dev/null +++ b/server.d.ts @@ -0,0 +1,2 @@ +#!/usr/bin/env node +export {}; diff --git a/server.js b/server.js new file mode 100755 index 0000000..2b097c5 --- /dev/null +++ b/server.js @@ -0,0 +1,92 @@ +#!/usr/bin/env node +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const tslib_1 = require("tslib"); +const fs = tslib_1.__importStar(require("node:fs/promises")); +const node_path_1 = tslib_1.__importDefault(require("node:path")); +const server_1 = require("./lib/server"); +function exit(error, code = 1) { + if (error) { + console.log('error: %s', error); + } + printHelp(); + process.exitCode = 1; +} +function getArguments() { + let confFilePath = ''; + let databaseFilePath = ''; + for (let i = 2; i < process.argv.length; i++) { + const argChunks = process.argv[i].match(/--(conf|database)=(.*)/); + if (!argChunks) { + break; + } + switch (argChunks[1]) { + case 'conf': + confFilePath = node_path_1.default.resolve(argChunks[2]); + break; + case 'database': + databaseFilePath = node_path_1.default.resolve(argChunks[2]); + break; + default: + throw new Error(`unexpected option ${argChunks[1]}`); + break; + } + } + if (!confFilePath) { + throw new Error('missing --conf option'); + } + if (!databaseFilePath) { + throw new Error('missing --database option'); + } + return { confFilePath, databaseFilePath }; +} +async function main() { + let args; + try { + args = getArguments(); + } + catch (error) { + exit(error.message); + return; + } + let configurationFilePath = args.confFilePath; + let databaseFilePath = args.databaseFilePath; + process.on('SIGTERM', async () => { + if (server) { + await server.stop(); + } + }); + let serverConfiguration; + let users = []; + try { + serverConfiguration = require(configurationFilePath); + users = require(databaseFilePath); + } + catch (error) { + exit(error.message); + return; + } + let certificatePublicKey; + let certificatePrivateKey; + if (serverConfiguration.certPath && serverConfiguration.certKeyPath) { + try { + certificatePublicKey = await fs.readFile(node_path_1.default.resolve(serverConfiguration.certPath)); + certificatePrivateKey = await fs.readFile(node_path_1.default.resolve(serverConfiguration.certKeyPath)); + } + catch (error) { + exit(error.message); + return; + } + } + const server = new server_1.LdapServerMock(users, serverConfiguration, certificatePublicKey, certificatePrivateKey); + await server.start(); +} +function printHelp() { + console.log(` +usage: npx ldap-server-mock options +options: + --conf="file" relative or absolute path to the server configuration file + --database="file" relative or absolute path to the file containing LDAP users in JSON format + `); +} +main();