You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 7-8.RSVP
Socket
Socket
Sign inDemoInstall

@mashroom/mashroom-security-provider-ldap

Package Overview
Dependencies
Maintainers
0
Versions
92
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.6.1 to 2.7.0

308

dist/LdapClientImpl.js

@@ -7,9 +7,33 @@ "use strict";

exports.default = void 0;
var _ldapjs = require("ldapjs");
var _ldapts = require("ldapts");
const DEFAULT_ATTRIBUTES = ['dn', 'cn', 'sn', 'givenName', 'displayName', 'uid', 'mail'];
// See https://ldap.com/ldap-result-code-reference/
const ERROR_NO_SUCH_OBJECT = 32;
const getAttributeValue = (name, attributes) => {
return attributes.find(({
type
}) => type === name)?.values?.[0];
if (!(name in attributes)) {
return undefined;
}
let value = attributes[name];
if (Array.isArray(value)) {
value = value[0];
}
if (typeof value === 'string') {
return value;
}
return value?.toString();
};
const getAttributeValues = (name, attributes) => {
if (!(name in attributes)) {
return undefined;
}
const value = attributes[name];
const values = Array.isArray(value) ? value : [value];
return values.map(value => {
if (typeof value === 'string') {
return value;
}
return value?.toString();
});
};
class LdapClientImpl {

@@ -38,3 +62,3 @@ constructor(_serverUrl, _connectTimeout, _timeout, _baseDN, _bindDN, _bindCredentials, _tlsOptions, loggerFactory) {

// For some reason in LdapJS 3 the attribute names need now to be lower case
// For some reason in the attribute names need now to be lower case
attributes = attributes.map(a => a.toLowerCase());

@@ -46,63 +70,51 @@ const searchOpts = {

};
return new Promise((resolve, reject) => {
const entries = [];
searchClient.search(this._baseDN, searchOpts, (err, res) => {
if (err) {
reject(err);
return;
let result;
try {
result = await searchClient.search(this._baseDN, searchOpts);
} catch (e) {
if (e.code === ERROR_NO_SUCH_OBJECT) {
return [];
}
throw new Error(`LDAP user search failed: ${e.message}`);
}
const entries = [];
result.searchEntries.forEach(({
dn,
...attributes
}) => {
const cns = getAttributeValues('cn', attributes);
const sn = getAttributeValue('sn', attributes);
const givenName = getAttributeValue('givenName', attributes);
const displayName = getAttributeValue('displayName', attributes);
const uid = getAttributeValue('uid', attributes);
const mail = getAttributeValue('mail', attributes);
let cn;
if (cns) {
// Take the last one, which is in OpenLDAP the actual group cn
cn = [...cns].pop();
} else if (dn) {
// Fallback, cn should always be present
cn = dn.split(',')[0].split('=').pop();
}
if (dn && cn && mail) {
const ldapEntry = {
dn,
cn,
sn,
uid,
mail,
givenName,
displayName
};
if (extraAttributes) {
extraAttributes.forEach(extraAttr => {
ldapEntry[extraAttr] = getAttributeValue(extraAttr, attributes);
});
}
res.on('searchEntry', ({
objectName,
attributes
}) => {
const dn = objectName?.toString();
const cns = attributes.find(({
type
}) => type === 'cn')?.values;
const sn = getAttributeValue('sn', attributes);
const givenName = getAttributeValue('givenName', attributes);
const displayName = getAttributeValue('displayName', attributes);
const uid = getAttributeValue('uid', attributes);
const mail = getAttributeValue('mail', attributes);
let cn;
if (cns) {
// Take the last one, which is in OpenLDAP the actual group cn
cn = [...cns].pop();
} else if (dn) {
// Fallback, cn should always be present
cn = dn.split(',')[0].split('=').pop();
}
if (dn && cn && mail) {
const ldapEntry = {
dn,
cn,
sn,
uid,
mail,
givenName,
displayName
};
if (extraAttributes) {
extraAttributes.forEach(extraAttr => {
ldapEntry[extraAttr] = getAttributeValue(extraAttr, attributes);
});
}
entries.push(ldapEntry);
} else {
this._logger.warn('Incomplete LDAP entry, dn, cn, and mail is required. Present attributes: ', attributes.map(a => `${a.type}:${a.values}`));
}
});
res.on('error', error => {
this._logger.error('LDAP search error', error);
reject(error);
});
res.on('end', result => {
if (result?.status === 0) {
resolve(entries);
} else {
reject(new Error(`Search failed: ${result?.errorMessage}`));
}
});
});
entries.push(ldapEntry);
} else {
this._logger.warn('Incomplete LDAP entry, dn, cn, and mail is required. Present attributes: ', attributes);
}
});
return entries;
}

@@ -115,48 +127,36 @@ async searchGroups(filter) {

};
return new Promise((resolve, reject) => {
const entries = [];
searchClient.search(this._baseDN, searchOpts, (err, res) => {
if (err) {
reject(err);
return;
}
res.on('searchEntry', ({
objectName,
attributes
}) => {
const dn = objectName?.toString();
const cns = attributes.find(({
type
}) => type === 'cn')?.values;
let cn;
if (cns) {
// Take the last one, which is in OpenLDAP the actual group cn
cn = [...cns].pop();
} else if (dn) {
// Fallback, cn should always be present
cn = dn.split(',')[0].split('=').pop();
}
if (dn && cn) {
const ldapEntry = {
dn,
cn
};
entries.push(ldapEntry);
} else {
this._logger.warn('Incomplete LDAP entry, dn and cs is required. Present attributes: ', attributes.map(a => `${a.type}:${a.values}`));
}
});
res.on('error', error => {
this._logger.error('LDAP search error', error);
reject(error);
});
res.on('end', result => {
if (result?.status === 0) {
resolve(entries);
} else {
reject(new Error(`Search failed: ${result?.errorMessage}`));
}
});
});
let result;
try {
result = await searchClient.search(this._baseDN, searchOpts);
} catch (e) {
if (e.code === ERROR_NO_SUCH_OBJECT) {
return [];
}
throw new Error(`LDAP group search failed: ${e.message}`);
}
const entries = [];
result.searchEntries.forEach(({
dn,
...attributes
}) => {
const cns = getAttributeValues('cn', attributes);
let cn;
if (cns) {
// Take the last one, which is in OpenLDAP the actual group cn
cn = [...cns].pop();
} else if (dn) {
// Fallback, cn should always be present
cn = dn.split(',')[0].split('=').pop();
}
if (dn && cn) {
const ldapEntry = {
dn,
cn
};
entries.push(ldapEntry);
} else {
this._logger.warn('Incomplete LDAP entry, dn and cs is required. Present attributes: ', attributes);
}
});
return entries;
}

@@ -166,10 +166,14 @@ async login(ldapEntry, password) {

try {
client = await this._createLdapJsClient();
await this._bind(ldapEntry.dn, password, client);
this._disconnect(client);
client = await this._createLdapTsClient();
await client.bind(ldapEntry.dn, password);
await client.unbind();
} catch (error) {
this._logger.warn(`Binding with user ${ldapEntry.dn} failed`, error);
if (client) {
this._disconnect(client);
try {
await client.unbind();
} catch {
// Ignore
}
}
this._logger.warn(`Binding with user ${ldapEntry.dn} failed`, error);
throw error;

@@ -180,3 +184,7 @@ }

if (this._searchClient) {
this._disconnect(this._searchClient);
try {
this._searchClient.unbind();
} catch {
// Ignore
}
this._searchClient = null;

@@ -186,13 +194,12 @@ }

async getSearchClient() {
if (this._searchClient) {
if (this._searchClient && this._searchClient.isConnected) {
return this._searchClient;
}
try {
const searchClient = await this._createLdapJsClient(true);
const searchClient = await this._createLdapTsClient();
const bind = async () => {
try {
await this._bind(this._bindDN, this._bindCredentials, searchClient);
await searchClient.bind(this._bindDN, this._bindCredentials);
} catch (error) {
this._logger.error(`Binding with user ${this._bindDN} failed`, error);
this._disconnect(searchClient);
this._searchClient = null;

@@ -202,6 +209,2 @@ }

await bind();
searchClient.on('connect', async () => {
// Re-bind on reconnect
await bind();
});
this._searchClient = searchClient;

@@ -214,3 +217,3 @@ return this._searchClient;

}
async _createLdapJsClient(keepForever = false) {
async _createLdapTsClient() {
const clientOptions = {

@@ -220,60 +223,7 @@ url: `${this._serverUrl}/${this._baseDN}`,

connectTimeout: this._connectTimeout,
timeout: this._timeout,
reconnect: {
initialDelay: 100,
maxDelay: 10000,
failAfter: keepForever ? Infinity : 3
}
timeout: this._timeout
};
return new Promise((resolve, reject) => {
let resolved = false;
let client;
try {
client = (0, _ldapjs.createClient)(clientOptions);
client.on('connect', () => {
this._logger.debug(`Connected to LDAP server: ${this._serverUrl}`);
if (!resolved) {
resolve(client);
resolved = true;
}
});
client.on('connectError', error => {
this._logger.error('LDAP connection error', error);
if (!resolved) {
reject(error);
resolved = true;
}
});
client.on('error', error => {
this._logger.warn('LDAP connection error, reconnecting...', error);
if (!resolved) {
reject(error);
resolved = true;
}
});
client.on('destroy', () => {
this._logger.debug(`Disconnected from LDAP server: ${this._serverUrl}`);
});
} catch (e) {
reject(e);
}
});
return new _ldapts.Client(clientOptions);
}
async _bind(user, password, ldapjsClient) {
return new Promise((resolve, reject) => {
ldapjsClient.bind(user, password, error => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
_disconnect(ldapjsClient) {
if (ldapjsClient.connected) {
ldapjsClient.destroy();
}
}
}
exports.default = LdapClientImpl;

@@ -10,3 +10,3 @@ "use strict";

var _LdapClientImpl = _interopRequireDefault(require("./LdapClientImpl"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const bootstrap = async (pluginName, pluginConfig, pluginContextHolder) => {

@@ -13,0 +13,0 @@ const {

@@ -11,3 +11,3 @@ "use strict";

var _loginFailureReason = _interopRequireDefault(require("./login-failure-reason"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const LDAP_AUTH_USER_SESSION_KEY = '__MASHROOM_SECURITY_LDAP_AUTH_USER';

@@ -24,3 +24,2 @@ const LDAP_AUTH_EXPIRES_SESSION_KEY = '__MASHROOM_SECURITY_LDAP_AUTH_EXPIRES';

this._ldapClient = _ldapClient;
this._serverRootFolder = _serverRootFolder;
this._authenticationTimeoutSec = _authenticationTimeoutSec;

@@ -27,0 +26,0 @@ const logger = loggerFactory('mashroom.security.provider.ldap');

@@ -7,3 +7,3 @@ {

"license": "MIT",
"version": "2.6.1",
"version": "2.7.0",
"files": [

@@ -14,10 +14,9 @@ "dist/**"

"express": "^4.19.2",
"ldapjs": "^3.0.7"
"ldapts": "^7.0.12"
},
"devDependencies": {
"@mashroom/mashroom": "2.6.1",
"@mashroom/mashroom-security": "2.6.1",
"@mashroom/mashroom-utils": "2.6.1",
"@types/express": "^4.17.21",
"@types/ldapjs": "^3.0.6"
"@mashroom/mashroom": "2.7.0",
"@mashroom/mashroom-security": "2.7.0",
"@mashroom/mashroom-utils": "2.7.0",
"@types/express": "^4.17.21"
},

@@ -24,0 +23,0 @@ "jest": {

@@ -6,3 +6,3 @@

This plugin adds a LDAP security provider.
This plugin adds an LDAP security provider.

@@ -9,0 +9,0 @@ ## Usage

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc