@mashroom/mashroom-security-provider-ldap
Advanced tools
Comparing version 2.1.3 to 2.2.0
@@ -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; |
@@ -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": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
5
584
29183
2
+ Addedexpress@^4.18.2
+ Addedaccepts@1.3.8(transitive)
+ Addedarray-flatten@1.1.1(transitive)
+ Addedbody-parser@1.20.3(transitive)
+ Addedbytes@3.1.2(transitive)
+ Addedcall-bind@1.0.7(transitive)
+ Addedcontent-disposition@0.5.4(transitive)
+ Addedcontent-type@1.0.5(transitive)
+ Addedcookie@0.6.0(transitive)
+ Addedcookie-signature@1.0.6(transitive)
+ Addeddebug@2.6.9(transitive)
+ Addeddefine-data-property@1.1.4(transitive)
+ Addeddepd@2.0.0(transitive)
+ Addeddestroy@1.2.0(transitive)
+ Addedee-first@1.1.1(transitive)
+ Addedencodeurl@1.0.22.0.0(transitive)
+ Addedes-define-property@1.0.0(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedescape-html@1.0.3(transitive)
+ Addedetag@1.8.1(transitive)
+ Addedexpress@4.21.0(transitive)
+ Addedfinalhandler@1.3.1(transitive)
+ Addedforwarded@0.2.0(transitive)
+ Addedfresh@0.5.2(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedget-intrinsic@1.2.4(transitive)
+ Addedgopd@1.0.1(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-proto@1.0.3(transitive)
+ Addedhas-symbols@1.0.3(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedhttp-errors@2.0.0(transitive)
+ Addediconv-lite@0.4.24(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedipaddr.js@1.9.1(transitive)
+ Addedmedia-typer@0.3.0(transitive)
+ Addedmerge-descriptors@1.0.3(transitive)
+ Addedmethods@1.1.2(transitive)
+ Addedmime@1.6.0(transitive)
+ Addedmime-db@1.52.0(transitive)
+ Addedmime-types@2.1.35(transitive)
+ Addedms@2.0.02.1.3(transitive)
+ Addednegotiator@0.6.3(transitive)
+ Addedobject-inspect@1.13.2(transitive)
+ Addedon-finished@2.4.1(transitive)
+ Addedparseurl@1.3.3(transitive)
+ Addedpath-to-regexp@0.1.10(transitive)
+ Addedproxy-addr@2.0.7(transitive)
+ Addedqs@6.13.0(transitive)
+ Addedrange-parser@1.2.1(transitive)
+ Addedraw-body@2.5.2(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedsend@0.19.0(transitive)
+ Addedserve-static@1.16.2(transitive)
+ Addedset-function-length@1.2.2(transitive)
+ Addedsetprototypeof@1.2.0(transitive)
+ Addedside-channel@1.0.6(transitive)
+ Addedstatuses@2.0.1(transitive)
+ Addedtoidentifier@1.0.1(transitive)
+ Addedtype-is@1.6.18(transitive)
+ Addedunpipe@1.0.0(transitive)
+ Addedutils-merge@1.0.1(transitive)
+ Addedvary@1.1.2(transitive)
Updatedldapjs@^2.3.3