From 987474cfc1ef5e6d4138ad65baf57e9097cb7549 Mon Sep 17 00:00:00 2001 From: Johan Lindskogen Date: Fri, 29 Jul 2016 21:20:38 -0400 Subject: [PATCH] implementing LDAP support --- client/index.html | 2 +- defaults/config.js | 38 +++++++++++++++++ package.json | 3 +- src/clientManager.js | 4 +- src/command-line/start.js | 2 +- src/server.js | 86 +++++++++++++++++++++++++++++++-------- 6 files changed, 112 insertions(+), 23 deletions(-) diff --git a/client/index.html b/client/index.html index edf9ee33..f59043b9 100644 --- a/client/index.html +++ b/client/index.html @@ -292,7 +292,7 @@ - <% if (!public) { %> + <% if (!public && !ldap.enable) { %>
diff --git a/defaults/config.js b/defaults/config.js index 241d0e94..254d2a4a 100644 --- a/defaults/config.js +++ b/defaults/config.js @@ -321,4 +321,42 @@ module.exports = { // @default null // oidentd: null, + + // + // LDAP authentication settings (only available if public=false) + // @type object + // @default {} + // + ldap: { + // + // Enable LDAP user authentication + // + // @type boolean + // @default false + // + enable: false, + + // + // LDAP server URL + // + // @type string + // + url: "ldaps://example.com", + + // + // LDAP base dn + // + // @type string + // + baseDN: "ou=accounts,dc=example,dc=com", + + // + // LDAP primary key + // + // @type string + // @default "uid" + // + primaryKey: "uid" + } + }; diff --git a/package.json b/package.json index 7f6432e0..ea48cb7a 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,8 @@ "request": "2.72.0", "semver": "5.1.0", "socket.io": "1.4.5", - "spdy": "3.3.2" + "spdy": "3.3.2", + "ldapjs": "1.0.0" }, "devDependencies": { "chai": "3.5.0", diff --git a/src/clientManager.js b/src/clientManager.js index a9b61bfd..dadaa774 100644 --- a/src/clientManager.js +++ b/src/clientManager.js @@ -14,10 +14,10 @@ function ClientManager() { } } -ClientManager.prototype.findClient = function(name) { +ClientManager.prototype.findClient = function(name, token) { for (var i in this.clients) { var client = this.clients[i]; - if (client.name === name) { + if (client.name === name || (token && token === client.token)) { return client; } } diff --git a/src/command-line/start.js b/src/command-line/start.js index e6c37ef1..4386a2fb 100644 --- a/src/command-line/start.js +++ b/src/command-line/start.js @@ -21,7 +21,7 @@ program mode = false; } - if (!mode && !users.length) { + if (!mode && !users.length && !Helper.config.ldap.enable) { log.warn("No users found!"); log.info("Create a new user with 'lounge add '."); diff --git a/src/server.js b/src/server.js index c542c020..3199966d 100644 --- a/src/server.js +++ b/src/server.js @@ -10,8 +10,11 @@ var fs = require("fs"); var io = require("socket.io"); var dns = require("dns"); var Helper = require("./helper"); +var ldap = require("ldapjs"); var manager = null; +var ldapclient = null; +var authFunction = localAuth; module.exports = function() { manager = new ClientManager(); @@ -24,6 +27,10 @@ module.exports = function() { var config = Helper.config; var server = null; + if (config.public && (config.ldap || {}).enable) { + throw "Server is public and set to use LDAP. Please disable public if trying to use LDAP authentication."; + } + if (!config.https.enable) { server = require("http"); server = server.createServer(app).listen(config.port, config.host); @@ -43,6 +50,13 @@ module.exports = function() { require("./identd").start(config.identd.port); } + if ((config.ldap || {}).enable) { + ldapclient = ldap.createClient({ + url: config.ldap.url + }); + authFunction = ldapAuth; + } + var sockets = io(server, { transports: config.transports }); @@ -138,7 +152,7 @@ function init(socket, client) { client.connect(data); } ); - if (!Helper.config.public) { + if (!Helper.config.public && !Helper.config.ldap.enable) { socket.on( "change-password", function(data) { @@ -223,10 +237,39 @@ function reverseDnsLookup(socket, client) { }); } +function localAuth(client, user, password, callback) { + var result = false; + try { + result = bcrypt.compareSync(password || "", client.config.password); + } catch (error) { + if (error === "Not a valid BCrypt hash.") { + console.error("User (" + user + ") with no local password set tried signed in. (Probably a ldap user)"); + } + result = false; + } finally { + callback(result); + } +} + +function ldapAuth(client, user, password, callback) { + var userDN = user.replace(/([,\\\/#+<>;"= ])/g, "\\$1"); + var bindDN = Helper.config.ldap.primaryKey + "=" + userDN + "," + Helper.config.ldap.baseDN; + + ldapclient.bind(bindDN, password, function(err) { + if (!err && !client) { + if (!manager.addUser(user, null)) { + console.log("Unable to create new user", user); + } + } + callback(!err); + }); +} + function auth(data) { var socket = this; + var client; if (Helper.config.public) { - var client = new Client(manager); + client = new Client(manager); manager.clients.push(client); socket.on("disconnect", function() { manager.clients = _.without(manager.clients, client); @@ -238,28 +281,35 @@ function auth(data) { init(socket, client); } } else { - var success = false; - _.each(manager.clients, function(client) { - if (data.token) { - if (data.token === client.config.token) { - success = true; - } - } else if (client.config.user === data.user) { - if (bcrypt.compareSync(data.password || "", client.config.password)) { - success = true; - } - } + client = manager.findClient(data.user, data.token); + var signedIn = data.token && client && client.token === data.token; + var token; + + if (data.remember || data.token) { + token = client.token; + } + + var authCallback = function(success) { if (success) { + if (!client) { + // LDAP just created a user + manager.loadUser(data.user); + client = manager.findClient(data.user); + } if (Helper.config.webirc !== null && !client.config["ip"]) { reverseDnsLookup(socket, client); } else { - init(socket, client); + init(socket, client, token); } - return false; + } else { + socket.emit("auth", {success: success}); } - }); - if (!success) { - socket.emit("auth", {success: success}); + }; + + if (signedIn) { + authCallback(true); + } else { + authFunction(client, data.user, data.password, authCallback); } } }