Add tests for LDAP auth plugin
This commit is contained in:
parent
12ba10f688
commit
00e54e49ac
@ -464,7 +464,7 @@ module.exports = {
|
|||||||
// @type string
|
// @type string
|
||||||
// @default "uid"
|
// @default "uid"
|
||||||
//
|
//
|
||||||
filter: "(objectClass=inetOrgPerson)(memberOf=ou=accounts,dc=example,dc=com)",
|
filter: "(objectClass=person)(memberOf=ou=accounts,dc=example,dc=com)",
|
||||||
|
|
||||||
//
|
//
|
||||||
// LDAP search base (search only within this node)
|
// LDAP search base (search only within this node)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
const Helper = require("../../helper");
|
const Helper = require("../../helper");
|
||||||
const ldap = require("ldapjs");
|
const ldap = require("ldapjs");
|
||||||
|
|
||||||
function ldapAuthCommon(manager, client, user, bindDN, password, callback) {
|
function ldapAuthCommon(user, bindDN, password, callback) {
|
||||||
const config = Helper.config;
|
const config = Helper.config;
|
||||||
|
|
||||||
const ldapclient = ldap.createClient({
|
const ldapclient = ldap.createClient({
|
||||||
@ -17,15 +17,12 @@ function ldapAuthCommon(manager, client, user, bindDN, password, callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ldapclient.bind(bindDN, password, function(err) {
|
ldapclient.bind(bindDN, password, function(err) {
|
||||||
if (!err && !client) {
|
|
||||||
manager.addUser(user, null);
|
|
||||||
}
|
|
||||||
ldapclient.unbind();
|
ldapclient.unbind();
|
||||||
callback(!err);
|
callback(!err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function simpleLdapAuth(manager, client, user, password, callback) {
|
function simpleLdapAuth(user, password, callback) {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return callback(false);
|
return callback(false);
|
||||||
}
|
}
|
||||||
@ -37,13 +34,13 @@ function simpleLdapAuth(manager, client, user, password, callback) {
|
|||||||
|
|
||||||
log.info("Auth against LDAP ", config.ldap.url, " with provided bindDN ", bindDN);
|
log.info("Auth against LDAP ", config.ldap.url, " with provided bindDN ", bindDN);
|
||||||
|
|
||||||
ldapAuthCommon(manager, client, user, bindDN, password, callback);
|
ldapAuthCommon(user, bindDN, password, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LDAP auth using initial DN search (see config comment for ldap.searchDN)
|
* LDAP auth using initial DN search (see config comment for ldap.searchDN)
|
||||||
*/
|
*/
|
||||||
function advancedLdapAuth(manager, client, user, password, callback) {
|
function advancedLdapAuth(user, password, callback) {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return callback(false);
|
return callback(false);
|
||||||
}
|
}
|
||||||
@ -87,7 +84,7 @@ function advancedLdapAuth(manager, client, user, password, callback) {
|
|||||||
log.info("Auth against LDAP ", config.ldap.url, " with found bindDN ", bindDN);
|
log.info("Auth against LDAP ", config.ldap.url, " with found bindDN ", bindDN);
|
||||||
ldapclient.unbind();
|
ldapclient.unbind();
|
||||||
|
|
||||||
ldapAuthCommon(manager, client, user, bindDN, password, callback);
|
ldapAuthCommon(user, bindDN, password, callback);
|
||||||
});
|
});
|
||||||
res.on("error", function(err3) {
|
res.on("error", function(err3) {
|
||||||
log.error("LDAP error: ", err3);
|
log.error("LDAP error: ", err3);
|
||||||
@ -105,13 +102,24 @@ function advancedLdapAuth(manager, client, user, password, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ldapAuth(manager, client, user, password, callback) {
|
function ldapAuth(manager, client, user, password, callback) {
|
||||||
let auth = function() {};
|
// TODO: Enable the use of starttls() as an alternative to ldaps
|
||||||
|
|
||||||
|
// TODO: move this out of here and get rid of `manager` and `client` in
|
||||||
|
// auth plugin API
|
||||||
|
function callbackWrapper(valid) {
|
||||||
|
if (valid && !client) {
|
||||||
|
manager.addUser(user, null);
|
||||||
|
}
|
||||||
|
callback(valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
let auth;
|
||||||
if ("baseDN" in Helper.config.ldap) {
|
if ("baseDN" in Helper.config.ldap) {
|
||||||
auth = simpleLdapAuth;
|
auth = simpleLdapAuth;
|
||||||
} else {
|
} else {
|
||||||
auth = advancedLdapAuth;
|
auth = advancedLdapAuth;
|
||||||
}
|
}
|
||||||
return auth(manager, client, user, password, callback);
|
return auth(user, password, callbackWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isLdapEnabled() {
|
function isLdapEnabled() {
|
||||||
|
@ -37,8 +37,6 @@ function localAuth(manager, client, user, password, callback) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
auth: localAuth,
|
auth: localAuth,
|
||||||
isEnabled: function() {
|
isEnabled: () => true
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -436,7 +436,7 @@ function performAuthentication(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Perform password checking
|
// Perform password checking
|
||||||
let auth = function() {};
|
let auth;
|
||||||
if (ldapAuth.isEnabled()) {
|
if (ldapAuth.isEnabled()) {
|
||||||
auth = ldapAuth.auth;
|
auth = ldapAuth.auth;
|
||||||
} else if (localAuth.isEnabled()) {
|
} else if (localAuth.isEnabled()) {
|
||||||
|
151
test/plugins/auth/ldap.js
Normal file
151
test/plugins/auth/ldap.js
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const ldapAuth = require("../../../src/plugins/auth/ldap");
|
||||||
|
const Helper = require("../../../src/helper");
|
||||||
|
const ldap = require("ldapjs");
|
||||||
|
const expect = require("chai").expect;
|
||||||
|
|
||||||
|
const user = "johndoe";
|
||||||
|
const wrongUser = "eve";
|
||||||
|
const correctPassword = "loremipsum";
|
||||||
|
const wrongPassword = "dolorsitamet";
|
||||||
|
const baseDN = "ou=accounts,dc=example,dc=com";
|
||||||
|
const primaryKey = "uid";
|
||||||
|
const serverPort = 1389;
|
||||||
|
|
||||||
|
function normalizeDN(dn) {
|
||||||
|
return ldap.parseDN(dn).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startLdapServer(callback) {
|
||||||
|
const server = ldap.createServer();
|
||||||
|
|
||||||
|
const searchConf = Helper.config.ldap.searchDN;
|
||||||
|
const userDN = primaryKey + "=" + user + "," + baseDN;
|
||||||
|
|
||||||
|
// Two users are authorized: john doe and the root user in case of
|
||||||
|
// advanced auth (the user that does the search for john's actual
|
||||||
|
// bindDN)
|
||||||
|
const authorizedUsers = {};
|
||||||
|
authorizedUsers[normalizeDN(searchConf.rootDN)] = searchConf.rootPassword;
|
||||||
|
authorizedUsers[normalizeDN(userDN)] = correctPassword;
|
||||||
|
|
||||||
|
function authorize(req, res, next) {
|
||||||
|
const bindDN = req.connection.ldap.bindDN;
|
||||||
|
|
||||||
|
if (bindDN in authorizedUsers) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(new ldap.InsufficientAccessRightsError());
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(authorizedUsers).forEach(function(dn) {
|
||||||
|
server.bind(dn, function(req, res, next) {
|
||||||
|
const bindDN = req.dn.toString();
|
||||||
|
const password = req.credentials;
|
||||||
|
|
||||||
|
if (bindDN in authorizedUsers && authorizedUsers[bindDN] === password) {
|
||||||
|
req.connection.ldap.bindDN = req.dn;
|
||||||
|
res.end();
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(new ldap.InsufficientAccessRightsError());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.search(searchConf.base, authorize, function(req, res) {
|
||||||
|
const obj = {
|
||||||
|
dn: userDN,
|
||||||
|
attributes: {
|
||||||
|
objectclass: ["person", "top"],
|
||||||
|
cn: ["john doe"],
|
||||||
|
sn: ["johnny"],
|
||||||
|
uid: ["johndoe"],
|
||||||
|
memberof: [baseDN]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (req.filter.matches(obj.attributes)) {
|
||||||
|
// TODO: check req.scope if ldapjs does not
|
||||||
|
res.send(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(serverPort, function() {
|
||||||
|
console.log("LDAP server listening at %s", server.url);
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testLdapAuth() {
|
||||||
|
// Create mock manager and client. When client is true, manager should not
|
||||||
|
// be used. But ideally the auth plugin should not use any of those.
|
||||||
|
const manager = {};
|
||||||
|
const client = true;
|
||||||
|
|
||||||
|
it("should successfully authenticate with correct password", function(done) {
|
||||||
|
ldapAuth.auth(manager, client, user, correctPassword, function(valid) {
|
||||||
|
expect(valid).to.equal(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail to authenticate with incorrect password", function(done) {
|
||||||
|
ldapAuth.auth(manager, client, user, wrongPassword, function(valid) {
|
||||||
|
expect(valid).to.equal(false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail to authenticate with incorrect username", function(done) {
|
||||||
|
ldapAuth.auth(manager, client, wrongUser, correctPassword, function(valid) {
|
||||||
|
expect(valid).to.equal(false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("LDAP authentication plugin", function() {
|
||||||
|
before(function(done) {
|
||||||
|
this.server = startLdapServer(done);
|
||||||
|
});
|
||||||
|
after(function(done) {
|
||||||
|
this.server.close();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(function(done) {
|
||||||
|
Helper.config.public = false;
|
||||||
|
Helper.config.ldap.enable = true;
|
||||||
|
Helper.config.ldap.url = "ldap://localhost:" + String(serverPort);
|
||||||
|
Helper.config.ldap.primaryKey = primaryKey;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("LDAP authentication availability", function() {
|
||||||
|
it("checks that the configuration is correctly tied to isEnabled()", function(done) {
|
||||||
|
Helper.config.ldap.enable = true;
|
||||||
|
expect(ldapAuth.isEnabled()).to.equal(true);
|
||||||
|
Helper.config.ldap.enable = false;
|
||||||
|
expect(ldapAuth.isEnabled()).to.equal(false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Simple LDAP authentication (predefined DN pattern)", function() {
|
||||||
|
Helper.config.ldap.baseDN = baseDN;
|
||||||
|
testLdapAuth();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Advanced LDAP authentication (DN found by a prior search query)", function() {
|
||||||
|
delete Helper.config.ldap.baseDN;
|
||||||
|
testLdapAuth();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user