ep_ldapauth
Advanced tools
Comparing version 0.0.5 to 0.1.0
@@ -35,3 +35,3 @@ // Copyright 2013 Andrew Grimberg <tykeal@bardicgrove.org> | ||
var ldap = new MyLdapAuth({ | ||
var authenticateLDAP = new MyLdapAuth({ | ||
url: settings.users.ldapauth.url, | ||
@@ -46,5 +46,11 @@ adminDn: settings.users.ldapauth.searchDN, | ||
// Attempt to authenticate the user | ||
ldap.authenticate(username, password, function(err, user) { | ||
authenticateLDAP.authenticate(username, password, function(err, user) { | ||
if (err) { | ||
console.error('ep_ldapauth.authenticate: LDAP auth error: %s', err); | ||
authenticateLDAP.close(function (err) { | ||
if (err) { | ||
console.error('ep_ldapauth.authenticate: LDAP close error: %s', err); | ||
} | ||
}); | ||
authenticateLDAP = null; | ||
return cb([false]); | ||
@@ -55,7 +61,18 @@ } | ||
context.req.session.user = { username: username, displayName: user.cn }; | ||
if (settings.users.ldapauth.groupAttributeIsDN) { | ||
context.req.session.user.userDN = user.dn; | ||
} | ||
settings.globalUserName = username; | ||
console.debug('ep_ldapauth.authenticate: deferring setting of username [%s] to CLIENT_READY for express_sid = %s', username, express_sid); | ||
authenticateLDAP.close(function (err) { | ||
if (err) { | ||
console.error('ep_ldapauth.authenticate: LDAP close error: %s', err); | ||
} | ||
}); | ||
authenticateLDAP = null; | ||
console.debug('ep_ldapauth.authenticate: successful authentication'); | ||
return cb([true]); | ||
}); | ||
} else { | ||
console.debug('ep_ldapauth.authenticate: failed authentication no auth headers'); | ||
return cb([false]); | ||
@@ -68,19 +85,10 @@ } | ||
var ldap = new MyLdapAuth({ | ||
url: settings.users.ldapauth.url, | ||
adminDn: settings.users.ldapauth.searchDN, | ||
adminPassword: settings.users.ldapauth.searchPWD, | ||
searchBase: settings.users.ldapauth.accountBase, | ||
searchFilter: settings.users.ldapauth.accountPattern, | ||
groupSearchBase: settings.users.ldapauth.groupSearchBase, | ||
groupAttribute: settings.users.ldapauth.groupAttribute, | ||
groupAttributeIsDN: settings.users.ldapauth.groupAttributeIsDN, | ||
searchScope: settings.users.ldapauth.searchScope, | ||
groupSearch: settings.users.ldapauth.groupSearch, | ||
cache: true | ||
}); | ||
userDN = null; | ||
if (typeof(context.req.session.user) != 'undefined' && | ||
typeof(context.req.session.user.username) != 'undefined') { | ||
if (typeof(context.req.session.user) !== 'undefined' && | ||
typeof(context.req.session.user.username) !== 'undefined') { | ||
username = context.req.session.user.username; | ||
if (typeof(context.req.session.user.userDN !== 'undefined')) { | ||
userDN = context.req.session.user.userDN; | ||
} | ||
} else { | ||
@@ -95,6 +103,27 @@ console.debug('ep_ldapauth.authorize: no username in user object'); | ||
} else if (context.resource.match(/^\/admin/)) { | ||
console.debug('ep_ldapauth.authorize: authorizing along administrative path %s', context.resource); | ||
ldap.groupsearch(username, function(err, groups) { | ||
console.debug('ep_ldapauth.authorize: attempting to authorize along administrative path %s', context.resource); | ||
var authorizeLDAP = new MyLdapAuth({ | ||
url: settings.users.ldapauth.url, | ||
adminDn: settings.users.ldapauth.searchDN, | ||
adminPassword: settings.users.ldapauth.searchPWD, | ||
searchBase: settings.users.ldapauth.accountBase, | ||
searchFilter: settings.users.ldapauth.accountPattern, | ||
groupSearchBase: settings.users.ldapauth.groupSearchBase, | ||
groupAttribute: settings.users.ldapauth.groupAttribute, | ||
groupAttributeIsDN: settings.users.ldapauth.groupAttributeIsDN, | ||
searchScope: settings.users.ldapauth.searchScope, | ||
groupSearch: settings.users.ldapauth.groupSearch, | ||
cache: true | ||
}); | ||
authorizeLDAP.groupsearch(username, userDN, function(err, groups) { | ||
if (err) { | ||
console.error('ep_ldapauth.authorize: LDAP groupsearch error: %s', err); | ||
authorizeLDAP.close(function (err) { | ||
if (err) { | ||
console.error('ep_ldapauth.authorize: LDAP close error: %s', err); | ||
} | ||
}); | ||
authorizeLDAP = null; | ||
return cb([false]); | ||
@@ -107,5 +136,19 @@ } | ||
context.req.session.user.is_admin = true; | ||
authorizeLDAP.close(function (err) { | ||
if (err) { | ||
console.error('ep_ldapauth.authorize: LDAP close error: %s', err); | ||
} | ||
}); | ||
authorizeLDAP = null; | ||
console.debug('ep_ldapauth.authorize: successful authorization'); | ||
return cb([true]); | ||
} else { | ||
context.req.session.user.is_admin = false; | ||
authorizeLDAP.close(function (err) { | ||
if (err) { | ||
console.error('ep_ldapauth.authorize: LDAP close error: %s', err); | ||
} | ||
}); | ||
authorizeLDAP = null; | ||
console.debug('ep_ldapauth.authorize: failed authorization'); | ||
return cb([false]); | ||
@@ -112,0 +155,0 @@ } |
@@ -5,8 +5,24 @@ // Copyright 2013 Andrew Grimberg <tykeal@bardicgrove.org> | ||
var assert = require('assert'); | ||
var util = require('util'); | ||
var LdapAuth = require('ldapauth'); | ||
var ldap = require('ldapjs'); | ||
/** | ||
* Create the MyLdapAuth class which is a super class of LdapAuth | ||
* Create the MyLdapAuth class which is a former super class of LdapAuth | ||
* until I ended up having to reimplement too much of it | ||
* | ||
* @param opts {Object} config options. Keys (required, unless stated | ||
* otherwise) are: | ||
* url {String} E.g. 'ldaps://ldap.example.com:663' | ||
* adminDn {String} E.g. 'uid=myapp,ou=users,o=example.com' | ||
* adminPassword {String} Password for adminDn | ||
* searchBase {String} The base DN from which to search for users by | ||
* username. E.g. 'ou=users,o=example.com' | ||
* searchFilter {String} LDAP search filter with which to find a user by | ||
* username, e.g. '(uid={{username}})'. Use the literal '{{username}}' | ||
* to have the given username be interpolated in for the LDAP search. | ||
* log4js {Module} Optional. The require'd log4js module to use for logging. | ||
* If given this will result in TRACE-level loggin for MyLdapAuth | ||
* verbose {Boolean} Optional, default false. if `log4js` is also given, | ||
* this will add TRACE-level logging for ldapjs (quite verbose). | ||
* Additional @params opts (see LdapAuth for base params) | ||
@@ -25,13 +41,127 @@ * groupSearchBase {string} Base search location for groups | ||
function MyLdapAuth(opts) { | ||
MyLdapAuth.super_.call(this, opts); | ||
this.opts = opts; | ||
assert.ok(opts.url); | ||
assert.ok(opts.adminDn); | ||
assert.ok(opts.searchBase); | ||
assert.ok(opts.searchFilter); | ||
this.log = opts.log4js && opts.jog4js.getLogger('ldapauth'); | ||
var clientOpts = {url: opts.url}; | ||
if (opts.log4js && opts.verbose) { | ||
clientOpts.log4js = opts.log4js; | ||
} | ||
} | ||
util.inherits(MyLdapAuth, LdapAuth); | ||
MyLdapAuth.prototype.close = function (cb) { | ||
var self = this; | ||
if (typeof(self._adminClient) !== 'undefined') { | ||
self._adminClient.unbind(function (err) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
self._adminClient = null; | ||
return cb(); | ||
}); | ||
} | ||
} | ||
MyLdapAuth.prototype._adminBind = function (cb) { | ||
var self = this; | ||
if (typeof(self._adminClient) !== 'undefined') { | ||
return cb(); | ||
} | ||
var clientOpts = {url: self.opts.url}; | ||
self._adminClient = ldap.createClient(clientOpts); | ||
self._adminClient.bind(self.opts.adminDn, self.opts.adminPassword, | ||
function (err) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
return cb(); | ||
}); | ||
} | ||
/** | ||
* Find the user record for the given username. | ||
* | ||
* @param username {String} | ||
* @param cb {Function} `function (err, user)`. If not such user is | ||
* found but no error processing, then `user` is undefined. | ||
*/ | ||
MyLdapAuth.prototype._findUser = function (username, cb) { | ||
var self = this; | ||
if (!username) { | ||
return cb("empty username"); | ||
} | ||
self._adminBind(function (err) { | ||
if (err) | ||
return cb(err); | ||
var searchFilter = self.opts.searchFilter.replace('{{username}}', username); | ||
var opts = {filter: searchFilter, scope: 'sub'}; | ||
self._adminClient.search(self.opts.searchBase, opts, | ||
function (err, result) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
var items = []; | ||
result.on('searchEntry', function (entry) { | ||
items.push(entry.object); | ||
}); | ||
result.on('error', function (err) { | ||
return cb(err); | ||
}); | ||
result.on('end', function (result) { | ||
if (result.status !== 0) { | ||
var err = 'non-zero status from LDAP search: ' + result.status; | ||
return cb(err); | ||
} | ||
switch (items.length) { | ||
case 0: | ||
return cb(); | ||
case 1: | ||
return cb(null, items[0]); | ||
default: | ||
return cb(util.format( | ||
'unexpected number of matches (%s) for "%s" username', | ||
items.length, username)); | ||
} | ||
}); | ||
}); | ||
}); | ||
} | ||
MyLdapAuth.prototype.authenticate = function (username, password, cb) { | ||
var self = this; | ||
// 1. Find the user DN in question. | ||
self._findUser(username, function (err, user) { | ||
if (err) | ||
return cb(err); | ||
if (!user) | ||
return cb(util.format('no such user: "%s"', username)); | ||
// 2. Attempt to bind as that user to check password. | ||
var clientOpts = {url: self.opts.url}; | ||
var userClient = ldap.createClient(clientOpts); | ||
userClient.bind(user.dn, password, function (err) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
// User auth's cleanly, destroy the LDAP bind | ||
userClient.unbind(); | ||
userClient = null; | ||
return cb(null, user); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Searches groups to see if a given user is in them | ||
* | ||
* @param username {string} Username to lookup against groupSearch | ||
* @param userDN {string} | ||
*/ | ||
MyLdapAuth.prototype.groupsearch = function (username, cb) { | ||
MyLdapAuth.prototype.groupsearch = function (username, userDN, cb) { | ||
var self = this; | ||
@@ -46,10 +176,3 @@ if (!username) { | ||
if (self.opts.groupAttributeIsDN) { | ||
// We need to lookup the user DN | ||
self._findUser(username, function (err, user) { | ||
if (err) | ||
return cb(err); | ||
if (!user) | ||
return cb(util.format('no such user: "%s"', username)); | ||
usersearch = user.dn; | ||
}); | ||
usersearch = userDN; | ||
} | ||
@@ -93,4 +216,5 @@ | ||
return (item === usersearch); | ||
})) | ||
})) { | ||
return cb(null, items[0]); | ||
} | ||
@@ -97,0 +221,0 @@ return cb(util.format('LDAP groupsearch: "%s" is not a member of "%s"', |
@@ -5,3 +5,3 @@ { | ||
"author": "Andrew Grimberg <agrimberg@linuxfoundation.org>", | ||
"version": "0.0.5", | ||
"version": "0.1.0", | ||
"license": "GPLv2", | ||
@@ -13,5 +13,5 @@ "repository": { | ||
"dependencies": { | ||
"ldapauth": ">= 2.2.2", | ||
"async-stacktrace": "0.0.2" | ||
"async-stacktrace": "0.0.2", | ||
"ldapjs": "0.6.3" | ||
} | ||
} |
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
33598
382
+ Addedldapjs@0.6.3
+ Addedasn1@0.1.11(transitive)
+ Addedassert-plus@0.1.2(transitive)
+ Addedbuffertools@1.1.0(transitive)
+ Addedbunyan@0.21.1(transitive)
+ Addedldapjs@0.6.3(transitive)
+ Addedonce@1.1.1(transitive)
+ Addedpooling@0.4.4(transitive)
+ Addedvasync@1.3.3(transitive)
- Removedldapauth@>= 2.2.2
- Removedasn1@0.2.1(transitive)
- Removedassert-plus@0.1.40.1.5(transitive)
- Removedbackoff@2.3.0(transitive)
- Removedbcrypt@0.8.7(transitive)
- Removedbindings@1.2.1(transitive)
- Removedbunyan@0.22.1(transitive)
- Removedldapauth@2.3.1(transitive)
- Removedldapjs@0.7.1(transitive)
- Removedlru-cache@2.0.4(transitive)
- Removednan@2.3.5(transitive)
- Removedonce@1.3.0(transitive)
- Removedpooling@0.4.6(transitive)
- Removedvasync@1.4.0(transitive)