node-ldapauthz
A simple node.js lib for user authorization based on LDAP server groups.
License
##Installation
$ npm install connect-roles
Usage
var LdapAuthz = require('ldapauthz');
var authz = new LdapAuthz({url: 'ldaps://ldap.example.com:663', ...});
...
authz.getGrantedAuthorities(username, function (err, authorities) { ... });
...
authz.close(function (err) { ... })
API
LdapAuthz(opts)
Creates an LDAP authz class.
- opts {Object} Configuration options
- url {String} A valid LDAP url. E.g. 'ldaps://ldap.example.com:663'
- adminDn {String} Optional, e.g. 'uid=myapp,ou=users,o=example.com'
- adminPassword {String} Password for adminDn.
- groupSearchBase {String} Defines the part of the directory tree under which group searches should be performed. E.g. 'ou=groups,o=example.com'
- groupSearchScope {String} Optional, default 'sub'. Scope of the search, one of 'base', 'one', or 'sub'.
- groupSearchFilter {String} The filter which is used to search for group membership. Use the literal '{{username}}' to have the given username be interpolated in for the LDAP search. The default is (uniqueMember={{username}}), corresponding to the groupOfUniqueMembers LDAP class.
- groupRoleAttribute {String} The attribute which contains the name of the authority defined by the group entry. Defaults to cn.
- log4js Optional. The require'd log4js module to use for logging. If given this will result in TRACE-level logging for ldapauthz.
- verbose {Boolean} Optional, default false. If
log4js
is also given, this will add TRACE-level logging for ldapjs (quite verbose). - timeout {Integer} Optional, default Infinity. How long the client should let operations live for before timing out.
- connectTimeout {Integer} Optional, default is up to the OS. How long the client should wait before timing out on TCP connections.
- tlsOptions {Object} Additional options passed to the TLS connection layer when connecting via ldaps://. See http://nodejs.org/api/tls.html#tls_tls_connect_options_callback for available options.
- maxConnections {Integer} Whether or not to enable connection pooling, and if so, how many to maintain.
- bindDN {String} The DN all connections should be bound as. Defaults to adminDn.
- bindCredentials {String} The credentials to use with bindDN. Defaults to adminPassword.
- checkInterval {Integer} How often to schedule health checks for the connection pool.
- maxIdleTime {Integer} How long a client can be idle before health-checking the connection (subject to the checkInterval frequency).
close(callback)
Closes the LDAP client.
- callback {Function} Callback function.
getGrantedAuthorities(username, callback);
Gets the list of authorities for the user.
- username {String} The user name.
- callback {Function}
function (err, authorities)
The callback function.
Examples
Example using express, passport and connect-roles
const URL = 'ldap://localhost:389';
const ADMIN_DN = 'cn=root,ou=person,dc=sample,dc=com';
const ADMIN_PASSWORD = 'secret';
const SEARCH_BASE = 'ou=person,dc=sample,dc=com';
const SEARCH_FILTER = '(&(objectclass=inetOrgPerson)(uid={{username}}))';
const GROUP_SEARCH_BASE = 'ou=group,dc=sample,dc=com';
const GROUP_SEARCH_FILTER = '(&(uniqueMember={{username}})(objectClass=group))';
var _ = require("underscore"),
express = require('express'),
bodyParser = require('body-parser'),
session = require('express-session'),
cookieParser = require('cookie-parser'),
passport = require('passport'),
LdapStrategy = require('passport-ldapauth').Strategy,
ConnectRoles = require('connect-roles'),
LdapAuthz = require('./ldapauthz.js');
var app = express();
var user = new ConnectRoles();
passport.use(new LdapStrategy({
server: {
url: URL,
adminDn: ADMIN_DN,
adminPassword: ADMIN_PASSWORD,
searchBase: SEARCH_BASE,
searchFilter: SEARCH_FILTER
}
},
function(user, done) {
console.log('user: ' + user.dn);
var authz = new LdapAuthz({
url: URL,
adminDn: ADMIN_DN,
adminPassword: ADMIN_PASSWORD,
groupSearchBase: GROUP_SEARCH_BASE,
groupSearchFilter: GROUP_SEARCH_FILTER
});
authz.getGrantedAuthorities(user.dn, function (err, authorities) {
authz.close(function (err) {});
if (err) {
return done(err);
}
user.roles = authorities;
return done(null, user);
});
}
));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(session({ secret: 'keyboard cat', saveUninitialized: true, resave: true }));
app.use(cookieParser());
app.use(passport.initialize());
app.use(passport.session());
app.use(user.middleware());
// Anonymous users can only access the home page
// Returning false stops any more rules from being considered
user.use(function (req, action) {
if (!req.isAuthenticated()) {
return action === 'access home page';
}
})
user.use('user', function (req) {
return _.contains(req.user.roles, 'GEC-BR-search-usuarios');
});
user.use('admin', function (req) {
return _.contains(req.user.roles, 'GEC-BR-search-administradores');
});
app.get('/', user.can('access home page'), function (req, res) {
res.send({status: 'home - ok'}).end();
});
app.get('/login', passport.authenticate('ldapauth'), function(req, res) {
res.send({status: 'login - ok'}).end();
});
app.get('/user', user.is('user'), function (req, res) {
res.send({status: 'user - ok'}).end();
});
app.get('/admin', user.is('admin'), function (req, res) {
res.send({status: 'admin - ok'}).end();
});
app.listen(8080);