ldap-rest 0.7.0 → 0.8.0
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
|
@@ -139,6 +139,178 @@ Fixed attributes:
|
|
|
139
139
|
- Cannot be modified after creation
|
|
140
140
|
- Cannot be deleted
|
|
141
141
|
|
|
142
|
+
### Naming things, in the reader's language
|
|
143
|
+
|
|
144
|
+
`label` is what a client shows instead of the attribute name, and `entity.label`
|
|
145
|
+
/ `entity.singularLabel` are what it calls the collection and one of its
|
|
146
|
+
entries. Each may be a plain string, or a map keyed by language tag:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"entity": {
|
|
151
|
+
"name": "twakeUser",
|
|
152
|
+
"singularName": "user",
|
|
153
|
+
"pluralName": "users",
|
|
154
|
+
"label": { "en": "Users", "fr": "Utilisateurs" },
|
|
155
|
+
"singularLabel": { "en": "user", "fr": "utilisateur" }
|
|
156
|
+
},
|
|
157
|
+
"attributes": {
|
|
158
|
+
"twakeDepartmentLink": {
|
|
159
|
+
"type": "pointer",
|
|
160
|
+
"role": "organizationLink",
|
|
161
|
+
"label": { "en": "Department", "fr": "Organisation" }
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
These words name _your_ entities and attributes, so their translations belong
|
|
168
|
+
in your schemas rather than in a catalogue shipped with the product — which is
|
|
169
|
+
also why an interface can be fully translated without the product learning what
|
|
170
|
+
you call a department. A client picks the exact tag, then its language, then
|
|
171
|
+
English, then whatever the map does hold; with no label at all it falls back to
|
|
172
|
+
the attribute name made readable.
|
|
173
|
+
|
|
174
|
+
A nomenclature names its values too. `entity.valueLabels` maps the main
|
|
175
|
+
attribute value of an entry to what a client shows for it, so a group whose
|
|
176
|
+
type is `cn=teamMailbox,…` reads _Shared mailbox_ — or _Boîte partagée_ —
|
|
177
|
+
wherever a pointer lands on it:
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"entity": {
|
|
182
|
+
"name": "twakeMailboxType",
|
|
183
|
+
"mainAttribute": "cn",
|
|
184
|
+
"valueLabels": {
|
|
185
|
+
"group": { "en": "Group", "fr": "Groupe" },
|
|
186
|
+
"teamMailbox": { "en": "Shared mailbox", "fr": "Boîte partagée" }
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
A value the map does not hold is shown as it is stored: a directory with a
|
|
193
|
+
value of its own keeps working, and names it by adding it to its schema.
|
|
194
|
+
|
|
195
|
+
### Explaining a pattern: `hint`
|
|
196
|
+
|
|
197
|
+
A `test` tells the server what to refuse. A `hint` tells the person what to
|
|
198
|
+
type, before they get it wrong:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"telephoneNumber": {
|
|
203
|
+
"type": "string",
|
|
204
|
+
"test": "^\\(\\+99\\)\\s\\d{3}\\s\\d{4}$",
|
|
205
|
+
"hint": "Expected pattern (+99) 999 9999"
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The hint travels with the schema, so a client shows it under the field and the
|
|
211
|
+
server repeats it when it rejects a value — the pattern and its explanation
|
|
212
|
+
cannot drift apart, and neither is hardcoded in the interface.
|
|
213
|
+
|
|
214
|
+
### Attributes a client may not send
|
|
215
|
+
|
|
216
|
+
| Marker | Meaning |
|
|
217
|
+
| ----------- | ------------------------------------------------------------------------------- |
|
|
218
|
+
| `generated` | The server computes the value. A request that carries it is refused with a 400. |
|
|
219
|
+
| `readOnly` | The value is derived elsewhere — `memberOf` is driven from the group side. |
|
|
220
|
+
|
|
221
|
+
Both restrict the **request body** only: the core and its plugins still write
|
|
222
|
+
these attributes.
|
|
223
|
+
|
|
224
|
+
### Attributes a client never receives
|
|
225
|
+
|
|
226
|
+
`neverReturn` strips an attribute from every API answer while leaving it
|
|
227
|
+
writable. It is what makes "a manager may reset a password without being able
|
|
228
|
+
to read it" a projection rather than an authorization engine:
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"userPassword": { "type": "string", "role": "password", "neverReturn": true },
|
|
233
|
+
"pwdReset": {
|
|
234
|
+
"type": "boolean",
|
|
235
|
+
"role": "passwordReset",
|
|
236
|
+
"neverReturn": true
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Deriving the identifier
|
|
242
|
+
|
|
243
|
+
When the RDN is not something a person chooses, `generatedFrom` says where it
|
|
244
|
+
comes from. The client then posts without it:
|
|
245
|
+
|
|
246
|
+
```json
|
|
247
|
+
{
|
|
248
|
+
"uid": {
|
|
249
|
+
"type": "string",
|
|
250
|
+
"role": "identifier",
|
|
251
|
+
"generated": true,
|
|
252
|
+
"generatedFrom": {
|
|
253
|
+
"attribute": "mail",
|
|
254
|
+
"extract": "^([^@]+)@",
|
|
255
|
+
"lowercase": true,
|
|
256
|
+
"onCollision": "suffix"
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
`onCollision` decides what happens when the value is taken — two directories'
|
|
263
|
+
worth of `jean.dupont@a.example` and `jean.dupont@b.example` map to the same
|
|
264
|
+
local part. `suffix` appends `-2`, `-3`, …; `error` (the default) refuses the
|
|
265
|
+
creation with a 409.
|
|
266
|
+
|
|
267
|
+
### Semantic roles
|
|
268
|
+
|
|
269
|
+
`role` says what an attribute _means_, so core code and plugins can find it
|
|
270
|
+
without knowing its name. `roleAttribute(schema, 'accountStatus')` returns
|
|
271
|
+
`twakeAccountStatus` in a Twake directory and whatever another deployment
|
|
272
|
+
chose in its own. The roles the core looks for:
|
|
273
|
+
|
|
274
|
+
| Role | Meaning |
|
|
275
|
+
| -------------------------------------- | --------------------------------------------- |
|
|
276
|
+
| `identifier`, `displayName` | RDN value, human-readable name |
|
|
277
|
+
| `primaryEmail`, `emailAliases` | Mail addresses of the entry |
|
|
278
|
+
| `emailQuota` | Mailbox size limit, in bytes |
|
|
279
|
+
| `organizationLink`, `organizationPath` | Organization the entry belongs to |
|
|
280
|
+
| `members`, `owners` | Members of a group, and who may write to it |
|
|
281
|
+
| `accountStatus`, `accountExpiry` | Lifecycle state and scheduled removal |
|
|
282
|
+
| `password`, `passwordReset` | Credential and "change it at next login" flag |
|
|
283
|
+
| `domainLink`, `domainName` | Mail domains an organization may use |
|
|
284
|
+
|
|
285
|
+
An attribute may carry several roles (`"role": ["identifier", "displayName"]`),
|
|
286
|
+
and a deployment is free to add its own for its plugins.
|
|
287
|
+
|
|
288
|
+
### Which attributes a search covers: `searchable`
|
|
289
|
+
|
|
290
|
+
`GET /{pluralName}?match=…&attribute=…` takes one attribute name or several
|
|
291
|
+
separated by commas, and an entry matching any of them is returned. Which ones
|
|
292
|
+
a client should offer is a property of the deployment rather than of the code:
|
|
293
|
+
a substring filter on an attribute the directory has not indexed scans the
|
|
294
|
+
branch.
|
|
295
|
+
|
|
296
|
+
`"searchable": true` says so. A schema that marks even one attribute has
|
|
297
|
+
declared its list, and that list is what a client searches; a schema that
|
|
298
|
+
marks none leaves the client to guess — everything returnable, single-valued
|
|
299
|
+
and not a DN, which suits a small branch and scans a large one. The shipped
|
|
300
|
+
schemas mark the attributes an operator looks people up by:
|
|
301
|
+
|
|
302
|
+
```json
|
|
303
|
+
"sn": { "type": "string", "searchable": true, "label": { … } }
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Align it with your indexes. In OpenLDAP that means `index sn,cn,mail sub` for
|
|
307
|
+
the attributes you mark.
|
|
308
|
+
|
|
309
|
+
The remaining markers — `unique`, `normalize`, `states`,
|
|
310
|
+
`referentialIntegrity`, `deleteGuard`, `mailDomainScope` — are read by
|
|
311
|
+
[enterprise-rules](enterprise-rules.md) and
|
|
312
|
+
[account-lifecycle](account-lifecycle.md).
|
|
313
|
+
|
|
142
314
|
## Generated APIs
|
|
143
315
|
|
|
144
316
|
For each schema, the plugin generates REST API endpoints:
|
|
@@ -41,7 +41,7 @@ This plugin manages groups stored as `groupOfNames` or similar object classes in
|
|
|
41
41
|
|
|
42
42
|
### Important Notes
|
|
43
43
|
|
|
44
|
-
- **Empty Groups**: `groupOfNames` requires at least one member. The plugin uses a dummy member (configurable via `--group-dummy-user`) for empty groups.
|
|
44
|
+
- **Empty Groups**: `groupOfNames` requires at least one member. The plugin uses a dummy member (configurable via `--group-dummy-user`) for empty groups. That dummy member is hidden from `member` on both `GET /ldap/groups` and `GET /ldap/groups/{cn}`, so a client never sees it as a real one.
|
|
45
45
|
- **Member Validation**: By default, the plugin validates that all members exist in LDAP before adding them. Set `--groups-allow-unexistent-members true` to disable validation.
|
|
46
46
|
- **Automatic Cleanup**: When a user is deleted, the plugin automatically removes them from all groups via the `ldapdeleterequest` hook.
|
|
47
47
|
|
|
@@ -55,13 +55,17 @@ GET /api/v1/ldap/groups
|
|
|
55
55
|
|
|
56
56
|
**Query Parameters:**
|
|
57
57
|
|
|
58
|
-
- `match` (optional):
|
|
58
|
+
- `match` (optional): Value to look for
|
|
59
|
+
- `attribute` (optional): Attribute(s) `match` is looked for in, as a
|
|
60
|
+
substring, several separated by commas — the same semantics as the flat
|
|
61
|
+
entity lists. Without it, `match` is either a raw LDAP filter (when it
|
|
62
|
+
contains `=`) or an exact group name.
|
|
59
63
|
- `attributes` (optional): Comma-separated list of attributes to return
|
|
60
64
|
|
|
61
65
|
**Example:**
|
|
62
66
|
|
|
63
67
|
```bash
|
|
64
|
-
curl "http://localhost:8081/api/v1/ldap/groups?match=admin
|
|
68
|
+
curl "http://localhost:8081/api/v1/ldap/groups?match=admin&attribute=cn,mail&attributes=cn,member,description"
|
|
65
69
|
```
|
|
66
70
|
|
|
67
71
|
**Response (200):**
|
|
@@ -150,7 +154,9 @@ POST /api/v1/ldap/groups
|
|
|
150
154
|
|
|
151
155
|
- `cn` is required
|
|
152
156
|
- `member` can be a string (single member) or array (multiple members)
|
|
153
|
-
- If no members provided, a dummy member is automatically added
|
|
157
|
+
- If no members provided, a dummy member is automatically added, and it is
|
|
158
|
+
left out of `member` when the group is read back, on both the list and the
|
|
159
|
+
single-group endpoint
|
|
154
160
|
- Additional attributes can be included based on schema
|
|
155
161
|
|
|
156
162
|
**Response (200):**
|
|
@@ -51,8 +51,10 @@ Organizations are hierarchical LDAP entries:
|
|
|
51
51
|
|
|
52
52
|
- Use LDAP DN structure for hierarchy
|
|
53
53
|
- Have a `twakeDepartmentPath` attribute showing their position in the tree
|
|
54
|
-
-
|
|
55
|
-
|
|
54
|
+
- The path reads from the root down, the entry's own name last, and stops
|
|
55
|
+
below the top organization: a top-level organization's path is its own name
|
|
56
|
+
- Example: `ou=IT,ou=Departments,ou=organization,dc=example,dc=com`
|
|
57
|
+
- Path: `Departments / IT`
|
|
56
58
|
|
|
57
59
|
### Users and Groups
|
|
58
60
|
|
|
@@ -64,8 +66,8 @@ Users and groups reference organizations via:
|
|
|
64
66
|
```json
|
|
65
67
|
{
|
|
66
68
|
"uid": "john.doe",
|
|
67
|
-
"twakeDepartmentLink": "ou=IT,ou=Departments,dc=example,dc=com",
|
|
68
|
-
"twakeDepartmentPath": "
|
|
69
|
+
"twakeDepartmentLink": "ou=IT,ou=Departments,ou=organization,dc=example,dc=com",
|
|
70
|
+
"twakeDepartmentPath": "Departments / IT"
|
|
69
71
|
}
|
|
70
72
|
```
|
|
71
73
|
|
|
@@ -118,7 +120,7 @@ curl "http://localhost:8081/api/v1/ldap/organizations/ou%3DIT%2Co%3Dgov%2Cc%3Dmu
|
|
|
118
120
|
"dn": "ou=IT,dc=example,dc=com",
|
|
119
121
|
"ou": "IT",
|
|
120
122
|
"description": "Information Technology Department",
|
|
121
|
-
"twakeDepartmentPath": "
|
|
123
|
+
"twakeDepartmentPath": "Government / IT"
|
|
122
124
|
}
|
|
123
125
|
```
|
|
124
126
|
|
|
@@ -185,7 +187,7 @@ POST /api/v1/ldap/organizations
|
|
|
185
187
|
"ou": "IT",
|
|
186
188
|
"parentDn": "dc=example,dc=com",
|
|
187
189
|
"description": "Information Technology Department",
|
|
188
|
-
"twakeDepartmentPath": "
|
|
190
|
+
"twakeDepartmentPath": "Government / IT"
|
|
189
191
|
}
|
|
190
192
|
```
|
|
191
193
|
|
|
@@ -264,7 +266,10 @@ POST /api/v1/ldap/organizations/:dn/move
|
|
|
264
266
|
|
|
265
267
|
- Moves an organization to a different parent organization
|
|
266
268
|
- The organization will become a child of the target organization
|
|
267
|
-
- All sub-organizations and linked entities (users/groups) move with it
|
|
269
|
+
- All sub-organizations and linked entities (users/groups) move with it. With
|
|
270
|
+
`core/ldap/departmentSync` loaded, the path of the organization and of every
|
|
271
|
+
sub-organization is recomputed, then the linked entities take the new link
|
|
272
|
+
and path; without it, those paths keep naming the former parent
|
|
268
273
|
- Cannot move an organization into itself or its own descendants (circular reference prevention)
|
|
269
274
|
- Cannot move to the same parent (no-op)
|
|
270
275
|
- Target must be a valid organizational unit
|
|
@@ -340,10 +345,14 @@ The plugin enforces several validation rules through hooks:
|
|
|
340
345
|
### For Organizations
|
|
341
346
|
|
|
342
347
|
1. **Path Validation**: The `twakeDepartmentPath` must:
|
|
343
|
-
-
|
|
344
|
-
- Be
|
|
345
|
-
-
|
|
346
|
-
|
|
348
|
+
- End with the organization's own `ou` name, preceded by the path separator
|
|
349
|
+
- Be preceded by the stored path of an existing organization
|
|
350
|
+
- Be its own name alone only when the entry hangs directly from the top
|
|
351
|
+
organization, which its DN has to say
|
|
352
|
+
- A path written the other way round, the entry's own name first and the
|
|
353
|
+
top organization's name last, is accepted as it stands: it is what
|
|
354
|
+
directories written before this convention hold. The server never
|
|
355
|
+
computes one.
|
|
347
356
|
|
|
348
357
|
2. **Deletion Protection**: Organizations can only be deleted if:
|
|
349
358
|
- No users have `twakeDepartmentLink` pointing to it
|
|
@@ -385,9 +394,10 @@ hooks: {
|
|
|
385
394
|
if (!isOrganization(entry)) {
|
|
386
395
|
await checkDeptLink(entry); // Validates link exists
|
|
387
396
|
}
|
|
388
|
-
// If entry is an organization, validate path
|
|
397
|
+
// If entry is an organization, validate path against the DN it is
|
|
398
|
+
// written at: that is what says where the entry hangs from
|
|
389
399
|
if (isOrganization(entry)) {
|
|
390
|
-
await checkDeptPath(entry); // Validates path matches hierarchy
|
|
400
|
+
await checkDeptPath(entry, dn); // Validates path matches hierarchy
|
|
391
401
|
}
|
|
392
402
|
return [dn, entry];
|
|
393
403
|
};
|
|
@@ -417,13 +427,13 @@ curl -X POST http://localhost:8081/api/v1/ldap/organizations \
|
|
|
417
427
|
-H "Content-Type: application/json" \
|
|
418
428
|
-d '{
|
|
419
429
|
"ou": "Recruitment",
|
|
420
|
-
"parentDn": "ou=HR,dc=example,dc=com",
|
|
421
|
-
"description": "Recruitment Team"
|
|
422
|
-
"twakeDepartmentPath": "Recruitment / HR / Government"
|
|
430
|
+
"parentDn": "ou=HR,ou=organization,dc=example,dc=com",
|
|
431
|
+
"description": "Recruitment Team"
|
|
423
432
|
}'
|
|
433
|
+
# `twakeDepartmentPath` is computed by the server: `HR / Recruitment`
|
|
424
434
|
```
|
|
425
435
|
|
|
426
|
-
This creates: `ou=Recruitment,ou=HR,dc=example,dc=com`
|
|
436
|
+
This creates: `ou=Recruitment,ou=HR,ou=organization,dc=example,dc=com`
|
|
427
437
|
|
|
428
438
|
### Example 3: Get Organization Hierarchy
|
|
429
439
|
|
|
@@ -493,7 +503,7 @@ curl -X POST http://localhost:8081/api/v1/ldap/users \
|
|
|
493
503
|
"sn": "Doe",
|
|
494
504
|
"mail": "john.doe@example.com",
|
|
495
505
|
"twakeDepartmentLink": "ou=IT,dc=example,dc=com",
|
|
496
|
-
"twakeDepartmentPath": "
|
|
506
|
+
"twakeDepartmentPath": "Government / IT"
|
|
497
507
|
}'
|
|
498
508
|
```
|
|
499
509
|
|
|
@@ -513,7 +523,7 @@ curl -X POST http://localhost:8081/api/v1/ldap/groups \
|
|
|
513
523
|
"cn": "it-admins",
|
|
514
524
|
"description": "IT Administrators",
|
|
515
525
|
"twakeDepartmentLink": "ou=IT,dc=example,dc=com",
|
|
516
|
-
"twakeDepartmentPath": "
|
|
526
|
+
"twakeDepartmentPath": "Government / IT",
|
|
517
527
|
"member": ["uid=john.doe,ou=users,dc=example,dc=com"]
|
|
518
528
|
}'
|
|
519
529
|
```
|
|
@@ -538,31 +548,47 @@ You must first:
|
|
|
538
548
|
|
|
539
549
|
### Path Format
|
|
540
550
|
|
|
541
|
-
The path attribute follows this pattern:
|
|
551
|
+
The path attribute follows this pattern, the top organization excluded:
|
|
542
552
|
|
|
543
553
|
```
|
|
544
|
-
{
|
|
554
|
+
{parent_of_parent_ou} / {parent_ou} / {current_ou}
|
|
545
555
|
```
|
|
546
556
|
|
|
547
557
|
### Validation Process
|
|
548
558
|
|
|
549
|
-
1. **
|
|
550
|
-
2. **
|
|
551
|
-
|
|
552
|
-
|
|
559
|
+
1. **Verify Last Component**: Must match the organization's own `ou`
|
|
560
|
+
2. **Validate Parent Path**: What precedes it must be the stored path of an
|
|
561
|
+
existing organization
|
|
562
|
+
3. **Verify Hierarchy**: An organization whose path is its own name alone must
|
|
563
|
+
hang directly from the top organization
|
|
564
|
+
4. **Accept The Old Form**: A path reading the other way round, ending in the
|
|
565
|
+
top organization's own name, is left alone — the directories that hold
|
|
566
|
+
them predate this convention and their entries have to stay writable
|
|
553
567
|
|
|
554
568
|
### Example Path Validation
|
|
555
569
|
|
|
556
|
-
For organization: `ou=Recruitment,ou=HR,dc=example,dc=com`
|
|
570
|
+
For organization: `ou=Recruitment,ou=HR,ou=organization,dc=example,dc=com`
|
|
557
571
|
|
|
558
|
-
Valid path: `
|
|
572
|
+
Valid path: `HR / Recruitment`
|
|
559
573
|
|
|
560
|
-
- `Recruitment
|
|
561
|
-
-
|
|
574
|
+
- it ends with `Recruitment`, the entry's own `ou`
|
|
575
|
+
- what precedes it, `HR`, must be the stored path of an existing organization
|
|
562
576
|
|
|
563
|
-
Invalid path: `
|
|
577
|
+
Invalid path: `IT / Recruitment`
|
|
564
578
|
|
|
565
|
-
-
|
|
579
|
+
- no organization holds the path `IT`
|
|
580
|
+
|
|
581
|
+
Invalid path: `Recruitment / HR`
|
|
582
|
+
|
|
583
|
+
- it ends with `HR`, not with the entry's own name
|
|
584
|
+
|
|
585
|
+
Invalid path: `Recruitment`
|
|
586
|
+
|
|
587
|
+
- it names no parent, and the entry does not hang from the top organization
|
|
588
|
+
|
|
589
|
+
Accepted path: `Recruitment / HR / organization`
|
|
590
|
+
|
|
591
|
+
- the old form, the top organization last: read, not written
|
|
566
592
|
|
|
567
593
|
## Troubleshooting
|
|
568
594
|
|
|
@@ -576,7 +602,7 @@ Invalid path: `Recruitment / IT / Government`
|
|
|
576
602
|
# Create organization first
|
|
577
603
|
curl -X POST http://localhost:8081/api/v1/ldap/organizations \
|
|
578
604
|
-H "Content-Type: application/json" \
|
|
579
|
-
-d '{"ou": "IT"
|
|
605
|
+
-d '{"ou": "IT"}' # the path is computed by the server
|
|
580
606
|
|
|
581
607
|
# Then create user with link
|
|
582
608
|
curl -X POST http://localhost:8081/api/v1/ldap/users \
|
|
@@ -591,7 +617,7 @@ curl -X POST http://localhost:8081/api/v1/ldap/users \
|
|
|
591
617
|
|
|
592
618
|
1. Ensure parent path exists and matches LDAP hierarchy
|
|
593
619
|
2. Verify separator matches configured separator (default: `" / "`)
|
|
594
|
-
3. Check that path
|
|
620
|
+
3. Check that the path ends with the organization's own `ou` name
|
|
595
621
|
|
|
596
622
|
### Cannot Delete Organization
|
|
597
623
|
|