2017-08-30 09:49:21 +00:00
|
|
|
"use strict";
|
|
|
|
|
2018-06-15 20:31:06 +00:00
|
|
|
const log = require("../../../src/log");
|
2017-08-30 09:49:21 +00:00
|
|
|
const ldapAuth = require("../../../src/plugins/auth/ldap");
|
2022-05-01 19:12:39 +00:00
|
|
|
const Config = require("../../../src/config");
|
2019-09-04 10:49:48 +00:00
|
|
|
const ldap = require("ldapjs");
|
2017-08-30 09:49:21 +00:00
|
|
|
const expect = require("chai").expect;
|
2019-01-05 23:08:10 +00:00
|
|
|
const stub = require("sinon").stub;
|
|
|
|
const TestUtil = require("../../util");
|
2017-08-30 09:49:21 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2022-05-01 19:12:39 +00:00
|
|
|
const searchConf = Config.values.ldap.searchDN;
|
2017-08-30 09:49:21 +00:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
Object.keys(authorizedUsers).forEach(function (dn) {
|
|
|
|
server.bind(dn, function (req, res, next) {
|
2017-08-30 09:49:21 +00:00
|
|
|
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());
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
server.search(searchConf.base, authorize, function (req, res) {
|
2017-08-30 09:49:21 +00:00
|
|
|
const obj = {
|
|
|
|
dn: userDN,
|
|
|
|
attributes: {
|
|
|
|
objectclass: ["person", "top"],
|
|
|
|
cn: ["john doe"],
|
|
|
|
sn: ["johnny"],
|
|
|
|
uid: ["johndoe"],
|
2017-11-15 06:35:15 +00:00
|
|
|
memberof: [baseDN],
|
|
|
|
},
|
2017-08-30 09:49:21 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if (req.filter.matches(obj.attributes)) {
|
|
|
|
// TODO: check req.scope if ldapjs does not
|
|
|
|
res.send(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
res.end();
|
|
|
|
});
|
|
|
|
|
2017-09-01 09:29:52 +00:00
|
|
|
server.listen(serverPort, callback);
|
2017-09-01 09:44:53 +00:00
|
|
|
|
2017-08-30 09:49:21 +00:00
|
|
|
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;
|
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
it("should successfully authenticate with correct password", function (done) {
|
|
|
|
ldapAuth.auth(manager, client, user, correctPassword, function (valid) {
|
2017-08-30 09:49:21 +00:00
|
|
|
expect(valid).to.equal(true);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
it("should fail to authenticate with incorrect password", function (done) {
|
2019-01-05 23:08:10 +00:00
|
|
|
let error = "";
|
2019-07-17 09:33:59 +00:00
|
|
|
stub(log, "error").callsFake(TestUtil.sanitizeLog((str) => (error += str)));
|
2019-01-05 23:08:10 +00:00
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
ldapAuth.auth(manager, client, user, wrongPassword, function (valid) {
|
2017-08-30 09:49:21 +00:00
|
|
|
expect(valid).to.equal(false);
|
2019-07-17 09:33:59 +00:00
|
|
|
expect(error).to.equal(
|
|
|
|
"LDAP bind failed: InsufficientAccessRightsError: InsufficientAccessRightsError\n"
|
|
|
|
);
|
2019-01-05 23:08:10 +00:00
|
|
|
log.error.restore();
|
2017-08-30 09:49:21 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
it("should fail to authenticate with incorrect username", function (done) {
|
2019-01-05 23:08:10 +00:00
|
|
|
let warning = "";
|
2019-07-17 09:33:59 +00:00
|
|
|
stub(log, "warn").callsFake(TestUtil.sanitizeLog((str) => (warning += str)));
|
2019-01-05 23:08:10 +00:00
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
ldapAuth.auth(manager, client, wrongUser, correctPassword, function (valid) {
|
2017-08-30 09:49:21 +00:00
|
|
|
expect(valid).to.equal(false);
|
2019-01-05 23:08:10 +00:00
|
|
|
expect(warning).to.equal("LDAP Search did not find anything for: eve (0)\n");
|
|
|
|
log.warn.restore();
|
2017-08-30 09:49:21 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
describe("LDAP authentication plugin", function () {
|
2019-11-27 18:25:29 +00:00
|
|
|
// Increase timeout due to unpredictable I/O on CI services
|
2020-02-09 12:21:45 +00:00
|
|
|
this.timeout(TestUtil.isRunningOnCI() ? 25000 : 5000);
|
|
|
|
this.slow(300);
|
2017-11-27 23:47:19 +00:00
|
|
|
|
2017-12-09 23:56:05 +00:00
|
|
|
let server;
|
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
before(function (done) {
|
2020-01-18 23:14:52 +00:00
|
|
|
stub(log, "info");
|
2017-12-09 23:56:05 +00:00
|
|
|
|
|
|
|
server = startLdapServer(done);
|
2017-08-30 09:49:21 +00:00
|
|
|
});
|
2017-10-06 09:53:08 +00:00
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
after(function () {
|
2017-12-09 23:56:05 +00:00
|
|
|
server.close();
|
|
|
|
|
2020-01-18 23:14:52 +00:00
|
|
|
log.info.restore();
|
2017-08-30 09:49:21 +00:00
|
|
|
});
|
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
beforeEach(function () {
|
2022-05-01 19:12:39 +00:00
|
|
|
Config.values.public = false;
|
|
|
|
Config.values.ldap.enable = true;
|
|
|
|
Config.values.ldap.url = "ldap://localhost:" + String(serverPort);
|
|
|
|
Config.values.ldap.primaryKey = primaryKey;
|
2017-08-30 09:49:21 +00:00
|
|
|
});
|
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
afterEach(function () {
|
2022-05-01 19:12:39 +00:00
|
|
|
Config.values.public = true;
|
|
|
|
Config.values.ldap.enable = false;
|
2019-10-31 09:01:44 +00:00
|
|
|
});
|
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
describe("LDAP authentication availability", function () {
|
|
|
|
it("checks that the configuration is correctly tied to isEnabled()", function () {
|
2022-05-01 19:12:39 +00:00
|
|
|
Config.values.ldap.enable = true;
|
2017-08-30 09:49:21 +00:00
|
|
|
expect(ldapAuth.isEnabled()).to.equal(true);
|
2017-10-06 09:53:08 +00:00
|
|
|
|
2022-05-01 19:12:39 +00:00
|
|
|
Config.values.ldap.enable = false;
|
2017-08-30 09:49:21 +00:00
|
|
|
expect(ldapAuth.isEnabled()).to.equal(false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
describe("Simple LDAP authentication (predefined DN pattern)", function () {
|
2022-05-01 19:12:39 +00:00
|
|
|
Config.values.ldap.baseDN = baseDN;
|
2017-08-30 09:49:21 +00:00
|
|
|
testLdapAuth();
|
|
|
|
});
|
|
|
|
|
2020-03-21 20:55:36 +00:00
|
|
|
describe("Advanced LDAP authentication (DN found by a prior search query)", function () {
|
2022-05-01 19:12:39 +00:00
|
|
|
delete Config.values.ldap.baseDN;
|
2017-08-30 09:49:21 +00:00
|
|
|
testLdapAuth();
|
|
|
|
});
|
|
|
|
});
|