Add external ldap tests
This commit is contained in:
511
src/test/externalldap-test.js
Normal file
511
src/test/externalldap-test.js
Normal file
@@ -0,0 +1,511 @@
|
||||
/* global it:false */
|
||||
/* global describe:false */
|
||||
/* global before:false */
|
||||
/* global after:false */
|
||||
|
||||
'use strict';
|
||||
|
||||
var async = require('async'),
|
||||
BoxError = require('../boxerror.js'),
|
||||
database = require('../database.js'),
|
||||
constants = require('../constants.js'),
|
||||
expect = require('expect.js'),
|
||||
externalldap = require('../externalldap.js'),
|
||||
groupdb = require('../groupdb.js'),
|
||||
groups = require('../groups.js'),
|
||||
domains = require('../domains.js'),
|
||||
ldap = require('ldapjs'),
|
||||
mail = require('../mail.js'),
|
||||
mailboxdb = require('../mailboxdb.js'),
|
||||
maildb = require('../maildb.js'),
|
||||
mailer = require('../mailer.js'),
|
||||
server = require('../server.js'),
|
||||
settings = require('../settings.js'),
|
||||
superagent = require('superagent'),
|
||||
userdb = require('../userdb.js'),
|
||||
users = require('../users.js'),
|
||||
_ = require('underscore');
|
||||
|
||||
var USERNAME = 'noBody';
|
||||
var USERNAME_NEW = 'noBodyNew';
|
||||
var EMAIL = 'else@no.body';
|
||||
var EMAIL_NEW = 'noBodyNew@no.body';
|
||||
var PASSWORD = 'sTrOnG#$34134';
|
||||
var NEW_PASSWORD = 'oTHER@#$235';
|
||||
var DISPLAY_NAME = 'Nobody cares';
|
||||
var DISPLAY_NAME_NEW = 'Somone cares';
|
||||
var userObject = null;
|
||||
var NON_ADMIN_GROUP = 'members';
|
||||
var AUDIT_SOURCE = { ip: '1.2.3.4', userId: 'someuserid' };
|
||||
|
||||
let gLdapServer;
|
||||
|
||||
const SERVER_URL = `http://localhost:${constants.PORT}`;
|
||||
|
||||
const DOMAIN_0 = {
|
||||
domain: 'example.com',
|
||||
zoneName: 'example.com',
|
||||
provider: 'manual',
|
||||
config: {},
|
||||
fallbackCertificate: null,
|
||||
tlsConfig: { provider: 'fallback' }
|
||||
};
|
||||
|
||||
const LDAP_SHARED_PASSWORD = 'validpassword';
|
||||
const LDAP_PORT = 4321;
|
||||
const LDAP_BASE_DN = 'ou=Users,dc=cloudron,dc=io';
|
||||
const LDAP_CONFIG = {
|
||||
provider: 'testserver',
|
||||
url: `ldap://localhost:${LDAP_PORT}`,
|
||||
usernameField: 'customusernameprop',
|
||||
baseDn: LDAP_BASE_DN,
|
||||
filter: '(objectClass=inetOrgPerson)',
|
||||
autoCreate: false
|
||||
};
|
||||
|
||||
function cleanupUsers(done) {
|
||||
mailer._mailQueue = [];
|
||||
|
||||
async.series([
|
||||
groupdb._clear,
|
||||
userdb._clear,
|
||||
mailboxdb._clear,
|
||||
], done);
|
||||
}
|
||||
|
||||
function createOwner(done) {
|
||||
users.createOwner(USERNAME, PASSWORD, EMAIL, DISPLAY_NAME, AUDIT_SOURCE, function (error, result) {
|
||||
expect(error).to.not.be.ok();
|
||||
expect(result).to.be.ok();
|
||||
|
||||
userObject = result;
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
// helper function to deal with pagination taken from ldap.js
|
||||
function finalSend(results, req, res, next) {
|
||||
var min = 0;
|
||||
var max = results.length;
|
||||
var cookie = null;
|
||||
var pageSize = 0;
|
||||
|
||||
// check if this is a paging request, if so get the cookie for session info
|
||||
req.controls.forEach(function (control) {
|
||||
if (control.type === ldap.PagedResultsControl.OID) {
|
||||
pageSize = control.value.size;
|
||||
cookie = control.value.cookie;
|
||||
}
|
||||
});
|
||||
|
||||
function sendPagedResults(start, end) {
|
||||
start = (start < min) ? min : start;
|
||||
end = (end > max || end < min) ? max : end;
|
||||
var i;
|
||||
|
||||
for (i = start; i < end; i++) {
|
||||
res.send(results[i]);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
if (cookie && Buffer.isBuffer(cookie)) {
|
||||
// we have pagination
|
||||
var first = min;
|
||||
if (cookie.length !== 0) {
|
||||
first = parseInt(cookie.toString(), 10);
|
||||
}
|
||||
var last = sendPagedResults(first, first + pageSize);
|
||||
|
||||
var resultCookie;
|
||||
if (last < max) {
|
||||
resultCookie = Buffer.from(last.toString());
|
||||
} else {
|
||||
resultCookie = Buffer.from('');
|
||||
}
|
||||
|
||||
res.controls.push(new ldap.PagedResultsControl({
|
||||
value: {
|
||||
size: pageSize, // correctness not required here
|
||||
cookie: resultCookie
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
// no pagination simply send all
|
||||
results.forEach(function (result) {
|
||||
res.send(result);
|
||||
});
|
||||
}
|
||||
|
||||
// all done
|
||||
res.end();
|
||||
next();
|
||||
}
|
||||
|
||||
let gLdapUsers = [];
|
||||
|
||||
function startLdapServer(callback) {
|
||||
gLdapServer = ldap.createServer();
|
||||
|
||||
gLdapServer.search(LDAP_CONFIG.baseDn, function (req, res, next) {
|
||||
let results = [];
|
||||
|
||||
gLdapUsers.forEach(function (entry) {
|
||||
var dn = ldap.parseDN(`cn=${entry.username},${LDAP_BASE_DN}`);
|
||||
|
||||
var obj = {
|
||||
dn: dn.toString(),
|
||||
attributes: {
|
||||
objectclass: [ 'inetOrgPerson' ],
|
||||
mail: entry.email,
|
||||
cn: entry.displayName
|
||||
}
|
||||
};
|
||||
|
||||
obj.attributes[LDAP_CONFIG.usernameField] = entry.username;
|
||||
|
||||
if ((req.dn.equals(dn) || req.dn.parentOf(dn)) && req.filter.matches(obj.attributes)) {
|
||||
results.push(obj);
|
||||
}
|
||||
});
|
||||
|
||||
finalSend(results, req, res, next);
|
||||
});
|
||||
|
||||
gLdapServer.bind(LDAP_CONFIG.baseDn, function (req, res, next) {
|
||||
// extract the common name which might have different attribute names
|
||||
var attributeName = Object.keys(req.dn.rdns[0].attrs)[0];
|
||||
var commonName = req.dn.rdns[0].attrs[attributeName].value;
|
||||
if (!commonName) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
|
||||
if (!gLdapUsers.find(function (u) { return u.username === commonName; })) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
if (req.credentials !== LDAP_SHARED_PASSWORD) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
|
||||
|
||||
res.end();
|
||||
});
|
||||
|
||||
gLdapServer.listen(LDAP_PORT, callback);
|
||||
}
|
||||
|
||||
function stopLdapServer(callback) {
|
||||
if (gLdapServer) gLdapServer.close();
|
||||
callback();
|
||||
}
|
||||
|
||||
function setup(done) {
|
||||
mailer._mailQueue = [];
|
||||
|
||||
async.series([
|
||||
startLdapServer,
|
||||
server.start,
|
||||
database._clear,
|
||||
domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE),
|
||||
cleanupUsers,
|
||||
createOwner,
|
||||
settings.setAdmin.bind(null, DOMAIN_0.domain, 'my.' + DOMAIN_0.domain)
|
||||
], done);
|
||||
}
|
||||
|
||||
function cleanup(done) {
|
||||
mailer._mailQueue = [];
|
||||
|
||||
async.series([
|
||||
database._clear,
|
||||
server.stop,
|
||||
stopLdapServer
|
||||
], done);
|
||||
}
|
||||
|
||||
function enable(config, callback) {
|
||||
if (typeof config === 'function') {
|
||||
callback = config;
|
||||
config = LDAP_CONFIG;
|
||||
}
|
||||
|
||||
settings.setExternalLdapConfig(config, callback);
|
||||
}
|
||||
|
||||
function disable(callback) {
|
||||
const config = {
|
||||
provider: 'noop'
|
||||
};
|
||||
|
||||
settings.setExternalLdapConfig(config, callback);
|
||||
}
|
||||
|
||||
describe('External LDAP', function () {
|
||||
before(setup);
|
||||
after(cleanup);
|
||||
|
||||
describe('settings', function () {
|
||||
it('enabling fails with missing url', function (done) {
|
||||
let conf = _.extend({}, LDAP_CONFIG);
|
||||
delete conf.url;
|
||||
|
||||
enable(conf, function (error) {
|
||||
expect(error).to.be.ok();
|
||||
expect(error.reason).to.equal(BoxError.BAD_FIELD);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('enabling fails with empty url', function (done) {
|
||||
let conf = _.extend({}, LDAP_CONFIG);
|
||||
conf.url = '';
|
||||
|
||||
enable(conf, function (error) {
|
||||
expect(error).to.be.ok();
|
||||
expect(error.reason).to.equal(BoxError.BAD_FIELD);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('enabling fails with missing baseDn', function (done) {
|
||||
let conf = _.extend({}, LDAP_CONFIG);
|
||||
delete conf.baseDn;
|
||||
|
||||
enable(conf, function (error) {
|
||||
expect(error).to.be.ok();
|
||||
expect(error.reason).to.equal(BoxError.BAD_FIELD);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('enabling fails with empty baseDn', function (done) {
|
||||
let conf = _.extend({}, LDAP_CONFIG);
|
||||
conf.baseDn = '';
|
||||
|
||||
enable(conf, function (error) {
|
||||
expect(error).to.be.ok();
|
||||
expect(error.reason).to.equal(BoxError.BAD_FIELD);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('enabling fails with missing filter', function (done) {
|
||||
let conf = _.extend({}, LDAP_CONFIG);
|
||||
delete conf.filter;
|
||||
|
||||
enable(conf, function (error) {
|
||||
expect(error).to.be.ok();
|
||||
expect(error.reason).to.equal(BoxError.BAD_FIELD);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('enabling fails with empty filter', function (done) {
|
||||
let conf = _.extend({}, LDAP_CONFIG);
|
||||
conf.filter = '';
|
||||
|
||||
enable(conf, function (error) {
|
||||
expect(error).to.be.ok();
|
||||
expect(error.reason).to.equal(BoxError.BAD_FIELD);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('enabling succeeds', function (done) {
|
||||
enable(function (error) {
|
||||
expect(error).to.equal(null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('disabling succeeds', function (done) {
|
||||
disable(function (error) {
|
||||
expect(error).to.equal(null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sync', function () {
|
||||
it('fails if disabled', function (done) {
|
||||
externalldap.sync(function progress() {}, function (error) {
|
||||
expect(error).to.be.ok();
|
||||
expect(error.reason).to.equal(BoxError.BAD_STATE);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('enable', enable);
|
||||
|
||||
it('succeeds for new users', function (done) {
|
||||
gLdapUsers.push({
|
||||
username: 'firstuser',
|
||||
displayName: 'First User',
|
||||
email: 'first@user.com'
|
||||
});
|
||||
|
||||
externalldap.sync(function progress() {}, function (error) {
|
||||
expect(error).to.equal(null);
|
||||
|
||||
users.getAll(function (error, result) {
|
||||
expect(error).to.equal(null);
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result.find(function (u) {
|
||||
return u.username === 'firstuser' && u.email === 'first@user.com' && u.displayName === 'First User';
|
||||
})).to.be.ok();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('succeeds for updated users', function (done) {
|
||||
gLdapUsers[0].displayName = 'User First';
|
||||
gLdapUsers[0].email = 'first@changed.com';
|
||||
|
||||
externalldap.sync(function progress() {}, function (error) {
|
||||
expect(error).to.equal(null);
|
||||
|
||||
users.getAll(function (error, result) {
|
||||
expect(error).to.equal(null);
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result.find(function (u) {
|
||||
return u.username === 'firstuser' && u.email === 'first@changed.com' && u.displayName === 'User First';
|
||||
})).to.be.ok();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores already existing users with same username', function (done) {
|
||||
gLdapUsers.push({
|
||||
username: USERNAME,
|
||||
displayName: 'Something Else',
|
||||
email: 'foobar@bar.com'
|
||||
});
|
||||
|
||||
externalldap.sync(function progress() {}, function (error) {
|
||||
expect(error).to.equal(null);
|
||||
|
||||
users.getAll(function (error, result) {
|
||||
expect(error).to.equal(null);
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result.find(function (u) {
|
||||
return u.email === 'foobar@bar.com' || u.displayName === 'Something Else';
|
||||
})).to.not.be.ok();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('disable', disable);
|
||||
});
|
||||
|
||||
describe('user auto creation', function () {
|
||||
it('fails if external ldap is disabled', function (done) {
|
||||
settings.setExternalLdapConfig({ provider: 'noop' }, function (error) {
|
||||
expect(error).to.equal(null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('enable', enable);
|
||||
|
||||
it('fails if auto create is disabled', function (done) {
|
||||
gLdapUsers.push({
|
||||
username: 'autologinuser0',
|
||||
displayName: 'Auto Login0',
|
||||
email: 'auto0@login.com'
|
||||
});
|
||||
|
||||
superagent.post(SERVER_URL + '/api/v1/developer/login')
|
||||
.send({ username: 'autologinuser0', password: LDAP_SHARED_PASSWORD })
|
||||
.end(function (error, result) {
|
||||
expect(result.statusCode).to.equal(401);
|
||||
|
||||
users.getAll(function (error, result) {
|
||||
expect(error).to.equal(null);
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result.find(function (u) {
|
||||
return u.username === 'autologinuser0';
|
||||
})).to.not.be.ok();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('enable auto create', function (done) {
|
||||
let conf = _.extend({}, LDAP_CONFIG);
|
||||
conf.autoCreate = true;
|
||||
|
||||
enable(conf, done);
|
||||
});
|
||||
|
||||
it('fails for unknown user', function (done) {
|
||||
superagent.post(SERVER_URL + '/api/v1/developer/login')
|
||||
.send({ username: 'doesnotexist', password: LDAP_SHARED_PASSWORD })
|
||||
.end(function (error, result) {
|
||||
expect(result.statusCode).to.equal(401);
|
||||
|
||||
users.getAll(function (error, result) {
|
||||
expect(error).to.equal(null);
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result.find(function (u) {
|
||||
return u.username === 'doesnotexist';
|
||||
})).to.not.be.ok();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('succeeds for known user with wrong password', function (done) {
|
||||
gLdapUsers.push({
|
||||
username: 'autologinuser1',
|
||||
displayName: 'Auto Login1',
|
||||
email: 'auto1@login.com'
|
||||
});
|
||||
|
||||
superagent.post(SERVER_URL + '/api/v1/developer/login')
|
||||
.send({ username: 'autologinuser1', password: 'wrongpassword' })
|
||||
.end(function (error, result) {
|
||||
expect(result.statusCode).to.equal(401);
|
||||
|
||||
users.getAll(function (error, result) {
|
||||
expect(error).to.equal(null);
|
||||
expect(result.length).to.equal(3);
|
||||
expect(result.find(function (u) {
|
||||
return u.username === 'autologinuser1';
|
||||
})).to.be.ok();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('succeeds for known user with correct password', function (done) {
|
||||
gLdapUsers.push({
|
||||
username: 'autologinuser2',
|
||||
displayName: 'Auto Login2',
|
||||
email: 'auto2@login.com'
|
||||
});
|
||||
|
||||
superagent.post(SERVER_URL + '/api/v1/developer/login')
|
||||
.send({ username: 'autologinuser2', password: LDAP_SHARED_PASSWORD })
|
||||
.end(function (error, result) {
|
||||
expect(result.statusCode).to.equal(200);
|
||||
|
||||
users.getAll(function (error, result) {
|
||||
expect(error).to.equal(null);
|
||||
expect(result.length).to.equal(4);
|
||||
expect(result.find(function (u) {
|
||||
return u.username === 'autologinuser2';
|
||||
})).to.be.ok();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('disable', disable);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user