# Query a real local LDAP directory

> Query an isolated OpenLDAP instance and verify the bind result in total5 0.0.19.

Framework: total5
Product: Total.js 5 (total5@0.0.19)
Language: en
Content-SHA256: acf740f174412263663ffdc967c13b624ab592c51d4cde00e0cd491fb1103357
Snapshot: c0700e321579725c5cf4f5ace1ad7d2866d1b4f93c24a63c0b41b6b7998e976c
Sources: total5-installed@total5@0.0.19
Review: not-recorded

`LDAP(options, callback)` is the native read client. `options.ldap` supplies `host` and `port`; `user` is the bind DN, `password` its password, `dn` the search base, and `type` the requested object class (`person` or `group`). `type:'profile'` uses `login` to search `sAMAccountName` and returns one object or `null`. `noauth:true` skips bind; it does not grant directory permissions. Always specify a search base explicitly.

`attributes` maps returned attribute names to conversion types (`string`, `number`, `date`, `buffer`, `hex`, `base64`, etc.); it is **not** an allowlist of attributes to retrieve. A directory may return scalar values or arrays when an attribute repeats. Restrict the data your HTTP API exposes.

This executable fixture requires local **OpenLDAP** tools: `slapd`, `ldapadd` and the core/cosine/inetorgperson schema files. On macOS the defaults below use the system tools; other installations can set `FORGE_SLAPD`, `FORGE_LDAPADD` and `FORGE_LDAP_SCHEMA`. It runs a separate unprivileged loopback server and temporary LDIF database, then terminates it. It never uses an existing directory. The test covers search, multiple attribute values and an invalid-bind boundary; it does not certify Active Directory compatibility, TLS/StartTLS, groups or reconnect behavior. Never send real credentials over an unprotected network.

**Decide authentication from the bind.** Check the result of the bind in your own code, and treat a returned profile as authenticated only after the bind has succeeded.

```js controllers/directory.js
exports.install = function() {
	ROUTE('POST /directory-fixture/ @json', directory);
};
function directory($) {
	var port = Number($.body.port);
	if (!Number.isInteger(port) || port < 1024 || port > 65535)
		return $.invalid(400);
	LDAP({
		ldap: { host: '127.0.0.1', port: port },
		user: 'cn=admin,dc=forge,dc=test',
		password: $.body.wrong ? 'incorrect' : 'local-fixture-only',
		dn: 'dc=forge,dc=test',
		type: 'person',
		attributes: { cn: 'string', mail: 'string' }
	}, function(err, people) {
		if (err)
			$.json({ failed: true });
		else
			$.json({ people: people.map(person => ({ cn: person.cn, mail: person.mail })) });
	});
}
```
