From 17d2d4e5cb6e28a3bf7523022a3502ec09a7b0d4 Mon Sep 17 00:00:00 2001 From: Mark Cavage Date: Mon, 15 Aug 2011 10:53:57 -0700 Subject: [PATCH] Documentation and makefile --- .gitignore | 2 + Makefile | 65 ++++++++++ docs/guide.md | 350 +++++++++++++++++++++++++------------------------- package.json | 1 - 4 files changed, 242 insertions(+), 176 deletions(-) create mode 100644 Makefile diff --git a/.gitignore b/.gitignore index 61d1d99..6b08590 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ node_modules *.log *.ldif +*.tar* +docs/pkg diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..72ff041 --- /dev/null +++ b/Makefile @@ -0,0 +1,65 @@ +NAME=ldapjs + +ifeq ($(VERSION), "") + @echo "Use gmake" +endif + + +SRC := $(shell pwd) +TAR = tar +UNAME := $(shell uname) +ifeq ($(UNAME), SunOS) + TAR = gtar +endif + +HAVE_GJSLINT := $(shell which gjslint >/dev/null && echo yes || echo no) +NPM := npm_config_tar=$(TAR) npm + +RESTDOWN = ./node_modules/.restdown/bin/restdown +RESTDOWN_VERSION=1.2.11 +DOCPKGDIR = ./docs/pkg + +.PHONY: dep lint test doc clean all + +all:: test doc + +node_modules/.ldapjs.npm.installed: + $(NPM) install --dev + if [[ ! -d node_modules/.restdown ]]; then \ + git clone git://github.com/trentm/restdown.git node_modules/.restdown; \ + else \ + (cd node_modules/.restdown && git fetch origin); \ + fi + @(cd ./node_modules/.restdown && git checkout $(RESTDOWN_VERSION)) + @touch ./node_modules/.ldapjs.npm.installed + +dep: ./node_modules/.ldapjs.npm.installed + +gjslint: + gjslint --nojsdoc -r lib -r tst + +ifeq ($(HAVE_GJSLINT), yes) +lint: gjslint +else +lint: + @echo "* * *" + @echo "* Warning: Cannot lint with gjslint. Install it from:" + @echo "* http://code.google.com/closure/utilities/docs/linter_howto.html" + @echo "* * *" +endif + +doc: dep + @rm -rf ${DOCPKGDIR} + @mkdir -p ${DOCPKGDIR} + ${RESTDOWN} -m ${DOCPKGDIR} -D mediaroot=media ./docs/guide.md + rm docs/*.json + mv docs/*.html ${DOCPKGDIR} + sed -i '' -e 's|docs/public/media|media|g' ${DOCPKGDIR}/*.html + (cd ${DOCPKGDIR} && $(TAR) -czf ${SRC}/${NAME}-docs-`git log -1 --pretty='format:%h'`.tar.gz *) + + +test: dep lint + $(NPM) test + +clean: + @rm -fr ${DOCPKGDIR} node_modules *.log diff --git a/docs/guide.md b/docs/guide.md index 4101172..3ab8e91 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -28,12 +28,12 @@ basically breaks down as follows: It might be helpful to visualize that: - o=example - / \ - ou=users ou=groups - / | | \ - cn=john cn=jane cn=dudes cn=dudettes - / + o=example + / \ + ou=users ou=groups + / | | \ + cn=john cn=jane cn=dudes cn=dudettes + / keyid=foo @@ -187,8 +187,8 @@ Blah blah, let's try running the ldap client again, first with a bad password: $ ldapsearch -H ldap://localhost:1389 -x -D cn=root -w foo -b "o=myhost" objectclass=* ldap_bind: Invalid credentials (49) - matched DN: cn=root - additional info: Invalid Credentials + matched DN: cn=root + additional info: Invalid Credentials And again with the correct one: @@ -212,7 +212,7 @@ authorization handler that we'll use in all our subsequent routes: function authorize(req, res, next) { if (!req.connection.ldap.bindDN.equals('cn=root')) - return next(new ldap.InsufficientAccessRightsError()); + return next(new ldap.InsufficientAccessRightsError()); return next(); } @@ -249,35 +249,35 @@ First, let's make a handler that just loads the "user database" for us in a function loadPasswdFile(req, res, next) { fs.readFile('/etc/passwd', 'utf8', function(err, data) { - if (err) - return next(new ldap.OperationsError(err.message)); + if (err) + return next(new ldap.OperationsError(err.message)); - req.users = {}; + req.users = {}; - var lines = data.split('\n'); - for (var i = 0; i < lines.length; i++) { - if (!lines[i] || /^#/.test(lines[i])) - continue; + var lines = data.split('\n'); + for (var i = 0; i < lines.length; i++) { + if (!lines[i] || /^#/.test(lines[i])) + continue; - var record = lines[i].split(':'); - if (!record || !record.length) - continue; + var record = lines[i].split(':'); + if (!record || !record.length) + continue; - req.users[record[0]] = { - dn: 'cn=' + record[0] + ', ou=users, o=myhost', - attributes: { - cn: record[0], - uid: record[2], - gid: record[3], - description: record[4], - homedirectory: record[5], - shell: record[6] || '', - objectclass: 'unixUser' - } - }; - } + req.users[record[0]] = { + dn: 'cn=' + record[0] + ', ou=users, o=myhost', + attributes: { + cn: record[0], + uid: record[2], + gid: record[3], + description: record[4], + homedirectory: record[5], + shell: record[6] || '', + objectclass: 'unixUser' + } + }; + } - return next(); + return next(); }); } @@ -289,8 +289,8 @@ handler to process that: server.search('o=myhost', pre, function(req, res, next) { Object.keys(req.users).forEach(function(k) { - if (req.filter.matches(req.users[k].attributes)) - res.send(req.users[k]); + if (req.filter.matches(req.users[k].attributes)) + res.send(req.users[k]); }); res.end(); @@ -357,13 +357,13 @@ of attributes. So that's why we did this: var entry = { dn: 'cn=' + record[0] + ', ou=users, o=myhost', attributes: { - cn: record[0], - uid: record[2], - gid: record[3], - description: record[4], - homedirectory: record[5], - shell: record[6] || '', - objectclass: 'unixUser' + cn: record[0], + uid: record[2], + gid: record[3], + description: record[4], + homedirectory: record[5], + shell: record[6] || '', + objectclass: 'unixUser' } }; @@ -398,36 +398,36 @@ the following code in as another handler (you'll need a server.add('ou=users, o=myhost', pre, function(req, res, next) { if (!req.dn.rdns[0].cn) - return next(new ldap.ConstraintViolationError('cn required')); + return next(new ldap.ConstraintViolationError('cn required')); if (req.users[req.dn.rdns[0].cn]) - return next(new ldap.EntryAlreadyExistsError(req.dn.toString())); + return next(new ldap.EntryAlreadyExistsError(req.dn.toString())); var entry = req.toObject().attributes; if (entry.objectclass.indexOf('unixUser') === -1) - return next(new ldap.ConstraintViolation('entry must be a unixUser')); + return next(new ldap.ConstraintViolation('entry must be a unixUser')); var opts = ['-m']; if (entry.description) { - opts.push('-c'); - opts.push(entry.description[0]); + opts.push('-c'); + opts.push(entry.description[0]); } if (entry.homedirectory) { - opts.push('-d'); - opts.push(entry.homedirectory[0]); + opts.push('-d'); + opts.push(entry.homedirectory[0]); } if (entry.gid) { - opts.push('-g'); - opts.push(entry.gid[0]); + opts.push('-g'); + opts.push(entry.gid[0]); } if (entry.shell) { - opts.push('-s'); - opts.push(entry.shell[0]); + opts.push('-s'); + opts.push(entry.shell[0]); } if (entry.uid) { - opts.push('-u'); - opts.push(entry.uid[0]); + opts.push('-u'); + opts.push(entry.uid[0]); } opts.push(entry.cn[0]); var useradd = spawn('useradd', opts); @@ -435,22 +435,22 @@ the following code in as another handler (you'll need a var messages = []; useradd.stdout.on('data', function(data) { - messages.push(data.toString()); + messages.push(data.toString()); }); useradd.stderr.on('data', function(data) { - messages.push(data.toString()); + messages.push(data.toString()); }); useradd.on('exit', function(code) { - if (code !== 0) { - var msg = '' + code; - if (messages.length) - msg += ': ' + messages.join(); - return next(new ldap.OperationsError(msg)); - } + if (code !== 0) { + var msg = '' + code; + if (messages.length) + msg += ': ' + messages.join(); + return next(new ldap.OperationsError(msg)); + } - res.end(); - return next(); + res.end(); + return next(); }); }); @@ -485,15 +485,15 @@ As before, here's a breakdown of the code: server.add('ou=users, o=myhost', pre, function(req, res, next) { if (!req.dn.rdns[0].cn) - return next(new ldap.ConstraintViolationError('cn required')); + return next(new ldap.ConstraintViolationError('cn required')); if (req.users[req.dn.rdns[0].cn]) - return next(new ldap.EntryAlreadyExistsError(req.dn.toString())); + return next(new ldap.EntryAlreadyExistsError(req.dn.toString())); var entry = req.toObject().attributes; if (entry.objectclass.indexOf('unixUser') === -1) - return next(new ldap.ConstraintViolation('entry must be a unixUser')); + return next(new ldap.ConstraintViolation('entry must be a unixUser')); Here's a few new things: @@ -530,37 +530,37 @@ Go ahead and add the following code into your source file: server.modify('ou=users, o=myhost', pre, function(req, res, next) { if (!req.dn.rdns[0].cn || !req.users[req.dn.rdns[0].cn]) - return next(new ldap.NoSuchObjectError(req.dn.toString())); + return next(new ldap.NoSuchObjectError(req.dn.toString())); if (!req.changes.length) - return next(new ldap.ProtocolError('changes required')); + return next(new ldap.ProtocolError('changes required')); var user = req.users[req.dn.rdns[0].cn].attributes; var mod; for (var i = 0; i < req.changes.length; i++) { - mod = req.changes[i].modification; - switch (req.changes[i].operation) { - case 'replace': - if (mod.type !== 'userpassword' || !mod.vals || !mod.vals.length) - return next(new ldap.UnwillingToPerformError('only password updates ' + - 'allowed')); - break; - case 'add': - case 'delete': - return next(new ldap.UnwillingToPerformError('only replace allowed')); - } + mod = req.changes[i].modification; + switch (req.changes[i].operation) { + case 'replace': + if (mod.type !== 'userpassword' || !mod.vals || !mod.vals.length) + return next(new ldap.UnwillingToPerformError('only password updates ' + + 'allowed')); + break; + case 'add': + case 'delete': + return next(new ldap.UnwillingToPerformError('only replace allowed')); + } } var passwd = spawn('chpasswd', ['-c', 'MD5']); passwd.stdin.end(user.cn + ':' + mod.vals[0], 'utf8'); passwd.on('exit', function(code) { - if (code !== 0) - return next(new ldap.OperationsError(code)); + if (code !== 0) + return next(new ldap.OperationsError(code)); - res.end(); - return next(); + res.end(); + return next(); }); }); @@ -592,28 +592,28 @@ delete it :). Go ahead and add the following code into your server: server.del('ou=users, o=myhost', pre, function(req, res, next) { if (!req.dn.rdns[0].cn || !req.users[req.dn.rdns[0].cn]) - return next(new ldap.NoSuchObjectError(req.dn.toString())); + return next(new ldap.NoSuchObjectError(req.dn.toString())); var userdel = spawn('userdel', ['-f', req.dn.rdns[0].cn]); var messages = []; userdel.stdout.on('data', function(data) { - messages.push(data.toString()); + messages.push(data.toString()); }); userdel.stderr.on('data', function(data) { - messages.push(data.toString()); + messages.push(data.toString()); }); userdel.on('exit', function(code) { - if (code !== 0) { - var msg = '' + code; - if (messages.length) - msg += ': ' + messages.join(); - return next(new ldap.OperationsError(msg)); - } + if (code !== 0) { + var msg = '' + code; + if (messages.length) + msg += ': ' + messages.join(); + return next(new ldap.OperationsError(msg)); + } - res.end(); - return next(); + res.end(); + return next(); }); }); @@ -638,7 +638,7 @@ the complete implementation for what we went through above: function authorize(req, res, next) { if (!req.connection.ldap.bindDN.equals('cn=root')) - return next(new ldap.InsufficientAccessRightsError()); + return next(new ldap.InsufficientAccessRightsError()); return next(); } @@ -646,35 +646,35 @@ the complete implementation for what we went through above: function loadPasswdFile(req, res, next) { fs.readFile('/etc/passwd', 'utf8', function(err, data) { - if (err) - return next(new ldap.OperationsError(err.message)); + if (err) + return next(new ldap.OperationsError(err.message)); - req.users = {}; + req.users = {}; - var lines = data.split('\n'); - for (var i = 0; i < lines.length; i++) { - if (!lines[i] || /^#/.test(lines[i])) - continue; + var lines = data.split('\n'); + for (var i = 0; i < lines.length; i++) { + if (!lines[i] || /^#/.test(lines[i])) + continue; - var record = lines[i].split(':'); - if (!record || !record.length) - continue; + var record = lines[i].split(':'); + if (!record || !record.length) + continue; - req.users[record[0]] = { - dn: 'cn=' + record[0] + ', ou=users, o=myhost', - attributes: { - cn: record[0], - uid: record[2], - gid: record[3], - description: record[4], - homedirectory: record[5], - shell: record[6] || '', - objectclass: 'unixUser' - } - }; - } + req.users[record[0]] = { + dn: 'cn=' + record[0] + ', ou=users, o=myhost', + attributes: { + cn: record[0], + uid: record[2], + gid: record[3], + description: record[4], + homedirectory: record[5], + shell: record[6] || '', + objectclass: 'unixUser' + } + }; + } - return next(); + return next(); }); } @@ -689,7 +689,7 @@ the complete implementation for what we went through above: server.bind('cn=root', function(req, res, next) { if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret') - return next(new ldap.InvalidCredentialsError()); + return next(new ldap.InvalidCredentialsError()); res.end(); return next(); @@ -698,36 +698,36 @@ the complete implementation for what we went through above: server.add('ou=users, o=myhost', pre, function(req, res, next) { if (!req.dn.rdns[0].cn) - return next(new ldap.ConstraintViolationError('cn required')); + return next(new ldap.ConstraintViolationError('cn required')); if (req.users[req.dn.rdns[0].cn]) - return next(new ldap.EntryAlreadyExistsError(req.dn.toString())); + return next(new ldap.EntryAlreadyExistsError(req.dn.toString())); var entry = req.toObject().attributes; if (entry.objectclass.indexOf('unixUser') === -1) - return next(new ldap.ConstraintViolation('entry must be a unixUser')); + return next(new ldap.ConstraintViolation('entry must be a unixUser')); var opts = ['-m']; if (entry.description) { - opts.push('-c'); - opts.push(entry.description[0]); + opts.push('-c'); + opts.push(entry.description[0]); } if (entry.homedirectory) { - opts.push('-d'); - opts.push(entry.homedirectory[0]); + opts.push('-d'); + opts.push(entry.homedirectory[0]); } if (entry.gid) { - opts.push('-g'); - opts.push(entry.gid[0]); + opts.push('-g'); + opts.push(entry.gid[0]); } if (entry.shell) { - opts.push('-s'); - opts.push(entry.shell[0]); + opts.push('-s'); + opts.push(entry.shell[0]); } if (entry.uid) { - opts.push('-u'); - opts.push(entry.uid[0]); + opts.push('-u'); + opts.push(entry.uid[0]); } opts.push(entry.cn[0]); var useradd = spawn('useradd', opts); @@ -735,95 +735,95 @@ the complete implementation for what we went through above: var messages = []; useradd.stdout.on('data', function(data) { - messages.push(data.toString()); + messages.push(data.toString()); }); useradd.stderr.on('data', function(data) { - messages.push(data.toString()); + messages.push(data.toString()); }); useradd.on('exit', function(code) { - if (code !== 0) { - var msg = '' + code; - if (messages.length) - msg += ': ' + messages.join(); - return next(new ldap.OperationsError(msg)); - } + if (code !== 0) { + var msg = '' + code; + if (messages.length) + msg += ': ' + messages.join(); + return next(new ldap.OperationsError(msg)); + } - res.end(); - return next(); + res.end(); + return next(); }); }); server.modify('ou=users, o=myhost', pre, function(req, res, next) { if (!req.dn.rdns[0].cn || !req.users[req.dn.rdns[0].cn]) - return next(new ldap.NoSuchObjectError(req.dn.toString())); + return next(new ldap.NoSuchObjectError(req.dn.toString())); if (!req.changes.length) - return next(new ldap.ProtocolError('changes required')); + return next(new ldap.ProtocolError('changes required')); var user = req.users[req.dn.rdns[0].cn].attributes; var mod; for (var i = 0; i < req.changes.length; i++) { - mod = req.changes[i].modification; - switch (req.changes[i].operation) { - case 'replace': - if (mod.type !== 'userpassword' || !mod.vals || !mod.vals.length) - return next(new ldap.UnwillingToPerformError('only password updates ' + - 'allowed')); - break; - case 'add': - case 'delete': - return next(new ldap.UnwillingToPerformError('only replace allowed')); - } + mod = req.changes[i].modification; + switch (req.changes[i].operation) { + case 'replace': + if (mod.type !== 'userpassword' || !mod.vals || !mod.vals.length) + return next(new ldap.UnwillingToPerformError('only password updates ' + + 'allowed')); + break; + case 'add': + case 'delete': + return next(new ldap.UnwillingToPerformError('only replace allowed')); + } } var passwd = spawn('chpasswd', ['-c', 'MD5']); passwd.stdin.end(user.cn + ':' + mod.vals[0], 'utf8'); passwd.on('exit', function(code) { - if (code !== 0) - return next(new ldap.OperationsError('' + code)); + if (code !== 0) + return next(new ldap.OperationsError('' + code)); - res.end(); - return next(); + res.end(); + return next(); }); }); server.del('ou=users, o=myhost', pre, function(req, res, next) { if (!req.dn.rdns[0].cn || !req.users[req.dn.rdns[0].cn]) - return next(new ldap.NoSuchObjectError(req.dn.toString())); + return next(new ldap.NoSuchObjectError(req.dn.toString())); var userdel = spawn('userdel', ['-f', req.dn.rdns[0].cn]); var messages = []; userdel.stdout.on('data', function(data) { - messages.push(data.toString()); + messages.push(data.toString()); }); userdel.stderr.on('data', function(data) { - messages.push(data.toString()); + messages.push(data.toString()); }); userdel.on('exit', function(code) { - if (code !== 0) { - var msg = '' + code; - if (messages.length) - msg += ': ' + messages.join(); - return next(new ldap.OperationsError(msg)); - } + if (code !== 0) { + var msg = '' + code; + if (messages.length) + msg += ': ' + messages.join(); + return next(new ldap.OperationsError(msg)); + } - res.end(); - return next(); + res.end(); + return next(); }); }); server.search('o=myhost', pre, function(req, res, next) { Object.keys(req.users).forEach(function(k) { - if (req.filter.matches(req.users[k].attributes)) - res.send(req.users[k]); + if (req.filter.matches(req.users[k].attributes)) + res.send(req.users[k]); }); res.end(); diff --git a/package.json b/package.json index cfd0494..55ce2e8 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "node-uuid": "~1.2.0" }, "scripts": { - "pretest": "which gjslint; if [[ \"$?\" = 0 ]] ; then gjslint --nojsdoc -r lib -r tst; else echo \"Missing gjslint. Skipping lint\"; fi", "test": "./node_modules/.bin/tap ./tst" } }