Skip to content

Commit

Permalink
add hashing of ldap_password, fix caching bug
Browse files Browse the repository at this point in the history
  • Loading branch information
hubermat committed Jun 30, 2018
1 parent e934608 commit bc5929e
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 14 deletions.
4 changes: 4 additions & 0 deletions ctldap.example.config
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ ldap_user=root
; The static password to be used for the ldap_user if it is NOT a CT account, or the account password of the chosen user otherwise
; If you did not use install.sh, choose a LONG SECURE RANDOM password from a password generator like KeePass!
ldap_password=XXXXXXXXXXXXXXXXXXXX
; If set to true, treat ldap_password as a bcrypt hash and compare against it
;ldap_password_bcrypt=true
; LDAP server port
ldap_port=1389
; The ctldap.sh service script will try to read this and setup an iptables NAT rule from iptables_port to ldap_port if it is set
Expand Down Expand Up @@ -40,12 +42,14 @@ cache_lifetime=10000
; Define the sites here. For each site please enter one section.
; The section title has to be in the format sites.<ldap_base_dn>, e.g. "sites.foobar",
; where foobar is the organisation, as in the instance foobar.church.tools .
; ldap_password_bcrypt is optional, if not specified, the default value will be taken from the config above
; dn_lower_case is optional, if not specified, the default value will be taken from the config above
; email_lower_case is optional, if not specified, the default value will be taken from the config above
; If ldap_base_dn is set above, the setting above are treated as an additional site.

;[sites.XXXXXXXXXXXXXXXXXXXX]
;ldap_password=XXXXXXXXXXXXXXXXXXXX
;ldap_password_bcrypt=true
;ct_uri=https://XXXXXXXXXXXXXXXXXXXX.church.tools/
;api_user=XXXXXXXXXXXXXXXXXXXX
;api_password=XXXXXXXXXXXXXXXXXXXX
Expand Down
45 changes: 31 additions & 14 deletions ctldap.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var ldapEsc = require('ldap-escape');
var extend = require('extend');
var Promise = require("bluebird");
var path = require('path');
var bcrypt = require('bcrypt');

var config = ini.parse(fs.readFileSync(path.resolve(__dirname, 'ctldap.config'), 'utf-8'));
if (config.debug) {
Expand All @@ -37,6 +38,7 @@ Object.keys(config.sites).map(function(sitename, index) {
site.cookieJar = rp.jar();
site.loginPromise = null;
site.adminDn = site.fnUserDn({cn: config.ldap_user});
site.CACHE = {};

if (site.dn_lower_case || ((site.dn_lower_case === undefined) && config.dn_lower_case)) {
site.compatTransform = function (s) {
Expand All @@ -56,6 +58,18 @@ Object.keys(config.sites).map(function(sitename, index) {
return s;
}
}
if (site.ldap_password_bcrypt || ((site.ldap_password_bcrypt === undefined) && config.ldap_password_bcrypt)) {
site.checkPassword = function (password, callback) {
var hash = site.ldap_password.replace(/^\$2y(.+)$/i, '$2a$1');
bcrypt.compare(password, hash, function(err, res) {
callback(res);
});
}
} else {
site.checkPassword = function (password, callback) {
callback(password === site.ldap_password);
}
}
if (site.ct_uri.slice(-1) !== "/") {
site.ct_uri += "/";
}
Expand Down Expand Up @@ -120,6 +134,7 @@ function apiLogin(site) {

/**
* Retrieves data from the PHP API via a POST call.
* @param {object} site - The current site
* @param {function} func - The function to call in the API class
* @param {object} [data] - The optional form data to pass along with the POST request
* @param {boolean} [triedLogin] - Is true if this is the second attempt after API login
Expand Down Expand Up @@ -156,7 +171,6 @@ function apiPost(site, func, data, triedLogin) {
});
}

var CACHE = {};
var USERS_KEY = "users", GROUPS_KEY = "groups";

/**
Expand All @@ -165,10 +179,10 @@ var USERS_KEY = "users", GROUPS_KEY = "groups";
* @param {number} maxAge - The maximum age of the cache entry, if older the data will be refreshed
* @param {function} factory - A function returning a Promise that resolves with the new cache entry or rejects
*/
function getCached(key, maxAge, factory) {
function getCached(site, key, maxAge, factory) {
return new Promise(function (resolve, reject) {
var time = new Date().getTime();
var co = CACHE[key] || { time: -1, entry: null };
var co = site.CACHE[key] || { time: -1, entry: null };
if (time - maxAge < co.time) {
resolve(co.entry);
} else {
Expand All @@ -177,7 +191,7 @@ function getCached(key, maxAge, factory) {
factory().then(function (result) {
co.entry = result;
co.time = new Date().getTime();
CACHE[key] = co;
site.CACHE[key] = co;
resolve(result);
}, reject);
}
Expand All @@ -193,7 +207,7 @@ function getCached(key, maxAge, factory) {
*/
function requestUsers (req, res, next) {
var site = req.site;
req.usersPromise = getCached(USERS_KEY, config.cache_lifetime, function () {
req.usersPromise = getCached(site, USERS_KEY, config.cache_lifetime, function () {
return apiPost(site, "getUsersData").then(function (results) {
var newCache = results.users.map(function (v) {
var cn = v.cmsuserid;
Expand Down Expand Up @@ -254,7 +268,7 @@ function requestUsers (req, res, next) {
*/
function requestGroups (req, res, next) {
var site = req.site;
req.groupsPromise = getCached(GROUPS_KEY, config.cache_lifetime, function () {
req.groupsPromise = getCached(site, GROUPS_KEY, config.cache_lifetime, function () {
return apiPost(site, "getGroupsData").then(function (results) {
var newCache = results.groups.map(function (v) {
var cn = v.bezeichnung;
Expand Down Expand Up @@ -386,15 +400,18 @@ function authenticate (req, res, next) {
}
// If ldap_password is undefined, try a default ChurchTools authentication with this user
if (site.ldap_password !== undefined) {
if (req.credentials === site.ldap_password) {
if (config.debug) {
console.log("Authentication success");
site.checkPassword(req.credentials, function (result) {
if (result) {
if (config.debug) {
console.log("Authentication success");
}
return next();
} else {
console.log("Invalid root password!");
return next(new ldap.InvalidCredentialsError());
}
return next();
} else {
console.log("Invalid root password!");
return next(new ldap.InvalidCredentialsError());
}
});
return;
}
} else if (config.debug) {
console.log('Bind user DN: ' + req.dn.toString());
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"version": "1.0.1",
"private": true,
"dependencies": {
"bcrypt": "^2.0.1",
"bluebird": "^3.5.0",
"extend": "^3.0.1",
"ini": "^1.1.0",
Expand Down

0 comments on commit bc5929e

Please sign in to comment.