#!/usr/bin/env node var fs = require('fs'); var ldapjs = require('ldapjs'); // Stub unimplemented functionality. ldapjs.ExtensibleFilter.prototype.matches = ldapjs.EqualityFilter.prototype.matches; ldapjs.ApproximateFilter.prototype.matches = ldapjs.EqualityFilter.prototype.matches; // Remove superfluous spaces from DNs. var wrappee = ldapjs.DN.prototype.format; ldapjs.DN.prototype.format = function(options) { options = options || this._format; options['skipSpace'] = true; return (wrappee.bind(this))(options); }; var port = process.env.PORT; var certificate = fs.readFileSync(process.env.SSL_CERT, "utf-8"); var key = fs.readFileSync(process.env.SSL_KEY, "utf-8"); var server = ldapjs.createServer({certificate: certificate, key: key}); // var pokemon = [ { dn: 'cn=bulbasaur,o=localhost', attributes: { cn: 'bulbasaur', evolution: "0", type: ["grass", "poison"], } }, { dn: 'cn=ivysaur,o=localhost', attributes: { cn: 'ivysaur', evolution: "1", type: ["grass", "poison"], } }, { dn: 'cn=venusaur,o=localhost', attributes: { cn: 'venusaur', evolution: "2", type: ["grass", "poison"], } }, { dn: 'cn=charmander,o=localhost', attributes: { cn: 'charmander', evolution: "0", type: ["fire"], } }, { dn: 'cn=charmeleon,o=localhost', attributes: { cn: 'charmeleon', evolution: "1", type: ["fire"], } }, { dn: 'cn=charizard,o=localhost', attributes: { cn: 'charizard', evolution: "2", type: ["fire", "flying"], } }, { dn: 'cn=squirtle,o=localhost', attributes: { cn: 'squirtle', evolution: "0", type: ["water"], } }, { dn: 'cn=wartortle,o=localhost', attributes: { cn: 'wartortle', evolution: "1", type: ["water"], } }, { dn: 'cn=blastoise,o=localhost', attributes: { cn: 'blastoise', evolution: "2", type: ["water"], } }, { dn: 'cn=caterpie,o=localhost', attributes: { cn: 'caterpie', evolution: "0", type: ["bug"], } }, { dn: 'cn=metapod,o=localhost', attributes: { cn: 'metapod', evolution: "1", type: ["bug"], } }, { dn: 'cn=butterfree,o=localhost', attributes: { cn: 'butterfree', evolution: "2", type: ["bug", "flying"], } }, { dn: 'cn=pikachu,o=localhost', attributes: { cn: 'pikachu', evolution: "0", type: ["electric"], password: ["i-choose-you"] } }, ]; server.bind('cn=admin', function(req, res, next) { if ((req.dn.toString() === 'cn=admin') && (req.credentials === 'secret')) { res.end(); return next(); } else { return next(new ldapjs.InvalidCredentialsError()); } }); server.bind('cn=pikachu,o=localhost', function(req, res, next) { if ((req.dn.toString() === 'cn=pikachu,o=localhost') && (req.credentials === 'i-choose-you')) { res.end(); return next(); } else { return next(new ldapjs.InvalidCredentialsError()); } }); function authorize(req, res, next) { var bindDN = req.connection.ldap.bindDN; if ((bindDN.equals('cn=admin')) || (bindDN.equals('cn=anonymous'))) { return next(); } else { return next(new ldapjs.InsufficientAccessRightsError()); } } server.search('o=localhost', [authorize], function(req, res, next) { for (var i = 0; i < pokemon.length; i++) { if (req.filter.matches(pokemon[i].attributes)) { res.send(pokemon[i]); } }; res.end(); return next(); }); // Javascript is helpless Array.prototype.diff = function(arr) { return this.filter(function(idx) { return arr.indexOf(idx) < 0; }); }; server.modify('o=localhost', [], function(req, res, next) { var dn = req.dn.toString(); for (var i = 0; i < pokemon.length; i++) { if (pokemon[i].dn === dn) { for (var j = 0; j < req.changes.length; j++) { var m = req.changes[j].modification; switch (req.changes[j].operation) { case 'add': pokemon[i].attributes[m.type] = pokemon[i].attributes[m.type].concat(m.vals); break; case 'delete': if (m.type === "password") { return next(new ldapjs.UnwillingToPerformError('cannot delete password')); } else { if (m.vals === 0) { delete pokemon[i].attributes[m.type]; } else { pokemon[i].attributes[m.type] = pokemon[i].attributes[m.type].diff(m.vals); if (pokemon[i].attributes[m.type].length === 0) { delete pokemon[i].attributes[m.type]; } } } break; case 'replace': if (m.vals === 0) { delete pokemon[i].attributes[m.type]; } else { pokemon[i].attributes[m.type] = m.vals; } break; } } res.end(); return next(); } } res.end(); return next(new ldapjs.NoSuchObjectError(dn)); }); server.add('o=localhost', [], function(req, res, next) { var attributes = req.toObject().attributes; pokemon.push(req.toObject()) res.end(); return next(); }); server.del('o=localhost', [], function(req, res, next) { for (var i = 0; i < pokemon.length; i++) { if (req.dn.toString() === pokemon[i].dn) { pokemon.splice(i, 1); res.end(); return next(); } } return next(new ldapjs.NoSuchObjectError(req.dn.toString())); }); server.modifyDN('o=localhost', [], function(req, res, next) { for (var i = 0; i < pokemon.length; i++) { if (req.dn.toString() === pokemon[i].dn) { req.dn.rdns[0] = req.newRdn.rdns[0]; pokemon[i].dn = req.dn.toString(); pokemon[i].attributes.cn = req.newRdn.rdns[0].attrs.cn.value; } } res.end(); return next(); }); server.compare('o=localhost', [], function(req, res, next) { for (var i = 0; i < pokemon.length; i++) { if (req.dn.toString() === pokemon[i].dn) { for (var attribute in pokemon[i].attributes) { if (attribute === req.attribute) { for (var j = 0; j < pokemon[i].attributes[attribute].length; j++) { if (pokemon[i].attributes[attribute][j] === req.value) { res.end(true); return next(); } } res.end(false); return next(); } } res.end(false); return next(); } } return next(new ldapjs.NoSuchObjectError(req.dn.toString())); }); server.listen(port, function() { console.log("ldaps://localhost:%d", port); });