2018 LDAP(2)NodeJS and LDAP

2018 LDAP(2)NodeJS and LDAP

Set up LDAP Server
Install LDAPJS
> npm install ldapjs

I suppose to have OpenLDAP CLI on my machine, check it out.
> ldapsearch -H

> ldapsearch -V
ldapsearch: @(#) $OpenLDAP: ldapsearch 2.4.28 (Jul 15 2017 15:44:01) $

Set Up a Simple Project as follow: package.json
{
  "devDependencies": {
    "gulp": "3.9.0",
    "gulp-jshint": "1.12.0",
    "gulp-mocha": "2.1.3",
    "gulp-nodemon": "2.0.4",
    "gulp-util": "3.0.7",
    "gulp-concat": "2.6.0",
    "gulp-tar": "1.5.0",
    "gulp-gzip": "1.2.0",
    "gulp-artifactory-upload": "1.2.0",
    "gulp-jsdoc": "0.1.5",
    "gulp-stubby-server": "0.1.5",
    "gulp-help": "1.6.1",
    "ldapjs": "1.0.2"
  },
  "engines": {
    "node": ">=0.12.0"
  },
  "name": "ldap-server",
  "private": true,
  "version": "1.0.0"
}

password-app.js
/*jslint node: true */
'use strict';

var ldap = require('ldapjs');

var server = ldap.createServer();

server.listen(1389, function(){
    console.log('/etc/passwd LDAP server up at: %s', server.url);
});

NPM install and run with command
> node password-app.js

Run the client to connect to that
> ldapsearch -H ldap://localhost:1389 -x -b "o=myhost" objectclass=*
# extended LDIF
#
# LDAPv3
# base <o=myhost> with scope subtree
# filter: objectclass=*
# requesting: ALL
#
# search result
search: 2
result: 32 No such object
text: No tree found for: o=myhost
# numResponses: 1

Add this binding to the server
server.bind('cn=root', function(req, res, next) {
  if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret')
    return next(new ldap.InvalidCredentialsError());

  res.end();
  return next();
});

We will have the password to verified.
> 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: InvalidCredentialsError

> ldapsearch -H ldap://localhost:1389 -x -D cn=root -w secret -LLL -b "o=myhost" objectclass=*
No such object (32)
Additional information: No tree found for: o=myhost

The format for /etc/passwd is as follow:
jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8910,(234)555-0044,email:/home/jsmith:/bin/sh
jsmith - Username
x         - password hash
1001   - Numeric UID
1000   - Numeric Primary GID
‘Joe Smith, …’ - DisplayName and Other information
/home/jsmith.    Home directory
/bin/sh               Shell

Load and Parse all the Data into req.users
function loadPasswdFile(req, res, next) {
  fs.readFile('/etc/passwd', 'utf8', function(err, data) {
    if (err)
      return next(new ldap.OperationsError(err.message));

    req.users = {};

    var lines = data.split('\n');
    for (var i = 0; i < lines.length; i++) {
      if (!lines[i] || /^#/.test(lines[i])) // empty line or start with #, ignore
        continue;

      var record = lines[i].split(':');
      if (!record || !record.length)   //no :, ignore
        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'
        }
      };
    }

    return next();
  });
}

Add this to the binding
var pre = [authorize, loadPasswdFile];

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]);
  });

  res.end();
  return next();
});

Search for root user
> ldapsearch -H ldap://localhost:1389 -x -D cn=root -w secret -LLL -b "o=myhost" cn=root
dn: cn=root, ou=users, o=myhost
cn: root
uid: 0
gid: 0
description: System Administrator
homedirectory: /var/root
shell: /bin/sh
objectclass: unixUser

List all the user information
> ldapsearch -H ldap://localhost:1389 -x -D cn=root -w secret -LLL -b "o=myhost" objectclass=*

CN start with r*
> ldapsearch -H ldap://localhost:1389 -x -D cn=root -w secret -LLL -b "o=myhost" cn=r*
dn: cn=root, ou=users, o=myhost
cn: root
uid: 0
gid: 0
description: System Administrator
homedirectory: /var/root
shell: /bin/sh
objectclass: unixUser

Server Add
server.add('ou=users, o=myhost', pre, function(req, res, next) {
  if (!req.dn.rdns[0].cn)
    return next(new ldap.ConstraintViolationError('cn required'));

  if (req.users[req.dn.rdns[0].cn])
    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'));

  var opts = ['-m'];
  if (entry.description) {
    opts.push('-c');
    opts.push(entry.description[0]);
  }
  if (entry.homedirectory) {
    opts.push('-d');
    opts.push(entry.homedirectory[0]);
  }
  if (entry.gid) {
    opts.push('-g');
    opts.push(entry.gid[0]);
  }
  if (entry.shell) {
    opts.push('-s');
    opts.push(entry.shell[0]);
  }
  if (entry.uid) {
    opts.push('-u');
    opts.push(entry.uid[0]);
  }
  opts.push(entry.cn[0]);
  var useradd = spawn('useradd', opts);

  var messages = [];

  useradd.stdout.on('data', function(data) {
    messages.push(data.toString());
  });
  useradd.stderr.on('data', function(data) {
    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));
    }

    res.end();
    return next();
  });
});

Execute the command useradd

Modify User
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()));

  if (!req.changes.length)
    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'));
    }
  }

  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));

    res.end();
    return next();
  });
});

Create a file user.ldif
dn: cn=ldapjs, ou=users, o=myhost
objectClass: unixUser
cn: ldapjs
shell: /bin/bash
description:Created via ldapadd

Add User
>ldapadd -H ldap://localhost:1389 -x -D cn=root -w secret -f ./user.ldif

Change Password passwd.ldif
dn: cn=ldapjs, ou=users, o=myhost
changetype: modify
replace: userPassword
userPassword: secret
>ldapmodify -H ldap://localhost:1389 -x -D cn=root -w secret -f ./passwd.ldif

Delete User
>ldapdelete -H ldap://localhost:1389 -x -D cn=root -w secret "cn=ldapjs, ou=users, o=myhost"

It seems that the ldapJS will handle the protocol things, everything under that, our solution can store the data in DB or file system.

References:
http://ldapjs.org/guide.html
http://sillycat.iteye.com/blog/2414080
http://ldapjs.org/examples.html




Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326065303&siteId=291194637