Socket
Socket
Sign inDemoInstall

@mashroom/mashroom-security-provider-ldap

Package Overview
Dependencies
Maintainers
1
Versions
92
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mashroom/mashroom-security-provider-ldap - npm Package Compare versions

Comparing version 2.1.3 to 2.2.0

33

dist/LdapClientImpl.js

@@ -7,7 +7,4 @@ "use strict";

exports.default = void 0;
var _ldapjs = require("ldapjs");
const DEFAULT_ATTRIBUTES = ['dn', 'cn', 'sn', 'givenName', 'displayName', 'uid', 'mail'];
class LdapClientImpl {

@@ -25,7 +22,5 @@ constructor(_serverUrl, _connectTimeout, _timeout, _baseDN, _bindDN, _bindCredentials, _tlsOptions, loggerFactory) {

}
async search(filter, extraAttributes) {
const searchClient = await this.getSearchClient();
const attributes = DEFAULT_ATTRIBUTES;
if (extraAttributes) {

@@ -38,3 +33,2 @@ extraAttributes.forEach(extraAttribute => {

}
const searchOpts = {

@@ -52,3 +46,2 @@ filter,

}
res.on('searchEntry', ({

@@ -58,3 +51,2 @@ object: entry

let cn;
if (Array.isArray(entry.cn)) {

@@ -69,3 +61,2 @@ // Take the last one, which is in OpenLDAP the actual group cn

}
const ldapEntry = {

@@ -80,3 +71,2 @@ dn: entry.dn,

};
if (extraAttributes) {

@@ -87,3 +77,2 @@ extraAttributes.forEach(extraAttr => {

}
entries.push(ldapEntry);

@@ -93,3 +82,2 @@ });

this._logger.error('LDAP search error', error);
reject(error);

@@ -107,6 +95,4 @@ });

}
async login(ldapEntry, password) {
let client;
try {

@@ -118,19 +104,14 @@ client = await this._createLdapJsClient();

this._logger.warn(`Binding with user ${ldapEntry.dn} failed`, error);
if (client) {
await this._disconnect(client);
}
throw error;
}
}
shutdown() {
if (this._searchClient) {
this._disconnect(this._searchClient);
this._searchClient = null;
}
}
async getSearchClient() {

@@ -140,6 +121,4 @@ if (this._searchClient) {

}
try {
const searchClient = await this._createLdapJsClient(true);
const bind = async () => {

@@ -150,3 +129,2 @@ try {

this._logger.error(`Binding with user ${this._bindDN} failed`, error);
await this._disconnect(searchClient);

@@ -156,3 +134,2 @@ this._searchClient = null;

};
await bind();

@@ -167,7 +144,5 @@ searchClient.on('connect', async () => {

this._logger.error('Creating search LDAP client failed!', error);
throw error;
}
}
async _createLdapJsClient(keepForever = false) {

@@ -188,3 +163,2 @@ const clientOptions = {

let client;
try {

@@ -194,3 +168,2 @@ client = (0, _ldapjs.createClient)(clientOptions);

this._logger.debug(`Connected to LDAP server: ${this._serverUrl}`);
if (!resolved) {

@@ -203,3 +176,2 @@ resolve(client);

this._logger.error('LDAP connection error', error);
if (!resolved) {

@@ -212,3 +184,2 @@ reject(error);

this._logger.warn('LDAP connection error, reconnecting...', error);
if (!resolved) {

@@ -227,3 +198,2 @@ reject(error);

}
async _bind(user, password, ldapjsClient) {

@@ -240,3 +210,2 @@ return new Promise((resolve, reject) => {

}
_disconnect(ldapjsClient) {

@@ -247,5 +216,3 @@ if (ldapjsClient.connected) {

}
}
exports.default = LdapClientImpl;

23

dist/login_failure_reason.js

@@ -8,2 +8,3 @@ "use strict";

// Active Directory errors
const AD_LOGIN_ERRORS = {

@@ -22,41 +23,32 @@ USER_NOT_FOUND: '525',

const AD_ERROR_MESSAGE_REGEX = /.*DSID-.*, data ([0-9a-f]{3}),.*/;
var _default = errorMessage => {
if (!errorMessage) {
return;
} // OpenLDAP
}
// OpenLDAP
// https://www.openldap.org/doc/admin24/appendix-common-errors.html
if (errorMessage === 'Invalid Credentials') {
return 'Invalid credentials';
} // Active Directory
}
// Active Directory
// https://ldapwiki.com/wiki/Common%20Active%20Directory%20Bind%20Errors
const adMatch = errorMessage.match(AD_ERROR_MESSAGE_REGEX);
if (adMatch) {
const adErrorCode = adMatch[1];
switch (adErrorCode) {
case AD_LOGIN_ERRORS.USER_NOT_FOUND:
return 'User not found';
case AD_LOGIN_ERRORS.INVALID_CREDENTIALS:
return 'Invalid credentials';
case AD_LOGIN_ERRORS.MUST_RESET_PASSWORD:
case AD_LOGIN_ERRORS.PASSWORD_EXPIRED:
return 'Password expired';
case AD_LOGIN_ERRORS.ACCOUNT_DISABLED:
return 'Account disabled';
case AD_LOGIN_ERRORS.ACCOUNT_EXPIRED:
return 'Account expired';
case AD_LOGIN_ERRORS.ACCOUNT_LOCKED:
return 'Account locked';
case AD_LOGIN_ERRORS.NOT_PERMITTED_TO_LOGON_TIME:

@@ -69,3 +61,2 @@ case AD_LOGIN_ERRORS.NOT_PERMITTED_TO_LOGON_WORKSTATION:

};
exports.default = _default;

@@ -7,11 +7,6 @@ "use strict";

exports.default = void 0;
var _tls_utils = require("@mashroom/mashroom-utils/lib/tls_utils");
var _MashroomLdapSecurityProvider = _interopRequireDefault(require("./MashroomLdapSecurityProvider"));
var _LdapClientImpl = _interopRequireDefault(require("./LdapClientImpl"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const bootstrap = async (pluginName, pluginConfig, pluginContextHolder) => {

@@ -51,4 +46,3 @@ const {

};
var _default = bootstrap;
exports.default = _default;

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

exports.default = void 0;
var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
var _querystring = _interopRequireDefault(require("querystring"));
var _login_failure_reason = _interopRequireDefault(require("./login_failure_reason"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const LDAP_AUTH_USER_SESSION_KEY = '__MASHROOM_SECURITY_LDAP_AUTH_USER';
const LDAP_AUTH_EXPIRES_SESSION_KEY = '__MASHROOM_SECURITY_LDAP_AUTH_EXPIRES';
class MashroomLdapSecurityProvider {

@@ -33,10 +26,7 @@ constructor(_loginPage, _userSearchFilter, _groupSearchFilter, _extraDataMapping, _secretsMapping, groupToRoleMappingPath, userToRoleMappingPath, _ldapClient, _serverRootFolder, _authenticationTimeoutSec, loggerFactory) {

const logger = loggerFactory('mashroom.security.provider.ldap');
if (groupToRoleMappingPath) {
this._groupToRoleMappingPath = groupToRoleMappingPath;
if (!_path.default.isAbsolute(groupToRoleMappingPath)) {
this._groupToRoleMappingPath = _path.default.resolve(_serverRootFolder, groupToRoleMappingPath);
}
if (this._groupToRoleMappingPath && _fs.default.existsSync(this._groupToRoleMappingPath)) {

@@ -49,10 +39,7 @@ logger.info(`Using group to role mapping: ${this._groupToRoleMappingPath}`);

}
if (userToRoleMappingPath) {
this._userToRoleMappingPath = userToRoleMappingPath;
if (!_path.default.isAbsolute(userToRoleMappingPath)) {
this._userToRoleMappingPath = _path.default.resolve(_serverRootFolder, userToRoleMappingPath);
}
if (this._userToRoleMappingPath && _fs.default.existsSync(this._userToRoleMappingPath)) {

@@ -66,7 +53,5 @@ logger.info(`Using user to role mapping: ${this._userToRoleMappingPath}`);

}
async canAuthenticateWithoutUserInteraction() {
return false;
}
async authenticate(request, response, authenticationHints = {}) {

@@ -79,7 +64,4 @@ // Prevent a redirect loop if the login page is not accessible

}
const encodedRedirectUrl = encodeURIComponent(request.originalUrl);
const authenticationHintsQuery = _querystring.default.stringify(authenticationHints);
response.redirect(`${this._loginPage}?redirectUrl=${encodedRedirectUrl}${authenticationHintsQuery ? `&${authenticationHintsQuery}` : ''}`);

@@ -90,3 +72,2 @@ return {

}
async checkAuthentication(request) {

@@ -97,7 +78,5 @@ if (this.getUser(request)) {

}
getAuthenticationExpiration(request) {
return request.session[LDAP_AUTH_EXPIRES_SESSION_KEY];
}
async revokeAuthentication(request) {

@@ -107,6 +86,6 @@ delete request.session[LDAP_AUTH_EXPIRES_SESSION_KEY];

}
async login(request, username, password) {
const logger = request.pluginContext.loggerFactory('mashroom.security.provider.ldap'); // Because LDAP accepts logins with empty passwords (simple login) we need to be extra careful for
const logger = request.pluginContext.loggerFactory('mashroom.security.provider.ldap');
// Because LDAP accepts logins with empty passwords (simple login) we need to be extra careful for
if (!(password !== null && password !== void 0 && password.trim())) {

@@ -118,11 +97,7 @@ return {

}
let user = null;
const userSearchFilter = this._userSearchFilter.replace('@username@', username);
logger.debug(`Search for users: ${userSearchFilter}`);
const extraAttributes = [...(this._secretsMapping ? Object.values(this._secretsMapping) : []), ...(this._extraDataMapping ? Object.values(this._extraDataMapping) : [])];
const users = await this._ldapClient.search(userSearchFilter, extraAttributes);
if (users.length > 0) {

@@ -145,3 +120,2 @@ if (users.length === 1) {

}
if (user) {

@@ -157,15 +131,10 @@ try {

}
let displayName = user.displayName;
if (!displayName && user.sn) {
displayName = `${user.givenName ? `${user.givenName} ` : ''}${user.sn}`;
}
if (!displayName) {
displayName = user.cn;
}
let extraData = null;
if (this._extraDataMapping) {

@@ -179,5 +148,3 @@ extraData = {};

}
let secrets = null;
if (this._secretsMapping) {

@@ -191,7 +158,4 @@ secrets = {};

}
const groups = await this.getUserGroups(user, logger);
const roles = this._getRoles(request, username, groups, logger);
const mashroomUser = {

@@ -210,8 +174,7 @@ username,

request.session[LDAP_AUTH_EXPIRES_SESSION_KEY] = Date.now() + this._authenticationTimeoutSec * 1000;
if (request.session.cookie.maxAge && this._authenticationTimeoutSec * 1000 >= request.session.cookie.maxAge) {
logger.error(`Configuration error detected: The authenticationTimeoutSec (${this._authenticationTimeoutSec}s) value is higher than the session cookie maxAge (${Math.trunc(request.session.cookie.maxAge / 1000)}s). Since the authentication is stored in the session this might lead to unexpected behaviour.`);
} // Make sure the user is in the session when this method returns (file session store is async)
}
// Make sure the user is in the session when this method returns (file session store is async)
await new Promise(resolve => request.session.save(() => resolve()));

@@ -222,3 +185,2 @@ return {

}
return {

@@ -229,3 +191,2 @@ success: false,

}
getUser(request) {

@@ -235,9 +196,6 @@ if (!request.session) {

}
const timeout = request.session[LDAP_AUTH_EXPIRES_SESSION_KEY];
if (!timeout) {
return null;
}
if (timeout < Date.now()) {

@@ -247,6 +205,4 @@ delete request.session[LDAP_AUTH_USER_SESSION_KEY];

}
return request.session[LDAP_AUTH_USER_SESSION_KEY];
}
async getUserGroups(user, logger) {

@@ -256,5 +212,3 @@ if (!this._groupSearchFilter || !this._groupSearchFilter.trim()) {

}
const distinguishedName = this._escapeSpecialCharactersInDistinguishedName(user.dn);
const groupSearchFilter = `(&${this._groupSearchFilter}(member=${distinguishedName}))`;

@@ -265,9 +219,6 @@ logger.debug(`Search for user groups: ${groupSearchFilter}`);

}
_getRoles(request, username, groups, logger) {
const roles = [];
if (groups && groups.length > 0) {
const groupToRoles = this._getGroupToRoleMapping(request, logger);
if (groupToRoles) {

@@ -277,3 +228,2 @@ groups.forEach(group => {

const groupRoles = groupToRoles[group];
if (groupRoles && Array.isArray(groupRoles)) {

@@ -289,8 +239,5 @@ groupToRoles[group].forEach(role => roles.push(role));

}
const userToRoles = this._getUserToRoleMapping(request, logger);
if (userToRoles) {
const roleProp = Object.keys(userToRoles).find(u => u.toLowerCase() === username.toLowerCase());
if (roleProp) {

@@ -304,6 +251,4 @@ userToRoles[roleProp].forEach(role => {

}
return roles;
}
_getGroupToRoleMapping(request, logger) {

@@ -313,11 +258,8 @@ if (!this._groupToRoleMappingPath) {

}
if (this._groupToRoleMapping) {
return this._groupToRoleMapping;
}
if (_fs.default.existsSync(this._groupToRoleMappingPath)) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
this._groupToRoleMapping = require(this._groupToRoleMappingPath);
this._createRoleDefinitions(this._groupToRoleMapping, request, logger);

@@ -328,6 +270,4 @@ } else {

}
return this._groupToRoleMapping;
}
_getUserToRoleMapping(request, logger) {

@@ -337,11 +277,8 @@ if (!this._userToRoleMappingPath) {

}
if (this._userToRoleMapping) {
return this._userToRoleMapping;
}
if (_fs.default.existsSync(this._userToRoleMappingPath)) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
this._userToRoleMapping = require(this._userToRoleMappingPath);
this._createRoleDefinitions(this._userToRoleMapping, request, logger);

@@ -352,6 +289,4 @@ } else {

}
return this._userToRoleMapping;
}
async _createRoleDefinitions(mapping, request, logger) {

@@ -371,3 +306,2 @@ const securityService = request.pluginContext.services.security.service;

logger.debug('Adding new role definitions:', roles);
for (const id of roles) {

@@ -378,6 +312,6 @@ await securityService.addRoleDefinition(request, {

}
} // Escape special characters in the distinguished name.
}
// Escape special characters in the distinguished name.
// See RFC 2253: https://datatracker.ietf.org/doc/html/rfc2253
_escapeSpecialCharactersInDistinguishedName(dn) {

@@ -390,5 +324,3 @@ let escapedDn = dn;

}
}
exports.default = MashroomLdapSecurityProvider;

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

"license": "MIT",
"version": "2.1.3",
"version": "2.2.0",
"files": [

@@ -13,11 +13,11 @@ "dist/**"

"dependencies": {
"ldapjs": "^2.3.2"
"express": "^4.18.2",
"ldapjs": "^2.3.3"
},
"devDependencies": {
"@mashroom/mashroom": "2.1.3",
"@mashroom/mashroom-security": "2.1.3",
"@mashroom/mashroom-utils": "2.1.3",
"@types/express": "^4.17.13",
"@types/ldapjs": "^2.2.2",
"express": "^4.18.1"
"@mashroom/mashroom": "2.2.0",
"@mashroom/mashroom-security": "2.2.0",
"@mashroom/mashroom-utils": "2.2.0",
"@types/express": "^4.17.14",
"@types/ldapjs": "^2.2.4"
},

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

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc