node-activedirectory:微软Active Directory的ldapjs客户端,支持认证、授权与范围检索

ActiveDirectory is an Node.js ldapjs client for authN (authentication) and authZ (authorization) for Microsoft Active Directory with range retrieval support for large Active Directory installations.

分支1Tags22
文件最后提交记录最后更新时间
10 年前
10 年前
11 年前
12 年前
10 年前
12 年前
10 年前

活动目录(ActiveDirectory)用于Node.js

活动目录是一个基于ldapjs客户端,专用于微软Active Directory的身份验证(authN)和授权(authZ),并且支持大型Active Directory安装的大范围检索功能。这个代码是从我几年前编写的一个存在的C#库(未公开发布)移植过来的。以下是主要特性:

  • 身份验证
    • 权限验证(通过组成员信息)
    • 支持嵌套组
    • 范围指定器/检索支持(http://msdn.microsoft.com/en-us/library/dd358433.aspx
    • 自动分页支持(默认情况下,Active Directory结果(MaxPageSize)每请求限制为1000条)
    • 回收站(tombstone)查询支持
    • 遥控指针支持

所需库

活动目录使用以下额外的Node.js模块:

  • underscore - JavaScript的工具带库,提供了很多函数式编程支持
  • async - 适用于Node.js和浏览器的异步工具
  • ldapjs - 在Node.js中实现LDAP客户端和服务器的纯JavaScript框架
  • bunyan - 一个简单快速的JSON日志模块,适用于node.js服务

安装

npm install activedirectory

使用

var ActiveDirectory = require('activedirectory');
var config = { url: 'ldap://dc.domain.com',
               baseDN: 'dc=domain,dc=com',
               username: 'username@domain.com',
               password: 'password' }
var ad = new ActiveDirectory(config);

配置中指定的用户名和密码用于用户和组查找操作。

文档


### authenticate(username, password, callback)

使用指定的凭据进行简单的绑定来验证用户名和密码。

参数

  • username - 要验证的用户名。
  • password - 认证用的密码。
  • callback(err, authenticated) - 验证完成后调用的回调函数。

示例

var ad = new ActiveDirectory(config);
var username = 'john.smith@domain.com';
var password = 'password';

ad.authenticate(username, password, function(err, auth) {
  if (err) {
    console.log('ERROR: '+JSON.stringify(err));
    return;
  }
  
  if (auth) {
    console.log('已认证!');
  }
  else {
    console.log('认证失败!');
  }
});

### isUserMemberOf(opts, username, groupName, callback)

检查用户是否属于指定的组。此函数还会检查组内的成员资格。即使用户并未明确列为特定组的成员,但其所属的组是该组的成员时,此函数也会返回true。

参数

  • opts - 可选参数,扩展或覆盖功能。参见可选参数
  • username - 检查成员资格的用户名。可以是sAMAccountName、userPrincipalName或distinguishedName(dn)。
  • groupName - 检查成员资格的组名。可以是commonName(cn)或distinguishedName(dn)。
  • callback - 完成后执行的回调。callback(err: {Object}, result: {Boolean})

示例

var username = 'user@domain.com';
var groupName = 'Employees';

var ad = new ActiveDirectory(config);
ad.isUserMemberOf(username, groupName, function(err, isMember) {
  if (err) {
    console.log('ERROR: ' +JSON.stringify(err));
    return;
  }

  console.log(username + ' 是 ' + groupName + ' 的成员: ' + isMember);
});

### groupExists(opts, groupName, callback)

检查指定的组是否存在。

参数

示例

var groupName = 'Employees';

var ad = new ActiveDirectory(config);
ad.groupExists(groupName, function(err, exists) {
  if (err) {
    console.log('ERROR: ' +JSON.stringify(err));
    return;
  }

  console.log(groupName + ' 存在: ' + exists);
});

### userExists(opts, username, callback)

检查指定的用户是否存在。

参数

示例

var username = 'john.smith@domain.com';

var ad = new ActiveDirectory(config);
ad.userExists(username, function(err, exists) {
  if (err) {
    console.log('ERROR: ' +JSON.stringify(err));
    return;
  }

  console.log(username + ' 存在: ' + exists);
});

### getUsersForGroup(opts, groupName, callback)

对于指定的组,获取所有属于该组的用户。如果组包含其他组,那么也将递归地检索这些组的成员,以构建出完整属于指定组的用户列表。

参数

示例

var groupName = 'Employees';

var ad = new ActiveDirectory(config);
ad.getUsersForGroup(groupName, function(err, users) {
  if (err) {
    console.log('ERROR: ' +JSON.stringify(err));
    return;
  }

  if (! users) console.log('找不到名为:' + groupName + ' 的组。');
  else {
    console.log(JSON.stringify(users));
  }
});

### getGroupMembershipForUser(opts, username, callback)

对于指定的用户名,获取用户所属于的所有组。如果检索到的组是另一个组的成员,那么也会递归地检索该组以构建完整的用户所属组的层级结构。

参数

示例

var sAMAccountName = 'john.smith@domain.com';

var ad = new ActiveDirectory(config);
ad.getGroupMembershipForUser(sAMAccountName, function(err, groups) {
  if (err) {
    console.log('ERROR: ' +JSON.stringify(err));
    return;
  }

  if (! groups) console.log('未找到用户名:' + sAMAccountName + '。');
  else console.log(JSON.stringify(groups));
});

### getGroupMembershipForGroup(opts, groupName, callback)

对于指定的组,获取该组所属于的所有组。如果检索到的组是其他组的成员,那么也会递归地检索这些组,以构建完整的组所属关系的层级结构。

参数

示例

var groupName = 'Employees';

var ad = new ActiveDirectory(config);
ad.getGroupMembershipForGroup(groupName, function(err, groups) {
  if (err) {
    console.log('ERROR: ' +JSON.stringify(err));
    return;
  }

  if (! groups) console.log('未找到名为:' + groupName + ' 的组。');
  else console.log(JSON.stringify(groups));
});

### find(opts, callback)

执行针对指定的LDAP查询过滤器的一般搜索。此函数会返回匹配指定过滤器的用户和组。对结果不识别为用户或组的项(例如计算机账户等)可以在结果的"other"属性/数组中找到。

参数

示例

var _ = require('underscore');
var query = 'cn=*Exchange*';
var options = {
  includeMembership: ['group', 'user'], // 或者可以使用 'all'
  includeDeleted: false
};

var activeDirectory = new ActiveDirectory(config);
activeDirectory.find(query, function(error, results) {
  if (error || !results) {
    console.error('错误:', JSON.stringify(error));
    return;
  }

  console.log('组:');
  _.forEach(results.groups, function(group) {
    console.log('  ' + group.cn);
  });

  console.log('用户:');
  _.forEach(results.users, function(user) {
    console.log('  ' + user.cn);
  });

  console.log('其他:');
  _.forEach(results.other, function(other) {
    console.log('  ' + other.cn);
  });
});

查找已删除的对象(findDeletedObjects)

如果启用了活动目录安装的墓碑(回收站)功能,则使用findDeletedObjects来检索回收站中的项目。

关于墓碑和启用设置的更多信息,请参阅:

注意:当LDAP条目/对象被墓碑化时,并非所有属性都会保留。这是Active Directory本身的限制,而非库的限制。

参数

  • opts - 可选参数,用于扩展或覆盖功能。见可选参数。如果只提供字符串,则该字符串被认为是LDAP过滤器。
  • callback - 完成时执行的回调函数。callback(err: {Object}, result: {Array})

如果没有指定baseDN,则会在附加的URL上执行一个RootDSE查询,并追加'ou-Deleted Objects'。

示例

var url = 'ldap://yourdomain.com';
var opts = {
  baseDN: 'ou=Deleted Objects, dc=yourdomain, dc=com',
  filter: 'cn=*Bob*'
};
activeDirectory.findDeletedObjects(opts, function(err, result) {
  if (err) {
    console.error('ERROR:', JSON.stringify(err));
    return;
  }

  console.log('findDeletedObjects:', JSON.stringify(result));
});

查找用户(findUser)

通过sAMAccountNameuserPrincipalNamedistinguishedName(dn)或自定义过滤器查找用户名。如果找到,返回的对象包含所有请求的属性。默认情况下,以下属性被返回:

  • userPrincipalName, sAMAccountName, mail, lockoutTime, whenCreated, pwdLastSet, userAccountControl, employeeID, sn, givenName, initials, cn, displayName, comment, description

参数

  • opts - 可选参数,用于扩展或覆盖功能。见可选参数
  • username - 要获取信息的用户名。也可以传递用户的distinguishedName(dn)来检索。
  • callback(err, user) - 完成时执行的回调函数。callback(err: {Object}, user: {User})

示例

// 可以搜索任何类型的用户名
var sAMAccountName = 'username';
var userPrincipalName = 'username@domain.com';
var dn = 'CN=Smith\\, John,OU=Users,DC=domain,DC=com';

// 通过sAMAccountName查找用户
var activeDir = new ActiveDirectory(config);
activeDir.findUser(sAMAccountName, function(err, user) {
  if (err) {
    console.error('ERROR:', JSON.stringify(err));
    return;
  }

  if (!user) console.log('用户: ' + sAMAccountName + ' 未找到。');
  else console.log(JSON.stringify(user));
});

查找用户们(findUsers)

执行一个通用搜索,以匹配指定筛选器的用户。默认的用户LDAP过滤器为((&(|(objectClass=user)(objectClass=person))(!(objectClass=computer))(!(objectClass=group))))。

参数

  • opts - 可选参数,用于扩展或覆盖功能。见可选参数。如果只提供字符串,则该字符串被认为是追加在默认LDAP过滤器最后的额外条件。
  • callback - 完成时执行的回调函数。callback(err: {Object}, users: {Array[User]})

示例

var query = 'cn=*George*';

var activeDir = new ActiveDirectory(config);
activeDir.findUsers(query, function(err, users) {
  if (err) {
    console.error('ERROR:', JSON.stringify(err));
    return;
  }

  if (!users || users.length === 0) console.log('没有找到用户。');
  else {
    console.log('findUsers:', JSON.stringify(users));
  }
});

此文档提供了与Active Directory操作相关的JavaScript代码示例,包括查找用户、用户组以及已删除的对象等功能的实现方法和调用示例,以及如何初始化Active Directory实例并执行基本查询的操作。

高级用法

属性

默认情况下,用户和组返回以下属性:

  • 用户:distinguishedName, userPrincipalName, sAMAccountName, mail, lockoutTime, whenCreated, pwdLastSet, userAccountControl, employeeID, sn, givenName, initials, cn, displayName, comment, description
  • 组:distinguishedName, objectCategory, cn, description

如果需要覆盖这些默认设置,可以在创建ActiveDirectory实例时指定:

var ad = new ActiveDirectory({
  url: 'ldap://dc.domain.com',
  baseDN: 'dc=domain,dc=com',
  username: 'username@domain.com',
  password: 'password',
  attributes: {
    user: ['myCustomAttribute', 'mail', 'userPrinicipalName'],
    group: ['anotherCustomAttribute', 'objectCategory']
  }
});

如果要覆盖“user”或“group”的属性,必须指定所有想要的属性。现有的默认值会被覆盖。可选地,你可以使用opts参数按调用基础来重写属性。

引导记录

默认情况下,引导记录追踪是禁用的。要启用它,在创建实例时指定referrals属性。referrals对象具有以下语法:

{
  referrals: {
    enabled: false,
    excluded: [
      'ldaps?://ForestDnsZones\\./.*',
      'ldaps?://DomainDnsZones\\./.*',
      'ldaps?://.*/CN=Configuration,.*'
    ]
  }
}

excluded选项是一系列正则表达式过滤器,用于忽略特定的引导记录。默认排除列表已在上方列出,忽略了ActiveDirectory默认创建的特殊分区。要指定这些选项,如下所示进行覆盖:

var ad = new ActiveDirectory({
  url: 'ldap://dc.domain.com',
  baseDN: 'dc=domain,dc=com',
  username: 'username@domain.com',
  password: 'password',
  attributes: {...},
  referrals: {
    enabled: true,
    excluded: []
  }
});

如果启用引导记录追踪,指定的用户名必须是"userPrincipalName"。

自定义条目解析

如果你希望以不同方式处理搜索条目,或者用额外的数据增强搜索结果,可以传递一个自定义解析器。例如,如果你想改变二进制值objectSidGUID,这很有用。

示例:

function customEntryParser(entry, raw, callback){
    if (raw.hasOwnProperty("objectSid")){
        entry.objectSid = raw.objectSid;
    }
    if (raw.hasOwnProperty("objectGUID")){
        entry.objectGUID = raw.objectGUID;
    }
    callback(entry);
};

如果你想指定自己的解析器,可以如下覆盖默认解析器:

var ad = new ActiveDirectory({
  url: 'ldap://dc.domain.com',
  baseDN: 'dc=domain,dc=com',
  username: 'username@domain.com',
  password: 'password',
  attributes: {...},
  referrals: {...},
  entryParser : customEntryParser
});

可选地,你可以在opts对象中指定你的自定义条目解析器。有关更多信息,请参阅可选参数

var opts = function(entry, raw, callback) {
  entry.retrievedAt = new Date();
  callback(entry);
};
ad.findUser(opts, 'userPrincipalName=bob@domain.com', function(err, user) {
  ...
});
### 可选参数 / 扩展功能

任何接受opts参数的方法都允许添加额外的选项。支持activedirectory.js和内部ldapjs客户端的选项。

目前支持的ldapjs opts选项包括:

  • url - 有效的LDAP URL。
  • host - 要连接的主机名(与端口一起使用代替URL)。
  • port - 连接的端口号(与主机名一起使用代替URL)。
  • secure - 表示使用的是ldaps://还是ldap://。(与主机名/端口配合使用代替URL)。
  • tlsOptions - 添加其他TLS选项(请参阅ldapjs了解更多详细信息)。
  • socketPath - 如果你在Unix域套接字上运行LDAP服务器,可以使用这个。
  • log - 可以选择性传入一个bunyan实例,让客户端使用它获取日志器。客户端会将所有消息记录在trace级别。
  • timeout - 客户端应允许操作持续多长时间,然后超时。默认为Infinity。
  • idleTimeout - 在TCP连接超时前等待多久。默认由操作系统决定。
  • bindDN - 所有连接应该绑定的身份标识DN。
  • bindCredentials - 与bindDN配合使用的凭据。
  • scope - 基、单、或子之一。默认为基。
  • filter - 一个字符串形式的LDAP筛选器(见下文),或程序化构建的Filter对象。默认为(objectclass=*)。
  • attributes - 要选择并返回的属性(如果设置了这些属性,服务器只会返回这些属性)。默认为空集,意味着所有属性。
  • sizeLimit - 最大返回的条目数。默认为0(无限制)。
  • timeLimit - 服务器响应的最大时间,单位为秒。默认为10。很多服务器会忽略这个。

对于activedirectory.js的选项包括:

示例

var opts = {
  scope: 'sub',
  filter: 'objectClass=User',
  includeMembership: [ 'user' ],
  entryParser: function(entry, raw, callback) {
    // 返回null以排除结果
    if (entry.ignore) return(null);

    entry.retrievedAt = new Date();
    entry.preferredServer = getPreferredServerFromDatabase(entry.userPrincipalName);

    callback(entry);  
  }
};

项目介绍

ActiveDirectory is an Node.js ldapjs client for authN (authentication) and authZ (authorization) for Microsoft Active Directory with range retrieval support for large Active Directory installations.

定制我的领域