Add an LDAP user search filter in the configuration filte to specify the user attribute to search for in LDAP
parent
95130f491c
commit
c7e4f76b9c
|
@ -3,9 +3,11 @@
|
||||||
logs_level: info
|
logs_level: info
|
||||||
|
|
||||||
# Configuration of LDAP
|
# Configuration of LDAP
|
||||||
|
# Example: for user john, the DN will be cn=john,ou=users,dc=example,dc=com
|
||||||
ldap:
|
ldap:
|
||||||
url: ldap://ldap
|
url: ldap://ldap
|
||||||
base_dn: ou=users,dc=example,dc=com
|
user_search_base: ou=users,dc=example,dc=com
|
||||||
|
user_search_filter: cn
|
||||||
user: cn=admin,dc=example,dc=com
|
user: cn=admin,dc=example,dc=com
|
||||||
password: password
|
password: password
|
||||||
|
|
||||||
|
|
|
@ -29,3 +29,18 @@ uid: user
|
||||||
uidnumber: 1000
|
uidnumber: 1000
|
||||||
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
||||||
|
|
||||||
|
dn: uid=useruid,ou=users,dc=example,dc=com
|
||||||
|
cn: useruid
|
||||||
|
gidnumber: 500
|
||||||
|
givenname: user
|
||||||
|
homedirectory: /home/user1
|
||||||
|
loginshell: /bin/sh
|
||||||
|
objectclass: inetOrgPerson
|
||||||
|
objectclass: posixAccount
|
||||||
|
objectclass: top
|
||||||
|
mail: useruid@example.com
|
||||||
|
sn: User
|
||||||
|
uid: useruid
|
||||||
|
uidnumber: 1001
|
||||||
|
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@ var yaml_config = YAML.load(config_path);
|
||||||
var config = {
|
var config = {
|
||||||
port: process.env.PORT || 8080,
|
port: process.env.PORT || 8080,
|
||||||
ldap_url: yaml_config.ldap.url || 'ldap://127.0.0.1:389',
|
ldap_url: yaml_config.ldap.url || 'ldap://127.0.0.1:389',
|
||||||
ldap_users_dn: yaml_config.ldap.base_dn,
|
ldap_user_search_base: yaml_config.ldap.user_search_base,
|
||||||
|
ldap_user_search_filter: yaml_config.ldap.user_search_filter,
|
||||||
ldap_user: yaml_config.ldap.user,
|
ldap_user: yaml_config.ldap.user,
|
||||||
ldap_password: yaml_config.ldap.password,
|
ldap_password: yaml_config.ldap.password,
|
||||||
session_domain: yaml_config.session.domain,
|
session_domain: yaml_config.session.domain,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
validate: validateCredentials,
|
validate: validate_credentials,
|
||||||
get_email: retrieve_email,
|
get_email: retrieve_email,
|
||||||
update_password: update_password
|
update_password: update_password
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,12 @@ var Promise = require('bluebird');
|
||||||
var exceptions = require('./exceptions');
|
var exceptions = require('./exceptions');
|
||||||
var Dovehash = require('dovehash');
|
var Dovehash = require('dovehash');
|
||||||
|
|
||||||
function validateCredentials(ldap_client, username, password, users_dn) {
|
function validate_credentials(ldap_client, username, password, user_base, user_filter) {
|
||||||
var userDN = util.format("cn=%s,%s", username, users_dn);
|
// if not provided, default to cn
|
||||||
|
if(!user_filter) user_filter = 'cn';
|
||||||
|
|
||||||
|
var userDN = util.format("%s=%s,%s", user_filter, username, user_base);
|
||||||
|
console.log(userDN);
|
||||||
var bind_promised = Promise.promisify(ldap_client.bind, { context: ldap_client });
|
var bind_promised = Promise.promisify(ldap_client.bind, { context: ldap_client });
|
||||||
return bind_promised(userDN, password)
|
return bind_promised(userDN, password)
|
||||||
.error(function(err) {
|
.error(function(err) {
|
||||||
|
@ -20,16 +24,19 @@ function validateCredentials(ldap_client, username, password, users_dn) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function retrieve_email(ldap_client, username, users_dn) {
|
function retrieve_email(ldap_client, username, user_base, user_filter) {
|
||||||
var userDN = util.format("cn=%s,%s", username, users_dn);
|
// if not provided, default to cn
|
||||||
|
if(!user_filter) user_filter = 'cn';
|
||||||
|
|
||||||
|
var userDN = util.format("%s=%s,%s", user_filter, username, user_base);
|
||||||
|
console.log(userDN);
|
||||||
var search_promised = Promise.promisify(ldap_client.search, { context: ldap_client });
|
var search_promised = Promise.promisify(ldap_client.search, { context: ldap_client });
|
||||||
var query = {};
|
var query = {};
|
||||||
query.sizeLimit = 1;
|
query.sizeLimit = 1;
|
||||||
query.attributes = ['mail'];
|
query.attributes = ['mail'];
|
||||||
var base_dn = userDN;
|
|
||||||
|
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
search_promised(base_dn, query)
|
search_promised(userDN, query)
|
||||||
.then(function(res) {
|
.then(function(res) {
|
||||||
var doc;
|
var doc;
|
||||||
res.on('searchEntry', function(entry) {
|
res.on('searchEntry', function(entry) {
|
||||||
|
@ -49,7 +56,12 @@ function retrieve_email(ldap_client, username, users_dn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_password(ldap_client, ldap, username, new_password, config) {
|
function update_password(ldap_client, ldap, username, new_password, config) {
|
||||||
var userDN = util.format("cn=%s,%s", username, config.ldap_users_dn);
|
var user_filter = config.ldap_user_search_filter;
|
||||||
|
// if not provided, default to cn
|
||||||
|
if(!user_filter) user_filter = 'cn';
|
||||||
|
|
||||||
|
var userDN = util.format("%s=%s,%s", user_filter, username,
|
||||||
|
config.ldap_user_search_base);
|
||||||
var encoded_password = Dovehash.encode('SSHA', new_password);
|
var encoded_password = Dovehash.encode('SSHA', new_password);
|
||||||
var change = new ldap.Change({
|
var change = new ldap.Change({
|
||||||
operation: 'replace',
|
operation: 'replace',
|
||||||
|
|
|
@ -23,18 +23,20 @@ function first_factor(req, res) {
|
||||||
|
|
||||||
logger.debug('1st factor: Start bind operation against LDAP');
|
logger.debug('1st factor: Start bind operation against LDAP');
|
||||||
logger.debug('1st factor: username=%s', username);
|
logger.debug('1st factor: username=%s', username);
|
||||||
logger.debug('1st factor: base_dn=%s', config.ldap_users_dn);
|
logger.debug('1st factor: base_dn=%s', config.ldap_user_search_base);
|
||||||
|
logger.debug('1st factor: user_filter=%s', config.ldap_user_search_filter);
|
||||||
|
|
||||||
regulator.regulate(username)
|
regulator.regulate(username)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return ldap.validate(ldap_client, username, password, config.ldap_users_dn);
|
return ldap.validate(ldap_client, username, password, config.ldap_user_search_base, config.ldap_user_search_filter);
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
objectPath.set(req, 'session.auth_session.userid', username);
|
objectPath.set(req, 'session.auth_session.userid', username);
|
||||||
objectPath.set(req, 'session.auth_session.first_factor', true);
|
objectPath.set(req, 'session.auth_session.first_factor', true);
|
||||||
logger.info('1st factor: LDAP binding successful');
|
logger.info('1st factor: LDAP binding successful');
|
||||||
logger.debug('1st factor: Retrieve email from LDAP');
|
logger.debug('1st factor: Retrieve email from LDAP');
|
||||||
return ldap.get_email(ldap_client, username, config.ldap_users_dn)
|
return ldap.get_email(ldap_client, username, config.ldap_user_search_base,
|
||||||
|
config.ldap_user_search_filter)
|
||||||
})
|
})
|
||||||
.then(function(doc) {
|
.then(function(doc) {
|
||||||
var email = objectPath.get(doc, 'mail');
|
var email = objectPath.get(doc, 'mail');
|
||||||
|
|
|
@ -27,7 +27,8 @@ function pre_check(req) {
|
||||||
var ldap_client = req.app.get('ldap client');
|
var ldap_client = req.app.get('ldap client');
|
||||||
var config = req.app.get('config');
|
var config = req.app.get('config');
|
||||||
|
|
||||||
return ldap.get_email(ldap_client, userid, config.ldap_users_dn)
|
return ldap.get_email(ldap_client, userid, config.ldap_user_search_base,
|
||||||
|
config.ldap_user_search_filter)
|
||||||
.then(function(doc) {
|
.then(function(doc) {
|
||||||
var email = objectPath.get(doc, 'mail');
|
var email = objectPath.get(doc, 'mail');
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,8 @@ describe('test the first factor validation route', function() {
|
||||||
search: sinon.stub()
|
search: sinon.stub()
|
||||||
}
|
}
|
||||||
var config = {
|
var config = {
|
||||||
ldap_users_dn: 'dc=example,dc=com'
|
ldap_user_search_base: 'ou=users,dc=example,dc=com',
|
||||||
|
ldap_user_search_filter: 'uid'
|
||||||
}
|
}
|
||||||
|
|
||||||
var search_doc = {
|
var search_doc = {
|
||||||
|
@ -42,7 +43,7 @@ describe('test the first factor validation route', function() {
|
||||||
|
|
||||||
var app_get = sinon.stub();
|
var app_get = sinon.stub();
|
||||||
app_get.withArgs('ldap client').returns(ldap_interface_mock);
|
app_get.withArgs('ldap client').returns(ldap_interface_mock);
|
||||||
app_get.withArgs('config').returns(ldap_interface_mock);
|
app_get.withArgs('config').returns(config);
|
||||||
app_get.withArgs('logger').returns(winston);
|
app_get.withArgs('logger').returns(winston);
|
||||||
app_get.withArgs('authentication regulator').returns(regulator);
|
app_get.withArgs('authentication regulator').returns(regulator);
|
||||||
|
|
||||||
|
@ -79,6 +80,21 @@ describe('test the first factor validation route', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should bind user based on LDAP DN', function(done) {
|
||||||
|
ldap_interface_mock.bind = sinon.spy(function(dn) {
|
||||||
|
if(dn == 'uid=username,ou=users,dc=example,dc=com') done();
|
||||||
|
});
|
||||||
|
first_factor(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should retrieve email from LDAP', function(done) {
|
||||||
|
ldap_interface_mock.bind.yields(undefined);
|
||||||
|
ldap_interface_mock.search = sinon.spy(function(dn) {
|
||||||
|
if(dn == 'uid=username,ou=users,dc=example,dc=com') done();
|
||||||
|
});
|
||||||
|
first_factor(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return status code 401 when LDAP binding fails', function(done) {
|
it('should return status code 401 when LDAP binding fails', function(done) {
|
||||||
res.send = sinon.spy(function(data) {
|
res.send = sinon.spy(function(data) {
|
||||||
assert.equal(401, res.status.getCall(0).args[0]);
|
assert.equal(401, res.status.getCall(0).args[0]);
|
||||||
|
|
|
@ -46,7 +46,8 @@ describe('test reset password', function() {
|
||||||
req.app.get.withArgs('ldap client').returns(ldap_client);
|
req.app.get.withArgs('ldap client').returns(ldap_client);
|
||||||
|
|
||||||
config = {};
|
config = {};
|
||||||
config.ldap_users_dn = 'dc=example,dc=com';
|
config.ldap_user_search_base = 'dc=example,dc=com';
|
||||||
|
config.ldap_user_search_filter = 'cn';
|
||||||
req.app.get.withArgs('config').returns(config);
|
req.app.get.withArgs('config').returns(config);
|
||||||
|
|
||||||
res = {};
|
res = {};
|
||||||
|
@ -75,6 +76,15 @@ describe('test reset password', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should perform a search in ldap to find email address', function(done) {
|
||||||
|
config.ldap_user_search_filter = 'uid';
|
||||||
|
ldap_client.search = sinon.spy(function(dn) {
|
||||||
|
console.log(dn);
|
||||||
|
if(dn == 'uid=user,dc=example,dc=com') done();
|
||||||
|
});
|
||||||
|
reset_password.icheck_interface.pre_check_callback(req);
|
||||||
|
});
|
||||||
|
|
||||||
it('should returns identity when ldap replies', function(done) {
|
it('should returns identity when ldap replies', function(done) {
|
||||||
var doc = {};
|
var doc = {};
|
||||||
doc.object = {};
|
doc.object = {};
|
||||||
|
|
|
@ -54,7 +54,7 @@ describe('test data persistence', function() {
|
||||||
port: PORT,
|
port: PORT,
|
||||||
totp_secret: 'totp_secret',
|
totp_secret: 'totp_secret',
|
||||||
ldap_url: 'ldap://127.0.0.1:389',
|
ldap_url: 'ldap://127.0.0.1:389',
|
||||||
ldap_users_dn: 'ou=users,dc=example,dc=com',
|
ldap_user_search_base: 'ou=users,dc=example,dc=com',
|
||||||
session_secret: 'session_secret',
|
session_secret: 'session_secret',
|
||||||
session_max_age: 50000,
|
session_max_age: 50000,
|
||||||
store_directory: tmpDir.name,
|
store_directory: tmpDir.name,
|
||||||
|
|
|
@ -25,9 +25,8 @@ describe('test ldap validation', function() {
|
||||||
function test_validate() {
|
function test_validate() {
|
||||||
var username = 'user';
|
var username = 'user';
|
||||||
var password = 'password';
|
var password = 'password';
|
||||||
var ldap_url = 'http://ldap';
|
|
||||||
var users_dn = 'dc=example,dc=com';
|
var users_dn = 'dc=example,dc=com';
|
||||||
return ldap.validate(ldap_client, username, password, ldap_url, users_dn);
|
return ldap.validate(ldap_client, username, password, users_dn);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should bind the user if good credentials provided', function() {
|
it('should bind the user if good credentials provided', function() {
|
||||||
|
@ -35,6 +34,29 @@ describe('test ldap validation', function() {
|
||||||
return test_validate();
|
return test_validate();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should bind the user with correct DN', function(done) {
|
||||||
|
var username = 'user';
|
||||||
|
var password = 'password';
|
||||||
|
var user_search_base = 'dc=example,dc=com';
|
||||||
|
var user_search_filter = 'uid';
|
||||||
|
ldap_client.bind = sinon.spy(function(dn) {
|
||||||
|
if(dn == 'uid=user,dc=example,dc=com') done();
|
||||||
|
});
|
||||||
|
ldap.validate(ldap_client, username, password, user_search_base,
|
||||||
|
user_search_filter);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should default to cn user search filter if no filter provided', function(done) {
|
||||||
|
var username = 'user';
|
||||||
|
var password = 'password';
|
||||||
|
var user_search_base = 'dc=example,dc=com';
|
||||||
|
ldap_client.bind = sinon.spy(function(dn) {
|
||||||
|
if(dn == 'cn=user,dc=example,dc=com') done();
|
||||||
|
});
|
||||||
|
ldap.validate(ldap_client, username, password, user_search_base,
|
||||||
|
undefined);
|
||||||
|
});
|
||||||
|
|
||||||
// cover an issue with promisify context
|
// cover an issue with promisify context
|
||||||
it('should promisify correctly', function() {
|
it('should promisify correctly', function() {
|
||||||
function LdapClient() {
|
function LdapClient() {
|
||||||
|
@ -76,6 +98,14 @@ describe('test ldap validation', function() {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should use the user filter', function(done) {
|
||||||
|
ldap_client.search = sinon.spy(function(dn) {
|
||||||
|
if(dn == 'uid=username,ou=users,dc=example,dc=com') done();
|
||||||
|
});
|
||||||
|
ldap.get_email(ldap_client, 'username', 'ou=users,dc=example,dc=com',
|
||||||
|
'uid')
|
||||||
|
});
|
||||||
|
|
||||||
it('should fail on error with search method', function(done) {
|
it('should fail on error with search method', function(done) {
|
||||||
var expected_doc = {};
|
var expected_doc = {};
|
||||||
expected_doc.mail = [];
|
expected_doc.mail = [];
|
||||||
|
@ -97,7 +127,7 @@ describe('test ldap validation', function() {
|
||||||
change.modification.userPassword = 'new-password';
|
change.modification.userPassword = 'new-password';
|
||||||
|
|
||||||
var config = {};
|
var config = {};
|
||||||
config.ldap_users_dn = 'dc=example,dc=com';
|
config.ldap_user_search_base = 'dc=example,dc=com';
|
||||||
config.ldap_user = 'admin';
|
config.ldap_user = 'admin';
|
||||||
|
|
||||||
var userdn = 'cn=user,dc=example,dc=com';
|
var userdn = 'cn=user,dc=example,dc=com';
|
||||||
|
@ -135,6 +165,22 @@ describe('test ldap validation', function() {
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should use the user filter', function(done) {
|
||||||
|
var ldapjs = {};
|
||||||
|
ldapjs.Change = sinon.spy();
|
||||||
|
|
||||||
|
var config = {};
|
||||||
|
config.ldap_user_search_base = 'ou=users,dc=example,dc=com';
|
||||||
|
config.ldap_user_search_filter = 'uid';
|
||||||
|
config.ldap_user = 'admin';
|
||||||
|
|
||||||
|
ldap_client.bind.yields(undefined);
|
||||||
|
ldap_client.modify = sinon.spy(function(dn) {
|
||||||
|
if(dn == 'uid=username,ou=users,dc=example,dc=com') done();
|
||||||
|
});
|
||||||
|
ldap.update_password(ldap_client, ldapjs, 'username', 'newpass', config)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,8 @@ describe('test the server', function() {
|
||||||
port: PORT,
|
port: PORT,
|
||||||
totp_secret: 'totp_secret',
|
totp_secret: 'totp_secret',
|
||||||
ldap_url: 'ldap://127.0.0.1:389',
|
ldap_url: 'ldap://127.0.0.1:389',
|
||||||
ldap_users_dn: 'ou=users,dc=example,dc=com',
|
ldap_user_search_base: 'ou=users,dc=example,dc=com',
|
||||||
|
ldap_user_search_filter: 'cn',
|
||||||
ldap_user: 'cn=admin,dc=example,dc=com',
|
ldap_user: 'cn=admin,dc=example,dc=com',
|
||||||
ldap_password: 'password',
|
ldap_password: 'password',
|
||||||
session_secret: 'session_secret',
|
session_secret: 'session_secret',
|
||||||
|
|
|
@ -4,9 +4,11 @@ var server = require('../../src/lib/server');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
|
|
||||||
describe('test server configuration', function() {
|
describe('test server configuration', function() {
|
||||||
it('should set cookie scope to domain set in the config', function() {
|
var deps;
|
||||||
var config = {};
|
var config;
|
||||||
config.session_domain = 'example.com';
|
|
||||||
|
before(function() {
|
||||||
|
config = {};
|
||||||
config.notifier = {
|
config.notifier = {
|
||||||
gmail: {
|
gmail: {
|
||||||
user: 'user@example.com',
|
user: 'user@example.com',
|
||||||
|
@ -22,13 +24,17 @@ describe('test server configuration', function() {
|
||||||
return transporter;
|
return transporter;
|
||||||
});
|
});
|
||||||
|
|
||||||
var deps = {};
|
deps = {};
|
||||||
deps.nedb = require('nedb');
|
deps.nedb = require('nedb');
|
||||||
deps.nodemailer = nodemailer;
|
deps.nodemailer = nodemailer;
|
||||||
deps.session = sinon.spy(function() {
|
deps.session = sinon.spy(function() {
|
||||||
return function(req, res, next) { next(); };
|
return function(req, res, next) { next(); };
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should set cookie scope to domain set in the config', function() {
|
||||||
|
config.session_domain = 'example.com';
|
||||||
server.run(config, undefined, deps);
|
server.run(config, undefined, deps);
|
||||||
|
|
||||||
assert(deps.session.calledOnce);
|
assert(deps.session.calledOnce);
|
||||||
|
|
Loading…
Reference in New Issue