ldap-rest 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abstract/ldapFlat.js +1 -1
- package/dist/abstract/plugin.js +1 -1
- package/dist/bin/index.js +1 -1
- package/dist/config/args.js +1 -1
- package/dist/config/schema.js +1 -0
- package/dist/lib/expressFormatedResponses.js +1 -1
- package/dist/lib/ldapActions.js +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/plugins/auth/authzScope.js +1 -0
- package/dist/plugins/auth/llng.js +1 -1
- package/dist/plugins/ldap/accountLifecycle.js +1 -0
- package/dist/plugins/ldap/bulkImport.js +1 -1
- package/dist/plugins/ldap/departmentSync.js +1 -1
- package/dist/plugins/ldap/enterpriseRules.js +1 -0
- package/dist/plugins/ldap/externalUsersInGroups.js +1 -1
- package/dist/plugins/ldap/flatGeneric.js +1 -1
- package/dist/plugins/ldap/groups.js +1 -1
- package/dist/plugins/ldap/organizations.js +1 -1
- package/dist/plugins/scim/groups.js +1 -1
- package/dist/plugins/twake/calendar.js +1 -0
- package/dist/plugins/twake/calendarResources.js +1 -1
- package/dist/plugins/twake/james.js +1 -1
- package/dist/src/abstract/ldapFlat.d.ts +159 -1
- package/dist/src/abstract/plugin.d.ts +13 -0
- package/dist/src/config/args.d.ts +2 -0
- package/dist/src/config/schema.d.ts +382 -12
- package/dist/src/lib/expressFormatedResponses.d.ts +14 -0
- package/dist/src/lib/ldapActions.d.ts +33 -6
- package/dist/src/lib/utils.d.ts +65 -0
- package/dist/src/plugins/auth/authzScope.d.ts +53 -0
- package/dist/src/plugins/auth/llng.d.ts +30 -1
- package/dist/src/plugins/ldap/accountLifecycle.d.ts +56 -0
- package/dist/src/plugins/ldap/departmentSync.d.ts +43 -0
- package/dist/src/plugins/ldap/enterpriseRules.d.ts +436 -0
- package/dist/src/plugins/ldap/flatGeneric.d.ts +29 -0
- package/dist/src/plugins/ldap/groups.d.ts +22 -0
- package/dist/src/plugins/ldap/organizations.d.ts +25 -4
- package/dist/src/plugins/twake/calendar.d.ts +113 -0
- package/dist/src/plugins/twake/calendarResources.d.ts +9 -58
- package/docs/README.md +1 -0
- package/docs/client-development/api/openapi.md +1 -1
- package/docs/client-development/browser/libraries.md +1 -1
- package/docs/plugin-development/dependencies.md +10 -7
- package/docs/presentations/en.md +1 -1
- package/docs/presentations/fr.md +1 -1
- package/docs/usage/README.md +1 -0
- package/docs/usage/configuration.md +35 -15
- package/docs/usage/directory-audit.md +105 -0
- package/docs/usage/plugins/README.md +6 -6
- package/docs/usage/plugins/auth/README.md +6 -5
- package/docs/usage/plugins/auth/authz-scope.md +103 -0
- package/docs/usage/plugins/auth/llng.md +17 -0
- package/docs/usage/plugins/integrations/README.md +5 -5
- package/docs/usage/plugins/integrations/{calendar-resources.md → calendar.md} +70 -13
- package/docs/usage/plugins/ldap/README.md +13 -11
- package/docs/usage/plugins/ldap/account-lifecycle.md +129 -0
- package/docs/usage/plugins/ldap/bulk-import.md +8 -1
- package/docs/usage/plugins/ldap/enterprise-rules.md +167 -0
- package/docs/usage/plugins/ldap/flat-generic.md +172 -0
- package/docs/usage/plugins/ldap/groups.md +10 -4
- package/docs/usage/plugins/ldap/organizations.md +59 -33
- package/docs/usage/upgrading.md +322 -0
- package/package.json +50 -5
- package/static/schemas/example/README.md +36 -0
- package/static/schemas/example/groups.json +94 -0
- package/static/schemas/example/nomenclature/domains.json +49 -0
- package/static/schemas/example/organizations.json +106 -0
- package/static/schemas/example/positions.json +35 -0
- package/static/schemas/example/users.json +246 -0
- package/static/schemas/twake/groups.json +81 -15
- package/static/schemas/twake/nomenclature/twakeAccountStatus.json +26 -0
- package/static/schemas/twake/nomenclature/twakeDeliveryMode.json +22 -0
- package/static/schemas/twake/nomenclature/twakeDomain.json +60 -0
- package/static/schemas/twake/nomenclature/twakeListType.json +22 -0
- package/static/schemas/twake/nomenclature/twakeMailboxType.json +22 -0
- package/static/schemas/twake/nomenclature/twakeTitle.json +42 -0
- package/static/schemas/twake/organizations.json +92 -12
- package/static/schemas/twake/positions.json +22 -4
- package/static/schemas/twake/users.json +269 -32
|
@@ -11,10 +11,10 @@ Plugins for connecting LDAP-Rest to external systems.
|
|
|
11
11
|
|
|
12
12
|
## Twake
|
|
13
13
|
|
|
14
|
-
| Plugin
|
|
15
|
-
|
|
|
16
|
-
| [calendar
|
|
17
|
-
| [app-accounts](app-accounts.md)
|
|
14
|
+
| Plugin | Description |
|
|
15
|
+
| ------------------------------- | ---------------------------------------- |
|
|
16
|
+
| [calendar](calendar.md) | Calendar resources and users sync |
|
|
17
|
+
| [app-accounts](app-accounts.md) | Applicative accounts API (devices, apps) |
|
|
18
18
|
|
|
19
19
|
## Prerequisites
|
|
20
20
|
|
|
@@ -37,6 +37,6 @@ James plugins require:
|
|
|
37
37
|
core/twake/james
|
|
38
38
|
└─ requires: core/ldap/onChange
|
|
39
39
|
|
|
40
|
-
core/twake/
|
|
40
|
+
core/twake/calendar
|
|
41
41
|
└─ requires: core/ldap/onChange
|
|
42
42
|
```
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
# Twake Calendar
|
|
1
|
+
# Twake Calendar Plugin
|
|
2
2
|
|
|
3
|
-
Plugin to
|
|
3
|
+
Plugin to keep Twake Calendar in sync with LDAP via its WebAdmin API:
|
|
4
|
+
|
|
5
|
+
- resources (meeting rooms, equipment, etc.) stored in an LDAP branch
|
|
6
|
+
- registered users, whose email, first name and last name follow LDAP
|
|
7
|
+
|
|
8
|
+
> Formerly `core/twake/calendarResources`. That name still loads the plugin,
|
|
9
|
+
> registered as `calendarResources`, but logs a deprecation warning and will
|
|
10
|
+
> be removed in a future major release.
|
|
4
11
|
|
|
5
12
|
## Features
|
|
6
13
|
|
|
@@ -8,6 +15,7 @@ Plugin to automatically synchronize LDAP resources (meeting rooms, equipment, et
|
|
|
8
15
|
- **Automatic Updates**: When a resource is modified in LDAP, changes are synced to Twake Calendar
|
|
9
16
|
- **Automatic Deletion**: When a resource is removed from LDAP, it's deleted from Twake Calendar
|
|
10
17
|
- **Flexible Configuration**: Configure which LDAP branch and objectClass to monitor
|
|
18
|
+
- **User Identity Propagation**: When a user's email, first name or last name changes in LDAP, the matching Calendar registered user is updated
|
|
11
19
|
|
|
12
20
|
## Configuration
|
|
13
21
|
|
|
@@ -15,10 +23,15 @@ Plugin to automatically synchronize LDAP resources (meeting rooms, equipment, et
|
|
|
15
23
|
|
|
16
24
|
- `DM_CALENDAR_WEBADMIN_URL`: URL of the Twake Calendar WebAdmin API (default: `http://localhost:8080`)
|
|
17
25
|
- `DM_CALENDAR_WEBADMIN_TOKEN`: Bearer token for WebAdmin API authentication
|
|
18
|
-
- `DM_CALENDAR_RESOURCE_BASE`: LDAP branch to monitor for resources (e.g., `ou=resources,dc=example,dc=com`)
|
|
19
|
-
- `DM_CALENDAR_RESOURCE_OBJECTCLASS`: Optional objectClass filter
|
|
26
|
+
- `DM_CALENDAR_RESOURCE_BASE`: LDAP branch to monitor for resources, as a full DN (e.g., `ou=resources,dc=example,dc=com`). It is compared to an entry's DN part by part, so a partial value such as `ou=resources` names no branch and matches nothing (the plugin warns at startup when the value is not a DN of the directory).
|
|
27
|
+
- `DM_CALENDAR_RESOURCE_OBJECTCLASS`: Optional objectClass filter, applied when a resource is **created** (see [Resource identity](#resource-identity))
|
|
20
28
|
- `DM_CALENDAR_RESOURCE_CREATOR`: Default creator email for resources (default: `admin@example.com`)
|
|
21
29
|
- `DM_CALENDAR_RESOURCE_DOMAIN`: Default domain for resources (extracted from DN if not specified)
|
|
30
|
+
- `DM_CALENDAR_FIRSTNAME_ATTRIBUTE`: LDAP attribute holding a user's first name (default: `givenName`)
|
|
31
|
+
- `DM_CALENDAR_LASTNAME_ATTRIBUTE`: LDAP attribute holding a user's last name (default: `sn`)
|
|
32
|
+
|
|
33
|
+
The resource variables are only needed for resource synchronization; user
|
|
34
|
+
identity propagation only needs the WebAdmin URL and token.
|
|
22
35
|
|
|
23
36
|
### Required Dependencies
|
|
24
37
|
|
|
@@ -66,6 +79,49 @@ The plugin uses the following WebAdmin API endpoints:
|
|
|
66
79
|
- `POST /resources` - Create a new resource
|
|
67
80
|
- `PATCH /resources/{id}` - Update an existing resource
|
|
68
81
|
- `DELETE /resources/{id}` - Delete a resource
|
|
82
|
+
- `GET /registeredUsers?email={mail}` - Find the registered user to update
|
|
83
|
+
- `PATCH /registeredUsers?id={id}` - Update a registered user's email, first and last name
|
|
84
|
+
- `POST /users/{mail}?action=deleteData` - Delete a user's data (see `deleteUserData`)
|
|
85
|
+
|
|
86
|
+
The resource id goes into the path percent-encoded, so a `/` or a `#` in it
|
|
87
|
+
cannot build a path naming something else. The user's address is left as it is
|
|
88
|
+
written — `user@example.com`, `@` being legal in a path segment — which is what
|
|
89
|
+
`plugins/twake/james` does with the same WebAdmin; whether the two should
|
|
90
|
+
encode it is issue #175.
|
|
91
|
+
|
|
92
|
+
### Registered Users
|
|
93
|
+
|
|
94
|
+
A change of the configured mail attribute updates the registered user found by
|
|
95
|
+
its **previous** address; a change of the first or last name attribute updates
|
|
96
|
+
the one found by its current address. In both cases the email, first name and
|
|
97
|
+
last name are re-read from LDAP and sent together. Adding or removing the mail
|
|
98
|
+
attribute, and users not registered in Calendar, are skipped.
|
|
99
|
+
|
|
100
|
+
Calendar lower-cases the address before looking it up, which is also how it
|
|
101
|
+
stores addresses, so a case difference between LDAP and Calendar does not
|
|
102
|
+
matter. The exception is a legacy record whose stored address kept upper case
|
|
103
|
+
(written without Calendar's normalisation, e.g. migrated data): it is not
|
|
104
|
+
found, and its user is logged as not registered. Calendar releases before
|
|
105
|
+
1.0.0.1 ignore the `email` parameter and answer the full list of registered
|
|
106
|
+
users; the plugin then picks the user from it, which works but costs a full
|
|
107
|
+
listing per change.
|
|
108
|
+
|
|
109
|
+
### Resource identity
|
|
110
|
+
|
|
111
|
+
A resource is known to Calendar by an `id`, which is the value of its LDAP
|
|
112
|
+
entry's own RDN with the escapes removed: `cn=Meeting Room 1,ou=resources,…`
|
|
113
|
+
gives `Meeting Room 1`, and `o=Room 12,ou=resources,…` gives `Room 12`. The
|
|
114
|
+
creation, the update and the deletion hooks all read it that way, so the three
|
|
115
|
+
name the same resource; an entry whose DN yields no value is not synchronised.
|
|
116
|
+
|
|
117
|
+
Only entries in `DM_CALENDAR_RESOURCE_BASE` are synchronised, on all three
|
|
118
|
+
hooks. `DM_CALENDAR_RESOURCE_OBJECTCLASS` filters creations only: a
|
|
119
|
+
modification carries the attributes that changed, so an untouched objectClass
|
|
120
|
+
is not there to compare, and a deleted entry cannot be read any more. The
|
|
121
|
+
branch is the guard that remains on those two paths.
|
|
122
|
+
|
|
123
|
+
Renaming an entry changes its RDN, hence its id: Calendar is not told, and the
|
|
124
|
+
resource keeps its former id there.
|
|
69
125
|
|
|
70
126
|
### Resource Data Format
|
|
71
127
|
|
|
@@ -103,7 +159,7 @@ DM_LDAP_FLAT_SCHEMA="/path/to/calendar-resources-schema.json"
|
|
|
103
159
|
3. Add the plugin to `DM_PLUGINS`:
|
|
104
160
|
|
|
105
161
|
```bash
|
|
106
|
-
DM_PLUGINS="core/ldap/onChange,core/ldap/flatGeneric,twake/
|
|
162
|
+
DM_PLUGINS="core/ldap/onChange,core/ldap/flatGeneric,twake/calendar"
|
|
107
163
|
```
|
|
108
164
|
|
|
109
165
|
4. Start ldap-rest - resources will be automatically synced
|
|
@@ -121,7 +177,7 @@ Example log:
|
|
|
121
177
|
|
|
122
178
|
```json
|
|
123
179
|
{
|
|
124
|
-
"plugin": "
|
|
180
|
+
"plugin": "calendar",
|
|
125
181
|
"event": "ldapcalendarResourceadddone",
|
|
126
182
|
"result": "success",
|
|
127
183
|
"http_status": 201,
|
|
@@ -138,6 +194,7 @@ Example log:
|
|
|
138
194
|
2. Verify that `DM_CALENDAR_WEBADMIN_TOKEN` is valid
|
|
139
195
|
3. Check logs for API errors
|
|
140
196
|
4. Ensure the ldapFlat schema has `entity.name` set to `calendarResource`
|
|
197
|
+
5. Ensure `DM_CALENDAR_RESOURCE_BASE` is the full DN of the branch the entries are in: a partial value matches no entry, and the plugin says so at startup
|
|
141
198
|
|
|
142
199
|
### Authentication errors
|
|
143
200
|
|
|
@@ -145,22 +202,21 @@ Ensure `DM_CALENDAR_WEBADMIN_TOKEN` is set and valid. The token is sent as a Bea
|
|
|
145
202
|
|
|
146
203
|
### Wrong resources being synced
|
|
147
204
|
|
|
148
|
-
Use `DM_CALENDAR_RESOURCE_BASE` and `DM_CALENDAR_RESOURCE_OBJECTCLASS` to filter which LDAP entries are considered resources.
|
|
205
|
+
Use `DM_CALENDAR_RESOURCE_BASE` and `DM_CALENDAR_RESOURCE_OBJECTCLASS` to filter which LDAP entries are considered resources. Note that the objectClass filter only applies to creations, so an entry of the resource entity that must never reach Calendar has to sit outside the branch.
|
|
149
206
|
|
|
150
207
|
## Public Methods
|
|
151
208
|
|
|
152
|
-
Public methods available on the
|
|
209
|
+
Public methods available on the Calendar plugin instance.
|
|
153
210
|
|
|
154
211
|
### Usage
|
|
155
212
|
|
|
156
213
|
```typescript
|
|
157
214
|
import type { DM } from 'ldap-rest';
|
|
158
|
-
import type
|
|
215
|
+
import type Calendar from 'ldap-rest/plugin-twake-calendar';
|
|
159
216
|
|
|
160
217
|
// Get the plugin instance from another plugin
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
) as CalendarResources;
|
|
218
|
+
// (declare `dependencies = { calendar: 'core/twake/calendar' }` so it loads first)
|
|
219
|
+
const calendar = this.requirePlugin<Calendar>('calendar');
|
|
164
220
|
```
|
|
165
221
|
|
|
166
222
|
### deleteUserData(mail)
|
|
@@ -182,7 +238,8 @@ async deleteUserData(mail: string): Promise<{ taskId: string } | null>
|
|
|
182
238
|
**Example:**
|
|
183
239
|
|
|
184
240
|
```typescript
|
|
185
|
-
|
|
241
|
+
// requirePlugin() returns null when the plugin is not loaded
|
|
242
|
+
const result = await calendar?.deleteUserData('user@example.com');
|
|
186
243
|
if (result) {
|
|
187
244
|
console.log(`Deletion task started: ${result.taskId}`);
|
|
188
245
|
}
|
|
@@ -4,17 +4,19 @@ Plugins for LDAP entity management.
|
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
| Plugin
|
|
8
|
-
|
|
|
9
|
-
| [flat-generic](flat-generic.md)
|
|
10
|
-
| [groups](groups.md)
|
|
11
|
-
| [organizations](organizations.md)
|
|
12
|
-
| [bulk-import](bulk-import.md)
|
|
13
|
-
| [trash](trash.md)
|
|
14
|
-
| [external-users](external-users.md)
|
|
15
|
-
| [on-change](on-change.md)
|
|
16
|
-
| [
|
|
17
|
-
| [
|
|
7
|
+
| Plugin | Entity | Features |
|
|
8
|
+
| ----------------------------------------- | ------------------------ | --------------------------------------- |
|
|
9
|
+
| [flat-generic](flat-generic.md) | Users, Positions, Custom | Schema-driven, Validation, Pointers |
|
|
10
|
+
| [groups](groups.md) | Groups | Member validation, Nested groups |
|
|
11
|
+
| [organizations](organizations.md) | Organizational Units | Tree navigation, Search |
|
|
12
|
+
| [bulk-import](bulk-import.md) | All (bulk operations) | CSV import, Template generation |
|
|
13
|
+
| [trash](trash.md) | All (soft delete) | Trash system, Recovery |
|
|
14
|
+
| [external-users](external-users.md) | Contacts | Automatic creation |
|
|
15
|
+
| [on-change](on-change.md) | All | Change detection |
|
|
16
|
+
| [enterprise-rules](enterprise-rules.md) | All (business rules) | Uniqueness, Mail domains, Delete guards |
|
|
17
|
+
| [account-lifecycle](account-lifecycle.md) | Accounts | Enable/disable, Password reset |
|
|
18
|
+
| [password-policy](password-policy.md) | Password management | Expiration, Lockout, Unlock |
|
|
19
|
+
| [raw](raw.md) | Whole directory (read) | Root DSE, Schema, Tree, Raw search |
|
|
18
20
|
|
|
19
21
|
## API Endpoints
|
|
20
22
|
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# LDAP Account Lifecycle Plugin
|
|
2
|
+
|
|
3
|
+
The two account operations an administrator performs most often — change an
|
|
4
|
+
account's state, and reset its password — as first-class endpoints.
|
|
5
|
+
|
|
6
|
+
Both are expressed against semantic roles, never attribute names. What
|
|
7
|
+
"disabled" _is_ in a given directory — a DN in a nomenclature, a string, a
|
|
8
|
+
boolean — is schema configuration.
|
|
9
|
+
|
|
10
|
+
## Configuration
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
--plugin core/ldap/accountLifecycle
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The plugin loads `core/ldap/flatGeneric` if it is not already there, and adds
|
|
17
|
+
endpoints to each entity whose schema asks for them:
|
|
18
|
+
|
|
19
|
+
- `POST /api/v1/ldap/{pluralName}/{id}/status` when the schema declares an
|
|
20
|
+
`accountStatus` attribute carrying named `states`
|
|
21
|
+
- `POST /api/v1/ldap/{pluralName}/{id}/password` when it declares a `password`
|
|
22
|
+
attribute
|
|
23
|
+
|
|
24
|
+
Changes go through the entity's own modify path, so schema validation, hooks
|
|
25
|
+
and authorization apply exactly as they do to any other update.
|
|
26
|
+
|
|
27
|
+
## Declaring the states
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"twakeAccountStatus": {
|
|
32
|
+
"type": "pointer",
|
|
33
|
+
"branch": ["ou=twakeAccountStatus,ou=nomenclature,dc=example,dc=com"],
|
|
34
|
+
"role": "accountStatus",
|
|
35
|
+
"generated": true,
|
|
36
|
+
"default": "cn=active,ou=twakeAccountStatus,ou=nomenclature,dc=example,dc=com",
|
|
37
|
+
"states": {
|
|
38
|
+
"enabled": "cn=active,ou=twakeAccountStatus,ou=nomenclature,dc=example,dc=com",
|
|
39
|
+
"disabled": "cn=disabled,ou=twakeAccountStatus,ou=nomenclature,dc=example,dc=com",
|
|
40
|
+
"noAccess": "cn=noaccess,ou=twakeAccountStatus,ou=nomenclature,dc=example,dc=com",
|
|
41
|
+
"toDelete": "cn=todelete,ou=twakeAccountStatus,ou=nomenclature,dc=example,dc=com"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`enabled` and `disabled` are the names the API speaks; a deployment adds its
|
|
48
|
+
own freely. `default` is what a new account gets, applied by
|
|
49
|
+
[enterprise-rules](enterprise-rules.md).
|
|
50
|
+
|
|
51
|
+
## Changing the state
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
curl -X POST http://localhost:8081/api/v1/ldap/users/alice/status \
|
|
55
|
+
-H 'Content-Type: application/json' \
|
|
56
|
+
-d '{"state": "disabled"}'
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{ "success": true, "state": "disabled" }
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
An unknown state is refused with a `400` that names the ones this directory
|
|
64
|
+
knows, so a client never has to hardcode the list:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"error": "Unknown state \"retired\" for user; known states: enabled, disabled, noAccess, toDelete"
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Resetting the password
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
curl -X POST http://localhost:8081/api/v1/ldap/users/alice/password \
|
|
76
|
+
-H 'Content-Type: application/json' -d '{}'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{ "success": true, "generated": true, "forceChange": true, "password": "xY3k…" }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
With no `password` in the body, one is generated and returned **once** — the
|
|
84
|
+
credential attributes are marked `neverReturn` in the schema precisely so a
|
|
85
|
+
later read cannot hand it back. Supply `password` to set a chosen one; the
|
|
86
|
+
answer then carries `"generated": false` and does not echo it.
|
|
87
|
+
|
|
88
|
+
A `password` that is present has to be a non-empty string. An empty one — or
|
|
89
|
+
`null`, or anything that is not text — is refused with a `400`:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"error": "Field password must be a non-empty string; omit it to have one generated"
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
It used to be taken as an absent one, so `{"password": ""}` answered
|
|
98
|
+
`success` after setting a random credential the caller never saw: a client
|
|
99
|
+
that emptied the field by accident believed it had set the password it typed.
|
|
100
|
+
Omitting the field is how a generated password is asked for.
|
|
101
|
+
|
|
102
|
+
`forceChange` (true by default) also writes the `passwordReset`-role attribute
|
|
103
|
+
so the directory asks for a new password at next login. For a boolean
|
|
104
|
+
attribute the values written are `TRUE` and `FALSE`; a directory that spells
|
|
105
|
+
them otherwise says so through `states.required` and `states.cleared`.
|
|
106
|
+
|
|
107
|
+
| Field | Meaning |
|
|
108
|
+
| ------------- | --------------------------------------------------- |
|
|
109
|
+
| `generated` | The server chose the password |
|
|
110
|
+
| `password` | Present only when generated |
|
|
111
|
+
| `forceChange` | Whether the "change at next login" flag was written |
|
|
112
|
+
|
|
113
|
+
> **Note** — `pwdReset` is an operational attribute of the OpenLDAP `ppolicy`
|
|
114
|
+
> overlay. Writing it requires the overlay to be loaded; without it the
|
|
115
|
+
> directory answers `attribute type undefined`.
|
|
116
|
+
|
|
117
|
+
## Errors
|
|
118
|
+
|
|
119
|
+
| Status | When |
|
|
120
|
+
| ------ | -------------------------------------------------------------- |
|
|
121
|
+
| `400` | Unknown state, or a body with no `state` |
|
|
122
|
+
| `400` | A `password` that is present but empty, `null` or not a string |
|
|
123
|
+
| `404` | No such account |
|
|
124
|
+
|
|
125
|
+
## See also
|
|
126
|
+
|
|
127
|
+
- [flat-generic](flat-generic.md) — `role`, `states` and `neverReturn`
|
|
128
|
+
- [enterprise-rules](enterprise-rules.md) — the default state of a new account
|
|
129
|
+
- [password-policy](password-policy.md) — expiry, lockout and complexity
|
|
@@ -137,6 +137,13 @@ jdoe,John Doe,Doe,John,john.doe@example.com,password123,ou=IT,ou=organization,dc
|
|
|
137
137
|
asmith,Alice Smith,Smith,Alice,alice.smith@example.com,secret456,ou=HR,ou=organization,dc=example,dc=com
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
+
A value must sit under a column whose name is a syntactically valid LDAP
|
|
141
|
+
attribute name (`mail`, `2.5.4.3`, `cn;lang-fr`). Otherwise its line fails
|
|
142
|
+
with `Invalid column name` and is counted in `failed`. The check is on syntax
|
|
143
|
+
only: an unknown attribute such as `maill` passes it and fails on the
|
|
144
|
+
directory side. Empty cells are ignored whatever their column is called, so a
|
|
145
|
+
comment column left empty, or a trailing comma, does not prevent the import.
|
|
146
|
+
|
|
140
147
|
### Special Column: `organizationDn`
|
|
141
148
|
|
|
142
149
|
The `organizationDn` column is special and triggers automatic calculation of:
|
|
@@ -289,7 +296,7 @@ mail: john.doe@example.com
|
|
|
289
296
|
userPassword: {SSHA}...
|
|
290
297
|
telephoneNumber: +1-555-0100
|
|
291
298
|
twakeDepartmentLink: ou=Engineering,ou=organization,dc=example,dc=com
|
|
292
|
-
twakeDepartmentPath: Engineering
|
|
299
|
+
twakeDepartmentPath: Engineering
|
|
293
300
|
```
|
|
294
301
|
|
|
295
302
|
## Operation Modes
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# LDAP Enterprise Rules Plugin
|
|
2
|
+
|
|
3
|
+
The business rules an enterprise directory needs on top of per-attribute
|
|
4
|
+
validation: uniqueness across a shared namespace, mail addresses confined to
|
|
5
|
+
the domains an organization owns, computed organization paths, normalised
|
|
6
|
+
quotas, and referential-integrity guards on deletion.
|
|
7
|
+
|
|
8
|
+
Every rule is driven by the loaded entity schemas. The plugin contains no
|
|
9
|
+
attribute name, no domain and no nomenclature value, so the same code serves a
|
|
10
|
+
directory laid out differently without a change.
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
--plugin core/ldap/enterpriseRules
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
It reads the schemas already loaded by `core/ldap/flatGeneric`,
|
|
19
|
+
`core/ldap/groups` and `core/ldap/organizations`, so load it alongside them.
|
|
20
|
+
The organizations plugin only exposes its schema when one is given:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
--organization-schema ./static/schemas/twake/organizations.json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
| Option | Default | Purpose |
|
|
27
|
+
| ------------------------------------ | ------------------- | ------------------------------------------------------------------- |
|
|
28
|
+
| `--enterprise-domain-name-attribute` | `associatedDomain` | Fallback when no loaded schema declares a `domainName` role |
|
|
29
|
+
| `--enterprise-domain-link-attribute` | _(from the schema)_ | Fallback when the organization schema declares no `domainLink` role |
|
|
30
|
+
| `--group-dummy-user` | `cn=fakeuser` | Placeholder member that does not count towards `deleteGuard` |
|
|
31
|
+
|
|
32
|
+
## Order of evaluation
|
|
33
|
+
|
|
34
|
+
Checks run **before** transformations, inside the same hook. A check therefore
|
|
35
|
+
always sees the payload as the client sent it: uniqueness and domain rules
|
|
36
|
+
validate the address that was submitted, never one the server has already
|
|
37
|
+
rewritten, and an identifier derived from that address cannot influence the
|
|
38
|
+
check that justified it.
|
|
39
|
+
|
|
40
|
+
## Rules
|
|
41
|
+
|
|
42
|
+
### Uniqueness
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"employeeNumber": { "type": "string", "unique": true },
|
|
47
|
+
"mail": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"role": "primaryEmail",
|
|
50
|
+
"unique": {
|
|
51
|
+
"attributes": ["mailAlternateAddress"],
|
|
52
|
+
"branches": ["dc=example,dc=com"]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- `attributes` names the other attributes sharing the value namespace — a mail
|
|
59
|
+
address must not already be someone's alias.
|
|
60
|
+
- `branches` widens the search beyond the entity's own branch, which is what
|
|
61
|
+
makes an address unique across accounts _and_ distribution lists.
|
|
62
|
+
- `sentinel` exempts one value, for directories that use a placeholder on
|
|
63
|
+
non-individual entries.
|
|
64
|
+
- `filter` narrows the search, e.g. `(objectClass=twakeAccount)`.
|
|
65
|
+
|
|
66
|
+
On an update the entry being modified is excluded from the search, so
|
|
67
|
+
re-sending an unchanged value never conflicts with itself.
|
|
68
|
+
|
|
69
|
+
### Mail domains
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mail": {
|
|
74
|
+
"mailDomainScope": "organization",
|
|
75
|
+
"allowSubdomains": true
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`organization` walks up from the entry's `organizationLink`, collecting the
|
|
81
|
+
domains each ancestor declares through its `domainLink` role, up to the root of
|
|
82
|
+
the tree. `directory` accepts any domain declared anywhere. When nobody
|
|
83
|
+
declares a domain, no restriction applies; a domain entry whose name is `*`
|
|
84
|
+
authorises every domain.
|
|
85
|
+
|
|
86
|
+
The host is taken after the **last** `@`, so a domain that merely ends with an
|
|
87
|
+
authorised one (`evil-example.org` against `example.org`) is refused.
|
|
88
|
+
`allowSubdomains` is what lets a distribution list live at
|
|
89
|
+
`all@lists.example.org` under an organization that owns `example.org`.
|
|
90
|
+
|
|
91
|
+
### Computed organization path
|
|
92
|
+
|
|
93
|
+
An entity declaring both an `organizationLink` and an `organizationPath` role
|
|
94
|
+
gets its path filled from the organization it points at, on creation and
|
|
95
|
+
whenever the link changes. Mark the path `generated` so a client cannot set it.
|
|
96
|
+
|
|
97
|
+
### Normalised sizes
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"mailQuotaSize": {
|
|
102
|
+
"type": "number",
|
|
103
|
+
"normalize": "byteSize",
|
|
104
|
+
"default": 1000000000,
|
|
105
|
+
"hint": "Expected pattern xGB, xMB, xKB or a number of bytes"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`5GB`, `500 MB`, `2KB` and `2048` all become a number of bytes, with decimal
|
|
111
|
+
multipliers (1 GB = 10⁹ bytes) as mail servers count them. The same conversion
|
|
112
|
+
applies to creations and to updates.
|
|
113
|
+
|
|
114
|
+
### Server-owned defaults
|
|
115
|
+
|
|
116
|
+
On creation, an attribute that is `generated` or `normalize`d and carries a
|
|
117
|
+
`default` is filled when the client said nothing — which is how a new account
|
|
118
|
+
lands in its initial state:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"twakeAccountStatus": {
|
|
123
|
+
"type": "pointer",
|
|
124
|
+
"role": "accountStatus",
|
|
125
|
+
"generated": true,
|
|
126
|
+
"default": "cn=active,ou=twakeAccountStatus,ou=nomenclature,dc=example,dc=com"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Scheduled removal
|
|
132
|
+
|
|
133
|
+
An `accountExpiry` attribute must not carry a date already past. The comparison
|
|
134
|
+
is against the start of the current day, so scheduling something for today is
|
|
135
|
+
allowed.
|
|
136
|
+
|
|
137
|
+
### Deletion guards
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"title": { "type": "pointer", "referentialIntegrity": "restrict" },
|
|
142
|
+
"member": { "type": "array", "role": "members", "deleteGuard": "nonEmpty" }
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- `referentialIntegrity: "restrict"` refuses to delete an entry while another
|
|
147
|
+
entry still points at it — a position somebody still holds, a nomenclature
|
|
148
|
+
value still in use. Declared on the **referencing** attribute, and the
|
|
149
|
+
pointer's `branch` tells the plugin which deletions to check.
|
|
150
|
+
- `deleteGuard: "nonEmpty"` refuses to delete a group that still has members.
|
|
151
|
+
The placeholder member some directories keep to satisfy `groupOfNames`
|
|
152
|
+
(`--group-dummy-user`) does not count.
|
|
153
|
+
|
|
154
|
+
Both default to off: a deployment turns them on where it wants them.
|
|
155
|
+
|
|
156
|
+
## Errors
|
|
157
|
+
|
|
158
|
+
| Status | When |
|
|
159
|
+
| ------ | ----------------------------------------------------------------------------- |
|
|
160
|
+
| `400` | A size that is not a size, a date already past |
|
|
161
|
+
| `409` | A duplicate value, a mail domain outside the authorised set, a guarded delete |
|
|
162
|
+
|
|
163
|
+
## See also
|
|
164
|
+
|
|
165
|
+
- [flat-generic](flat-generic.md) — the schema format these markers extend
|
|
166
|
+
- [account-lifecycle](account-lifecycle.md) — status changes and password resets
|
|
167
|
+
- [organizations](organizations.md) — the tree the domain rule walks up
|