# informat.ldap LDAP操作

## 概述

使用`informat.ldap`对象可链接到 LDAP（Lightweight Directory Access Protocol） 目录服务器，并进行搜索操作。

## connect

创建LDAP连接
通过指定的信息连接LDAP服务器，如果连接失败或者用户名密码验证失败会抛出异常，使用`connect`方法可以检查用户的用户名密码是否合法。
连接成功后，可以使用返回的[LdapConnection](/guide/script/ldap.md#ldapconnection)
进行查询，修改，新增等操作。注意在操作完毕后需要调用 [LdapConnection](/guide/script/ldap.md#ldapconnection) 的 `close`方法关闭连接。

```javascript
informat.ldap.connect(info)
```

| 参数   | 类型                                                              | 描述           |
|------|-----------------------------------------------------------------|--------------|
| info | [LdapConnectionInfo](/guide/script/model.md#ldapconnectioninfo) | 连接ldap服务器的信息 |

**返回值**

返回[LdapConnection](/guide/script/ldap.md#ldapconnection)对象

**示例**

```js
let connection = null
try {
    connection = informat.ldap.connect({
        providerURL: 'LDAP://1.13.173.190:389',
        securityPrincipal: 'user',
        securityCredentials: 'pwd'
    })

} catch (e) {
    //连接ldap服务器或者认证失败
    console.error('连接失败：', e)
} finally {
    if (connection != null) {
        connection.close();
    }
}
```

## LdapConnection

### close

关闭LDAP连接

```javascript
connection.close()
```

### search

搜索满足条件的条目
返回根节点为basedn，并且满足filter指定的条件的的条目。

```javascript
connection.search(basedn, filter, control)
```

| 参数      | 类型                  | 描述                                       |
|---------|---------------------|------------------------------------------|
| basedn  | `String`            | 根节点的完整路径,例如 `cn=users,dc=informat,dc=cn` |
| filter  | `String`            | 查询条件                                     |
| control | `LdapSearchControl` | 搜索控制                                     |

:::: tabs
::: tab 搜索条件的语法

- 并且条件 (& (...K1...) (...K2...))
- 或者条件 (| (...K1...) (...K2...))
- 嵌套的条件 (|(& (...K1...) (...K2...))(& (...K3...) (...K4...)))

:::
::: tab 查询条件的语法

- 等于:(attribute=abc) , e.g. (&(objectClass=user)(displayName=Foeckeler)
- 不等于:(!(attribute=abc)) , e.g. (!objectClass=group)
- 不等于空:    (attribute=*) , e.g. (mailNickName=*)
- 等于空:(!(attribute=*)) , e.g. (!proxyAddresses=*)
- 大于等于:(attribute>=abc) , e.g. (mdbStorageQuota>=100000)
- 小于等于:(attribute<=abc) , e.g. (mdbStorageQuota<=100000)
- 通配符:(attribute=ab*), e.g. (name=yan*)

:::
::::

**返回值**

类型为 Array<[LdapSearchResult](/guide/script/model.md#ldapsearchresult)>

**示例**

```js
// 查询cn=Users,dc=informat,dc=cn目录下10个条目，只返回id、name、age属性。
const result = connect.search('cn=Users,dc=informat,dc=cn', 'name=*', {
    searchScope: 'SUBTREE', // 搜索的限定范围,默认为 SUBTREE。SUBTREE 返回所有满足条件的条目，ONELEVEL 返回同级的条目，OBJECT 只返回满足条件的对象
    countLimit: 10, // 选填。返回的条目的最大数量，
    returningAttributes: ['id', 'name', 'age'] // 选填。返回的属性列表
})
// 输出所有条目的名称及其属性
result.forEach(r => {
    console.log(r.name);
    r.attributes.forEach(ra => {
        console.log(ra.id + " = " + ra.values[0])
    })
})
```

### list

枚举命名上下文中绑定的名称，以及绑定到它们的对象的类名

```javascript
connection.list(name)
```

| 参数   | 类型       | 描述 |
|------|----------|----|
| name | `String` | 名称 |

**返回值**

类型为 Array<[LdapListResult](/guide/script/model.md#ldaplistresult)>
返回绑定对象列表

### getAttributes

查询指定节点的属性

```javascript
connection.getAttributes(dn)
```

| 参数 | 类型       | 描述          |
|----|----------|-------------|
| dn | `String` | 要修改属性的节点的路径 |

**返回值**

类型为 Array<[LdapSearchResultAttribute](/guide/script/model.md#ldapsearchresultattribute)>
返回节点的属性列表

### addAttribute

新增属性到指定节点

```javascript
connection.addAttribute(dn, attributes)
```

| 参数         | 类型                                                                       | 描述          |
|------------|--------------------------------------------------------------------------|-------------|
| dn         | `String`                                                                 | 要修改属性的节点的路径 |
| attributes | Array<[LdapModifyAttribute](/guide/script/model.md#ldapmodifyattribute)> | 属性列表        |

### updateAttribute

更新指定节点的属性

```javascript
connection.updateAttribute(dn, attributes)
```

| 参数         | 类型                                                                       | 描述          |
|------------|--------------------------------------------------------------------------|-------------|
| dn         | `String`                                                                 | 要修改属性的节点的路径 |
| attributes | Array<[LdapModifyAttribute](/guide/script/model.md#ldapmodifyattribute)> | 属性列表        |

### deleteAttribute

删除指定节点的属性

```javascript
connection.deleteAttribute(dn, attributes)
```

| 参数         | 类型                                                                       | 描述          |
|------------|--------------------------------------------------------------------------|-------------|
| dn         | `String`                                                                 | 要修改属性的节点的路径 |
| attributes | Array<[LdapModifyAttribute](/guide/script/model.md#ldapmodifyattribute)> | 属性列表        |

### decodeSID

解码Windows AD域中账号的SID

```javascript
connection.decodeSID(sidAttribute)
```

| 参数           | 类型       | 描述     |
|--------------|----------|--------|
| sidAttribute | `Object` | sid属性值 |

**返回值**

类型为 `String`
返回解码后的SID值

### createEntry

创建一个新的 LDAP 条目（subcontext）。

用于创建 OU、user 等"新条目"，区别于 `addAttribute`（后者只能给已存在条目追加属性）。

```javascript
connection.createEntry(dn, attributes)
```

| 参数         | 类型                                                                       | 描述                                                                            |
|------------|--------------------------------------------------------------------------|-------------------------------------------------------------------------------|
| dn         | `String`                                                                 | 要创建的新条目的完整路径，例如 `OU=技术部,dc=informat,dc=cn`                                    |
| attributes | Array<[LdapModifyAttribute](/guide/script/model.md#ldapmodifyattribute)> | 新条目的初始属性列表。必须包含 `objectClass`。同一属性的多个值通过多条 `LdapModifyAttribute` 表达（每条只携带一个值） |

**返回值**

无返回值。如果父节点不存在、属性不合法或当前条目已存在，会抛出异常。

**示例**

```js
// 在 dc=informat,dc=cn 下创建一个组织单元 OU=技术部
connection.createEntry('OU=技术部,dc=informat,dc=cn', [
    { id: 'objectClass', value: 'top' },
    { id: 'objectClass', value: 'organizationalUnit' },
    { id: 'ou', value: '技术部' }
])

// 在 OU=技术部 下创建一个用户
connection.createEntry('CN=80818,OU=技术部,dc=informat,dc=cn', [
    { id: 'objectClass', value: 'top' },
    { id: 'objectClass', value: 'person' },
    { id: 'objectClass', value: 'organizationalPerson' },
    { id: 'objectClass', value: 'user' },
    { id: 'cn', value: '80818' },
    { id: 'sAMAccountName', value: '80818' },
    { id: 'displayName', value: '林文雅' },
    { id: 'userAccountControl', value: '514' } // 先以禁用方式建好，后续再设密码并启用
])
```

### deleteEntry

删除一个 LDAP 条目（subcontext）。

```javascript
connection.deleteEntry(dn)
```

| 参数 | 类型       | 描述               |
|----|----------|------------------|
| dn | `String` | 要删除的条目的完整路径      |

**返回值**

无返回值。若条目下还有子条目，AD 会返回错误（LDAP error code 66 - NotAllowedOnNonLeaf），需要先递归删除其所有子节点。

**示例**

```js
// 删除一个空的 OU
connection.deleteEntry('OU=废弃部门,dc=informat,dc=cn')

// 删除一个用户
connection.deleteEntry('CN=80818,OU=技术部,dc=informat,dc=cn')
```

### renameEntry

重命名或移动 LDAP 条目（对应 LDAP 的 ModifyDN 操作）。

- 仅改 RDN（如 `CN=80818` → `CN=80927`）：父容器不变，相当于"改名"
- 跨容器移动（`oldDn` 与 `newDn` 的父级 DN 不同）：相当于"移动"

```javascript
connection.renameEntry(oldDn, newDn)
```

| 参数    | 类型       | 描述         |
|-------|----------|------------|
| oldDn | `String` | 当前条目的完整路径  |
| newDn | `String` | 目标条目的完整路径  |

**返回值**

无返回值。若 `oldDn` 不存在、`newDn` 已被占用，或 LDAP 服务器禁止跨容器移动，会抛出异常。

**示例**

```js
// 重命名：用户工号变更（同部门）
connection.renameEntry(
    'CN=80818,OU=技术部,dc=informat,dc=cn',
    'CN=80927,OU=技术部,dc=informat,dc=cn'
)

// 移动：用户调岗到另一个部门
connection.renameEntry(
    'CN=80818,OU=技术部,dc=informat,dc=cn',
    'CN=80818,OU=产品部,dc=informat,dc=cn'
)

// 部门改名：OU 重命名
connection.renameEntry(
    'OU=技术部,dc=informat,dc=cn',
    'OU=研发部,dc=informat,dc=cn'
)
```

### addAttributeEncoded

新增属性到指定节点，属性的 `value` 会先按 `encoding` 编码成字节流再写入。

用于写入需要特殊字节编码的属性。最典型的场景是 Windows AD 的 `unicodePwd`（要求 UTF-16LE 字节流，且密码两端必须包含 ASCII 双引号）。

```javascript
connection.addAttributeEncoded(dn, attributes, encoding)
```

| 参数         | 类型                                                                       | 描述                                                                                                          |
|------------|--------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
| dn         | `String`                                                                 | 要修改属性的节点的路径                                                                                                 |
| attributes | Array<[LdapModifyAttribute](/guide/script/model.md#ldapmodifyattribute)> | 属性列表，`value` 为字符串                                                                                           |
| encoding   | `String`                                                                 | 字符集名（如 `"UTF-16LE"`、`"UTF-8"`、`"GBK"`），必须是 Java 支持的 `Charset` 名。传 `null` 或空字符串时退化为普通 `addAttribute`，按字符串写入 |

**返回值**

无返回值。如果节点不存在、编码不被支持或属性不合法，会抛出异常。

**示例**

```js
// 在用户节点上新增一个需要 UTF-16LE 编码的属性
connection.addAttributeEncoded('CN=80818,OU=技术部,dc=informat,dc=cn', [
    { id: 'someBinaryAttr', value: '中文值' }
], 'UTF-16LE')
```

### updateAttributeEncoded

更新指定节点的属性（REPLACE 语义），`value` 按 `encoding` 编码成字节流后写入。

最常用的场景：**为 Windows AD 用户设置密码 `unicodePwd`**。

```javascript
connection.updateAttributeEncoded(dn, attributes, encoding)
```

| 参数         | 类型                                                                       | 描述                                                                                                             |
|------------|--------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
| dn         | `String`                                                                 | 要修改属性的节点的路径                                                                                                    |
| attributes | Array<[LdapModifyAttribute](/guide/script/model.md#ldapmodifyattribute)> | 属性列表,`value` 为字符串                                                                                              |
| encoding   | `String`                                                                 | 字符集名（如 `"UTF-16LE"`、`"UTF-8"`、`"GBK"`），必须是 Java 支持的 `Charset` 名。传 `null` 或空字符串时退化为普通 `updateAttribute`，按字符串写入 |

**返回值**

无返回值。如果节点不存在、编码不被支持，或服务器拒绝该属性的修改（如 AD 拒绝在明文 LDAP 通道上设置密码），会抛出异常。

**示例**

```js
// 设置 Windows AD 用户密码（必须通过 LDAPS 636 端口调用）
const password = 'P@ssw0rd!'
connection.updateAttributeEncoded(
    'CN=80818,OU=技术部,dc=informat,dc=cn',
    [{ id: 'unicodePwd', value: `"${password}"` }],   // 密码两端必须加 ASCII 双引号
    'UTF-16LE'
)
```

::: tip
AD 写入 `unicodePwd` 必须通过 LDAPS（636 端口），明文 LDAP 389 端口下 AD 会拒绝并返回 `WILL_NOT_PERFORM`。
:::

### deleteAttributeEncoded

删除指定节点的属性，匹配按 `encoding` 编码后的字节值。

```javascript
connection.deleteAttributeEncoded(dn, attributes, encoding)
```

| 参数         | 类型                                                                       | 描述                                                                                                             |
|------------|--------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
| dn         | `String`                                                                 | 要修改属性的节点的路径                                                                                                    |
| attributes | Array<[LdapModifyAttribute](/guide/script/model.md#ldapmodifyattribute)> | 属性列表，`value` 为字符串                                                                                              |
| encoding   | `String`                                                                 | 字符集名（如 `"UTF-16LE"`、`"UTF-8"`、`"GBK"`），必须是 Java 支持的 `Charset` 名。传 `null` 或空字符串时退化为普通 `deleteAttribute`，按字符串匹配 |

**返回值**

无返回值。

**示例**

```js
// 删除按 UTF-16LE 编码写入的某个属性值
connection.deleteAttributeEncoded('CN=80818,OU=技术部,dc=informat,dc=cn', [
    { id: 'someBinaryAttr', value: '中文值' }
], 'UTF-16LE')
```
