Merge pull request #1478 from eliemichel/pr-proper-ldap--rebased
Implement a proper LDAP authentication process
This commit is contained in:
commit
099fb058c6
@ -365,6 +365,30 @@ module.exports = {
|
|||||||
// @type object
|
// @type object
|
||||||
// @default {}
|
// @default {}
|
||||||
//
|
//
|
||||||
|
// The authentication process works as follows:
|
||||||
|
//
|
||||||
|
// 1. Lounge connects to the LDAP server with its system credentials
|
||||||
|
// 2. It performs a LDAP search query to find the full DN associated to the
|
||||||
|
// user requesting to log in.
|
||||||
|
// 3. Lounge tries to connect a second time, but this time using the user's
|
||||||
|
// DN and password. Auth is validated iff this connection is successful.
|
||||||
|
//
|
||||||
|
// The search query takes a couple of parameters in `searchDN`:
|
||||||
|
// - a base DN `searchDN/base`. Only children nodes of this DN will be likely
|
||||||
|
// to be returned;
|
||||||
|
// - a search scope `searchDN/scope` (see LDAP documentation);
|
||||||
|
// - the query itself, build as (&(<primaryKey>=<username>) <filter>)
|
||||||
|
// where <username> is the user name provided in the log in request,
|
||||||
|
// <primaryKey> is provided by the config and <fitler> is a filtering complement
|
||||||
|
// also given in the config, to filter for instance only for nodes of type
|
||||||
|
// inetOrgPerson, or whatever LDAP search allows.
|
||||||
|
//
|
||||||
|
// Alternatively, you can specify the `bindDN` parameter. This will make the lounge
|
||||||
|
// ignore searchDN options and assume that the user DN is always:
|
||||||
|
// <bindDN>,<primaryKey>=<username>
|
||||||
|
// where <username> is the user name provided in the log in request, and <bindDN>
|
||||||
|
// and <primaryKey> are provided by the config.
|
||||||
|
//
|
||||||
ldap: {
|
ldap: {
|
||||||
//
|
//
|
||||||
// Enable LDAP user authentication
|
// Enable LDAP user authentication
|
||||||
@ -382,11 +406,25 @@ module.exports = {
|
|||||||
url: "ldaps://example.com",
|
url: "ldaps://example.com",
|
||||||
|
|
||||||
//
|
//
|
||||||
// LDAP base dn
|
// LDAP connection tls options (only used if scheme is ldaps://)
|
||||||
|
//
|
||||||
|
// @type object (see nodejs' tls.connect() options)
|
||||||
|
// @default {}
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// You can use this option in order to force the use of IPv6:
|
||||||
|
// {
|
||||||
|
// host: 'my::ip::v6',
|
||||||
|
// servername: 'example.com'
|
||||||
|
// }
|
||||||
|
tlsOptions: {},
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAP base dn, alternative to searchDN
|
||||||
//
|
//
|
||||||
// @type string
|
// @type string
|
||||||
//
|
//
|
||||||
baseDN: "ou=accounts,dc=example,dc=com",
|
// baseDN: "ou=accounts,dc=example,dc=com",
|
||||||
|
|
||||||
//
|
//
|
||||||
// LDAP primary key
|
// LDAP primary key
|
||||||
@ -394,7 +432,58 @@ module.exports = {
|
|||||||
// @type string
|
// @type string
|
||||||
// @default "uid"
|
// @default "uid"
|
||||||
//
|
//
|
||||||
primaryKey: "uid"
|
primaryKey: "uid",
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAP search dn settings. This defines the procedure by which the
|
||||||
|
// lounge first look for user DN before authenticating her.
|
||||||
|
// Ignored if baseDN is specified
|
||||||
|
//
|
||||||
|
// @type object
|
||||||
|
//
|
||||||
|
searchDN: {
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAP searching bind DN
|
||||||
|
// This bind DN is used to query the server for the DN of the user.
|
||||||
|
// This is supposed to be a system user that has access in read only to
|
||||||
|
// the DNs of the people that are allowed to log in.
|
||||||
|
//
|
||||||
|
// @type string
|
||||||
|
//
|
||||||
|
rootDN: "cn=thelounge,ou=system-users,dc=example,dc=com",
|
||||||
|
|
||||||
|
//
|
||||||
|
// Password of the lounge LDAP system user
|
||||||
|
//
|
||||||
|
// @type string
|
||||||
|
//
|
||||||
|
rootPassword: "1234",
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAP filter
|
||||||
|
//
|
||||||
|
// @type string
|
||||||
|
// @default "uid"
|
||||||
|
//
|
||||||
|
filter: "(objectClass=person)(memberOf=ou=accounts,dc=example,dc=com)",
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAP search base (search only within this node)
|
||||||
|
//
|
||||||
|
// @type string
|
||||||
|
//
|
||||||
|
base: "dc=example,dc=com",
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAP search scope
|
||||||
|
//
|
||||||
|
// @type string
|
||||||
|
// @default "sub"
|
||||||
|
//
|
||||||
|
scope: "sub"
|
||||||
|
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Extra debugging
|
// Extra debugging
|
||||||
|
132
src/plugins/auth/ldap.js
Normal file
132
src/plugins/auth/ldap.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Helper = require("../../helper");
|
||||||
|
const ldap = require("ldapjs");
|
||||||
|
|
||||||
|
function ldapAuthCommon(user, bindDN, password, callback) {
|
||||||
|
const config = Helper.config;
|
||||||
|
|
||||||
|
const ldapclient = ldap.createClient({
|
||||||
|
url: config.ldap.url,
|
||||||
|
tlsOptions: config.ldap.tlsOptions
|
||||||
|
});
|
||||||
|
|
||||||
|
ldapclient.on("error", function(err) {
|
||||||
|
log.error(`Unable to connect to LDAP server: ${err}`);
|
||||||
|
callback(!err);
|
||||||
|
});
|
||||||
|
|
||||||
|
ldapclient.bind(bindDN, password, function(err) {
|
||||||
|
ldapclient.unbind();
|
||||||
|
callback(!err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function simpleLdapAuth(user, password, callback) {
|
||||||
|
if (!user) {
|
||||||
|
return callback(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = Helper.config;
|
||||||
|
|
||||||
|
const userDN = user.replace(/([,\\/#+<>;"= ])/g, "\\$1");
|
||||||
|
const bindDN = `${config.ldap.primaryKey}=${userDN},${config.ldap.baseDN}`;
|
||||||
|
|
||||||
|
log.info(`Auth against LDAP ${config.ldap.url} with provided bindDN ${bindDN}`);
|
||||||
|
|
||||||
|
ldapAuthCommon(user, bindDN, password, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAP auth using initial DN search (see config comment for ldap.searchDN)
|
||||||
|
*/
|
||||||
|
function advancedLdapAuth(user, password, callback) {
|
||||||
|
if (!user) {
|
||||||
|
return callback(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = Helper.config;
|
||||||
|
const userDN = user.replace(/([,\\/#+<>;"= ])/g, "\\$1");
|
||||||
|
|
||||||
|
const ldapclient = ldap.createClient({
|
||||||
|
url: config.ldap.url,
|
||||||
|
tlsOptions: config.ldap.tlsOptions
|
||||||
|
});
|
||||||
|
|
||||||
|
const base = config.ldap.searchDN.base;
|
||||||
|
const searchOptions = {
|
||||||
|
scope: config.ldap.searchDN.scope,
|
||||||
|
filter: `(&(${config.ldap.primaryKey}=${userDN})${config.ldap.searchDN.filter})`,
|
||||||
|
attributes: ["dn"]
|
||||||
|
};
|
||||||
|
|
||||||
|
ldapclient.on("error", function(err) {
|
||||||
|
log.error(`Unable to connect to LDAP server: ${err}`);
|
||||||
|
callback(!err);
|
||||||
|
});
|
||||||
|
|
||||||
|
ldapclient.bind(config.ldap.searchDN.rootDN, config.ldap.searchDN.rootPassword, function(err) {
|
||||||
|
if (err) {
|
||||||
|
log.error("Invalid LDAP root credentials");
|
||||||
|
ldapclient.unbind();
|
||||||
|
callback(false);
|
||||||
|
} else {
|
||||||
|
ldapclient.search(base, searchOptions, function(err2, res) {
|
||||||
|
if (err2) {
|
||||||
|
log.warning(`User not found: ${userDN}`);
|
||||||
|
ldapclient.unbind();
|
||||||
|
callback(false);
|
||||||
|
} else {
|
||||||
|
let found = false;
|
||||||
|
res.on("searchEntry", function(entry) {
|
||||||
|
found = true;
|
||||||
|
const bindDN = entry.objectName;
|
||||||
|
log.info(`Auth against LDAP ${config.ldap.url} with found bindDN ${bindDN}`);
|
||||||
|
ldapclient.unbind();
|
||||||
|
|
||||||
|
ldapAuthCommon(user, bindDN, password, callback);
|
||||||
|
});
|
||||||
|
res.on("error", function(err3) {
|
||||||
|
log.error(`LDAP error: ${err3}`);
|
||||||
|
callback(false);
|
||||||
|
});
|
||||||
|
res.on("end", function() {
|
||||||
|
if (!found) {
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function ldapAuth(manager, client, user, password, callback) {
|
||||||
|
// 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) {
|
||||||
|
auth = simpleLdapAuth;
|
||||||
|
} else {
|
||||||
|
auth = advancedLdapAuth;
|
||||||
|
}
|
||||||
|
return auth(user, password, callbackWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLdapEnabled() {
|
||||||
|
return !Helper.config.public && Helper.config.ldap.enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
auth: ldapAuth,
|
||||||
|
isEnabled: isLdapEnabled
|
||||||
|
};
|
42
src/plugins/auth/local.js
Normal file
42
src/plugins/auth/local.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Helper = require("../../helper");
|
||||||
|
const colors = require("colors/safe");
|
||||||
|
|
||||||
|
function localAuth(manager, client, user, password, callback) {
|
||||||
|
// If no user is found, or if the client has not provided a password,
|
||||||
|
// fail the authentication straight away
|
||||||
|
if (!client || !password) {
|
||||||
|
return callback(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this user has no password set, fail the authentication
|
||||||
|
if (!client.config.password) {
|
||||||
|
log.error(`User ${colors.bold(user)} with no local password set tried to sign in. (Probably a LDAP user)`);
|
||||||
|
return callback(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Helper.password
|
||||||
|
.compare(password, client.config.password)
|
||||||
|
.then((matching) => {
|
||||||
|
if (matching && Helper.password.requiresUpdate(client.config.password)) {
|
||||||
|
const hash = Helper.password.hash(password);
|
||||||
|
|
||||||
|
client.setPassword(hash, (success) => {
|
||||||
|
if (success) {
|
||||||
|
log.info(`User ${colors.bold(client.name)} logged in and their hashed password has been updated to match new security requirements`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(matching);
|
||||||
|
}).catch((error) => {
|
||||||
|
log.error(`Error while checking users password. Error: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
auth: localAuth,
|
||||||
|
isEnabled: () => true
|
||||||
|
};
|
||||||
|
|
@ -11,11 +11,17 @@ var path = require("path");
|
|||||||
var io = require("socket.io");
|
var io = require("socket.io");
|
||||||
var dns = require("dns");
|
var dns = require("dns");
|
||||||
var Helper = require("./helper");
|
var Helper = require("./helper");
|
||||||
var ldap = require("ldapjs");
|
|
||||||
var colors = require("colors/safe");
|
var colors = require("colors/safe");
|
||||||
const net = require("net");
|
const net = require("net");
|
||||||
const Identification = require("./identification");
|
const Identification = require("./identification");
|
||||||
|
|
||||||
|
// The order defined the priority: the first available plugin is used
|
||||||
|
// ALways keep local auth in the end, which should always be enabled.
|
||||||
|
const authPlugins = [
|
||||||
|
require("./plugins/auth/ldap"),
|
||||||
|
require("./plugins/auth/local"),
|
||||||
|
];
|
||||||
|
|
||||||
var manager = null;
|
var manager = null;
|
||||||
|
|
||||||
module.exports = function() {
|
module.exports = function() {
|
||||||
@ -411,62 +417,6 @@ function initializeClient(socket, client, token) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function localAuth(client, user, password, callback) {
|
|
||||||
// If no user is found, or if the client has not provided a password,
|
|
||||||
// fail the authentication straight away
|
|
||||||
if (!client || !password) {
|
|
||||||
return callback(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this user has no password set, fail the authentication
|
|
||||||
if (!client.config.password) {
|
|
||||||
log.error(`User ${colors.bold(user)} with no local password set tried to sign in. (Probably a LDAP user)`);
|
|
||||||
return callback(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Helper.password
|
|
||||||
.compare(password, client.config.password)
|
|
||||||
.then((matching) => {
|
|
||||||
if (matching && Helper.password.requiresUpdate(client.config.password)) {
|
|
||||||
const hash = Helper.password.hash(password);
|
|
||||||
|
|
||||||
client.setPassword(hash, (success) => {
|
|
||||||
if (success) {
|
|
||||||
log.info(`User ${colors.bold(client.name)} logged in and their hashed password has been updated to match new security requirements`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(matching);
|
|
||||||
}).catch((error) => {
|
|
||||||
log.error(`Error while checking users password. Error: ${error}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function ldapAuth(client, user, password, callback) {
|
|
||||||
var userDN = user.replace(/([,\\/#+<>;"= ])/g, "\\$1");
|
|
||||||
var bindDN = Helper.config.ldap.primaryKey + "=" + userDN + "," + Helper.config.ldap.baseDN;
|
|
||||||
|
|
||||||
var ldapclient = ldap.createClient({
|
|
||||||
url: Helper.config.ldap.url
|
|
||||||
});
|
|
||||||
|
|
||||||
ldapclient.on("error", function(err) {
|
|
||||||
log.error("Unable to connect to LDAP server", err);
|
|
||||||
callback(!err);
|
|
||||||
});
|
|
||||||
|
|
||||||
ldapclient.bind(bindDN, password, function(err) {
|
|
||||||
if (!err && !client) {
|
|
||||||
if (!manager.addUser(user, null)) {
|
|
||||||
log.error("Unable to create new user", user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ldapclient.unbind();
|
|
||||||
callback(!err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function performAuthentication(data) {
|
function performAuthentication(data) {
|
||||||
const socket = this;
|
const socket = this;
|
||||||
let client;
|
let client;
|
||||||
@ -530,11 +480,16 @@ function performAuthentication(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Perform password checking
|
// Perform password checking
|
||||||
if (!Helper.config.public && Helper.config.ldap.enable) {
|
let auth = () => {
|
||||||
ldapAuth(client, data.user, data.password, authCallback);
|
log.error("None of the auth plugins is enabled");
|
||||||
} else {
|
};
|
||||||
localAuth(client, data.user, data.password, authCallback);
|
for (let i = 0; i < authPlugins.length; ++i) {
|
||||||
|
if (authPlugins[i].isEnabled()) {
|
||||||
|
auth = authPlugins[i].auth;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
auth(manager, client, data.user, data.password, authCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function reverseDnsLookup(ip, callback) {
|
function reverseDnsLookup(ip, callback) {
|
||||||
|
148
test/plugins/auth/ldap.js
Normal file
148
test/plugins/auth/ldap.js
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
"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, 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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -37,6 +37,11 @@ describe("Server", () => {
|
|||||||
describe("WebSockets", () => {
|
describe("WebSockets", () => {
|
||||||
let client;
|
let client;
|
||||||
|
|
||||||
|
before((done) => {
|
||||||
|
Helper.config.public = true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
client = io(webURL, {
|
client = io(webURL, {
|
||||||
path: "/socket.io/",
|
path: "/socket.io/",
|
||||||
|
Loading…
Reference in New Issue
Block a user