ldap-rest 0.12.0 → 0.13.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.
Files changed (48) hide show
  1. package/dist/abstract/ldapFlat.js +1 -1
  2. package/dist/bin/index.js +1 -1
  3. package/dist/config/args.js +1 -1
  4. package/dist/lib/errors.js +1 -1
  5. package/dist/lib/instanceProviders.js +1 -1
  6. package/dist/lib/ldapActions.js +1 -1
  7. package/dist/plugins/auth/openidconnect.js +1 -1
  8. package/dist/plugins/ldap/groups.js +1 -1
  9. package/dist/plugins/ldap/trash.js +1 -1
  10. package/dist/plugins/scim/filter.js +1 -1
  11. package/dist/plugins/scim/groups.js +1 -1
  12. package/dist/plugins/scim/mapping.js +1 -1
  13. package/dist/plugins/scim/patch.js +1 -1
  14. package/dist/plugins/scim/users.js +1 -1
  15. package/dist/plugins/twake/groups.js +1 -0
  16. package/dist/plugins/twake/instances.js +1 -1
  17. package/dist/plugins/twake/lifecycleAttributes.js +1 -1
  18. package/dist/plugins/twake/lifecycleEvents.js +1 -1
  19. package/dist/plugins/twake/tombstone.js +1 -1
  20. package/dist/src/abstract/ldapFlat.d.ts +34 -6
  21. package/dist/src/config/args.d.ts +12 -0
  22. package/dist/src/lib/errors.d.ts +3 -0
  23. package/dist/src/lib/instanceProviders.d.ts +2 -0
  24. package/dist/src/lib/ldapActions.d.ts +6 -0
  25. package/dist/src/plugins/auth/openidconnect.d.ts +15 -0
  26. package/dist/src/plugins/ldap/groups.d.ts +5 -0
  27. package/dist/src/plugins/scim/filter.d.ts +2 -1
  28. package/dist/src/plugins/scim/groups.d.ts +1 -0
  29. package/dist/src/plugins/scim/mapping.d.ts +18 -7
  30. package/dist/src/plugins/scim/patch.d.ts +2 -2
  31. package/dist/src/plugins/twake/groups.d.ts +89 -0
  32. package/dist/src/plugins/twake/instances.d.ts +14 -0
  33. package/dist/src/plugins/twake/lifecycleEvents.d.ts +20 -3
  34. package/docs/client-development/api/rest-api.md +32 -2
  35. package/docs/usage/configuration.md +22 -1
  36. package/docs/usage/plugins/README.md +1 -0
  37. package/docs/usage/plugins/auth/oidc.md +19 -0
  38. package/docs/usage/plugins/integrations/README.md +1 -0
  39. package/docs/usage/plugins/integrations/groups.md +225 -0
  40. package/docs/usage/plugins/integrations/instances.md +31 -3
  41. package/docs/usage/plugins/integrations/lifecycle-events.md +38 -5
  42. package/docs/usage/plugins/integrations/scim.md +44 -34
  43. package/docs/usage/plugins/integrations/tombstone.md +2 -10
  44. package/docs/usage/plugins/ldap/flat-generic.md +38 -2
  45. package/docs/usage/plugins/ldap/trash.md +4 -4
  46. package/docs/usage/upgrading.md +56 -0
  47. package/package.json +9 -1
  48. package/static/schemas/twake/organizationGroups.json +32 -0
@@ -1,11 +1,12 @@
1
1
  # Lifecycle Events
2
2
 
3
- `core/twake/lifecycleEvents` publishes an account's lifecycle to RabbitMQ from
4
- the directory write itself. REST, SCIM and any other plugin writing through
3
+ `core/twake/lifecycleEvents` publishes an account's or a group's lifecycle to
4
+ RabbitMQ from the directory write itself. REST, SCIM and any other plugin writing through
5
5
  LDAP-Rest announce the same events, with the same payloads.
6
6
 
7
- Which entries are accounts, which attributes carry the role, the lock and the
8
- deletion, and where each event goes are configuration.
7
+ Which entries are accounts or groups, which attributes carry the role, the
8
+ lock, the deletion and the members, and where each event goes are
9
+ configuration.
9
10
 
10
11
  ## Events
11
12
 
@@ -22,6 +23,14 @@ publishes nothing.
22
23
  role in a single-valued attribute.
23
24
  - `disabled`: the lock attribute takes the lock value.
24
25
  - `enabled`: the lock attribute no longer holds the lock value.
26
+ - `updated`: a target whose payload has `$changed.` sources publishes when one
27
+ of them changed, with those that did not left out. A target without them
28
+ publishes on every change of the entry.
29
+ - `memberAdded`, `memberRemoved`: the member attribute gains or loses values.
30
+ The old and new lists are compared as sets, so a whole list replaced
31
+ publishes only the members that came or went. The group placeholder
32
+ (`--group-dummy-user`) is never a member here. A group created with
33
+ members publishes `created` only, its members in `$members`.
25
34
  - `deleted`: the entry becomes a tombstone (the deleted attribute takes the
26
35
  deleted value), or an entry that is not a tombstone is removed.
27
36
 
@@ -102,6 +111,8 @@ operational one such as `pwdAccountLockedTime` is seen on both sides.
102
111
  - `--twake-lifecycle-deleted-at-attribute`,
103
112
  `--twake-lifecycle-deleted-at-format` (`iso8601`, the default, or
104
113
  `generalizedTime`): the deletion date. It is always published as ISO 8601.
114
+ - `--twake-lifecycle-member-attribute` (default `member`): the attribute
115
+ `memberAdded`, `memberRemoved` and member lists read.
105
116
  - `--twake-lifecycle-rules`: a JSON file, or the JSON itself.
106
117
 
107
118
  Each option has a `DM_` environment variable, for example
@@ -177,12 +188,34 @@ Payload and `when` values are sources:
177
188
  - `$context.actor`, `$context.requestId`, `$context.source`: who made the
178
189
  write, the request it belongs to, and the API it came through (`rest`,
179
190
  `scim`); left out for a write no request made, such as a scheduled task
191
+ - `$changed.attr`: the attribute after the write, only if the write changed
192
+ it; an attribute the write removed gives `""`
180
193
  - `$attr|domain`, `$previous.attr|domain`: what follows the `@`
181
194
  - `$now`: the current time, ISO 8601
182
195
  - anything else: the value itself
183
196
 
184
197
  `$attr` and `$previous.attr` carry the first value of a multi-valued
185
- attribute. A source with no value leaves its field out. Every message carries
198
+ attribute. A source with no value leaves its field out.
199
+
200
+ A payload field can also list members, as an object with one key:
201
+
202
+ - `$members`: the members after the write
203
+ - `$added`, `$removed`: the members `memberAdded` and `memberRemoved` are about
204
+
205
+ ```json
206
+ "members": { "$added": { "username": "$uid", "email": "$mail" } }
207
+ ```
208
+
209
+ Each member is read once, and shaped by the object it maps to, whose
210
+ sources are its own attributes (`$attr`) or plain values; any other source is
211
+ refused at startup. A tombstone is left
212
+ out, and a target whose `$added` or `$removed` is left empty publishes
213
+ nothing. A member no longer in the directory is known by its RDN alone:
214
+ `uid=jdoe,…` gives `{ "username": "jdoe" }` above. A group of 1,000 members
215
+ takes 1,000 reads, one after the other, off the path of the write's
216
+ response. A member that cannot be read, for a reason other than being gone,
217
+ drops that target, logged as an error: a member the service account may not
218
+ read silences every target of the group that lists its members. Every message carries
186
219
  a random AMQP `messageId`.
187
220
 
188
221
  A source can read any attribute of the entry: `$userPassword` would put the
@@ -51,7 +51,7 @@ All SCIM endpoints sit behind the auth middleware registered before the plugin
51
51
  | `--scim-base-map` | `DM_SCIM_BASE_MAP` | — | Path to JSON file mapping authenticated user → `{userBase, groupBase}` |
52
52
  | `--scim-user-object-class` | `DM_SCIM_USER_OBJECT_CLASSES` | `top, inetOrgPerson, organizationalPerson, person` | Object classes for created Users |
53
53
  | `--scim-user-rdn-attribute` | `DM_SCIM_USER_RDN_ATTRIBUTE` | `uid` | RDN attribute for Users |
54
- | `--scim-user-lock-attribute` | `DM_SCIM_USER_LOCK_ATTRIBUTE` | `pwdAccountLockedTime` | LDAP attribute whose presence marks a User as `active: false` |
54
+ | `--scim-user-lock-attribute` | `DM_SCIM_USER_LOCK_ATTRIBUTE` | `pwdAccountLockedTime` | LDAP attribute whose lock value marks a User as `active: false` |
55
55
  | `--scim-user-lock-value` | `DM_SCIM_USER_LOCK_VALUE` | — | Value written to that attribute when deactivating (required unless the attribute is the default) |
56
56
  | `--scim-group-object-class` | `DM_SCIM_GROUP_OBJECT_CLASSES` | `top, groupOfNames` | Object classes for created Groups |
57
57
  | `--scim-group-rdn-attribute` | `DM_SCIM_GROUP_RDN_ATTRIBUTE` | `cn` | RDN attribute for Groups |
@@ -113,23 +113,23 @@ All responses have `Content-Type: application/scim+json`.
113
113
 
114
114
  ### Default mapping (LDAP ⇄ SCIM User)
115
115
 
116
- | SCIM | LDAP |
117
- | ------------------------------------ | ------------------------------------------- |
118
- | `id` | `uid` (or `entryUUID`) |
119
- | `userName` | `uid` |
120
- | `externalId` | `employeeNumber` |
121
- | `name.familyName` | `sn` |
122
- | `name.givenName` | `givenName` |
123
- | `name.formatted` | `cn` |
124
- | `displayName` | `displayName` |
125
- | `title` | `title` |
126
- | `preferredLanguage` | `preferredLanguage` |
127
- | `emails[primary=true].value` | `mail` |
128
- | `emails[primary=false].value` | `mailAlternateAddress` |
129
- | `phoneNumbers[primary=true].value` | `telephoneNumber` |
130
- | `phoneNumbers[primary=false].value` | `mobile` |
131
- | `active` | pseudo (false if the lock attribute is set) |
132
- | `meta.created` / `meta.lastModified` | `createTimestamp` / `modifyTimestamp` |
116
+ | SCIM | LDAP |
117
+ | ------------------------------------ | --------------------------------------------------------- |
118
+ | `id` | `uid` (or `entryUUID`) |
119
+ | `userName` | `uid` |
120
+ | `externalId` | `employeeNumber` |
121
+ | `name.familyName` | `sn` |
122
+ | `name.givenName` | `givenName` |
123
+ | `name.formatted` | `cn` |
124
+ | `displayName` | `displayName` |
125
+ | `title` | `title` |
126
+ | `preferredLanguage` | `preferredLanguage` |
127
+ | `emails[primary=true].value` | `mail` |
128
+ | `emails[primary=false].value` | `mailAlternateAddress` |
129
+ | `phoneNumbers[primary=true].value` | `telephoneNumber` |
130
+ | `phoneNumbers[primary=false].value` | `mobile` |
131
+ | `active` | pseudo (false if the lock attribute holds the lock value) |
132
+ | `meta.created` / `meta.lastModified` | `createTimestamp` / `modifyTimestamp` |
133
133
 
134
134
  Override with `--scim-user-mapping /path/to/user-mapping.json` (same schema as
135
135
  `static/schemas/scim/default-mapping.json`).
@@ -140,14 +140,18 @@ SCIM models account status with the boolean `active` (RFC 7643 §4.1.1), and it
140
140
  is how Okta, Entra ID and JumpCloud disable a user — they PATCH `active` to
141
141
  `false` rather than DELETE the resource.
142
142
 
143
- LDAP has no single equivalent, so the plugin models it on the **presence of one
144
- attribute**: present means locked, absent means active.
143
+ LDAP has no single equivalent, so the plugin models it on **one attribute
144
+ holding one value**: `--scim-user-lock-value` in `--scim-user-lock-attribute`
145
+ means locked, anything else means active. A ppolicy lockout after failed binds
146
+ is a timestamp in `pwdAccountLockedTime`, so it reads as active, and
147
+ `"active": true` leaves it in place. It lasts `pwdLockoutDuration`, or until
148
+ an administrator clears it when that is 0.
145
149
 
146
150
  | SCIM | LDAP |
147
151
  | ----------------- | ----------------------------------------------- |
148
152
  | `"active": false` | write `--scim-user-lock-value` to the attribute |
149
- | `"active": true` | remove the attribute |
150
- | attribute absent | `"active": true` |
153
+ | `"active": true` | remove the attribute if it holds the lock value |
154
+ | lock value absent | `"active": true` |
151
155
 
152
156
  The default targets the ppolicy overlay:
153
157
 
@@ -184,8 +188,8 @@ neither can be told apart from a deliberate local schema:
184
188
 
185
189
  **None of this can tell whether your directory honours the value you chose.**
186
190
  The checks are on the shape of the configuration. `active` is read back from
187
- the mere presence of the attribute, so a value the directory stores and
188
- ignores still reads as `false`. Verify once, by hand, that a deactivation
191
+ the value this plugin wrote, so a value the directory stores and ignores still
192
+ reads as `false`. Verify once, by hand, that a deactivation
189
193
  actually prevents a bind.
190
194
 
191
195
  `active` is not a mapping entry and must not be added to a mapping file: it is
@@ -347,16 +351,16 @@ group entries works; use a mapping override if you would rather name it there.
347
351
  SCIM filters are translated to LDAP filters (RFC 4515). Values are always escaped
348
352
  via `escapeLdapFilter()` — no LDAP injection is possible.
349
353
 
350
- | SCIM | LDAP |
351
- | -------------------------------- | ----------------------------- |
352
- | `userName eq "alice"` | `(uid=alice)` |
353
- | `name.familyName sw "Du"` | `(sn=Du*)` |
354
- | `emails.value co "@example.com"` | `(mail=*@example.com*)` |
355
- | `displayName pr` | `(displayName=*)` |
356
- | `active eq true` | `(!(pwdAccountLockedTime=*))` |
357
- | `active pr` | `(objectClass=*)` |
358
- | `a eq "x" and b eq "y"` | `(&(a=x)(b=y))` |
359
- | `not (a eq "x")` | `(!(a=x))` |
354
+ | SCIM | LDAP |
355
+ | -------------------------------- | ----------------------------------------- |
356
+ | `userName eq "alice"` | `(uid=alice)` |
357
+ | `name.familyName sw "Du"` | `(sn=Du*)` |
358
+ | `emails.value co "@example.com"` | `(mail=*@example.com*)` |
359
+ | `displayName pr` | `(displayName=*)` |
360
+ | `active eq true` | `(!(pwdAccountLockedTime=000001010000Z))` |
361
+ | `active pr` | `(objectClass=*)` |
362
+ | `a eq "x" and b eq "y"` | `(&(a=x)(b=y))` |
363
+ | `not (a eq "x")` | `(!(a=x))` |
360
364
 
361
365
  Unknown SCIM attributes raise `400 invalidFilter`.
362
366
 
@@ -616,6 +620,12 @@ Other plugins can observe and transform SCIM operations via the dynamic hooks
616
620
  `scimuserdelete`, `scimuserdeletedone`, and their `scimgroup*` equivalents, plus
617
621
  `scimbulkdone` for Bulk.
618
622
 
623
+ `scimgroupcreate` receives the group, the request and the group base. A
624
+ group's RDN value is its `displayName`, unless the hook sets `id` on the group
625
+ and the group mapping stores nothing in the RDN attribute: that `id` is then
626
+ the RDN value. An `id` sent by the client is ignored. A hook returns all
627
+ three, so that the hooks after it still get the base.
628
+
619
629
  ## Exposing the configuration to client apps (`configApi`)
620
630
 
621
631
  Load `core/configApi` alongside the SCIM plugin, and `GET /api/v1/config` will
@@ -38,16 +38,8 @@ a delete that an authorization plugin refuses writes nothing.
38
38
 
39
39
  `core/ldap/trash` moves a deleted entry before this plugin sees the delete,
40
40
  so no tombstone would be written in a branch it watches. The server refuses
41
- to start when `--trash-watched-bases` covers a `--twake-tombstone-dn`
42
- pattern, read the way the trash reads it today, and an empty list watches
43
- every branch.
44
-
45
- The trash splits `--trash-watched-bases` on every comma, a bug
46
- ([#226](https://github.com/linagora/ldap-rest/issues/226)): a base such as
47
- `ou=users,dc=example,dc=com` becomes the suffixes `ou=users`, `dc=example`
48
- and `dc=com`, and `dc=com` watches every branch below it. The check follows
49
- the trash as it behaves, so such a value refuses to start next to any
50
- tombstone pattern under `dc=com`.
41
+ to start when one of the `;`-separated `--trash-watched-bases` covers or sits under
42
+ a `--twake-tombstone-dn` pattern, and an empty list watches every branch.
51
43
 
52
44
  ## Reason
53
45
 
@@ -318,13 +318,13 @@ For each schema, the plugin generates REST API endpoints:
318
318
  ### List Entries
319
319
 
320
320
  ```bash
321
- GET /api/v1/ldap/{pluralName}?match={filter}&attributes={attrs}
321
+ GET /api/v1/ldap/{pluralName}?match={value}&attribute={attrs}&attributes={attrs}&limit={n}
322
322
  ```
323
323
 
324
324
  **Example:**
325
325
 
326
326
  ```bash
327
- curl http://localhost:8081/api/v1/ldap/users?match=uid=*smith*&attributes=uid,cn,mail
327
+ curl 'http://localhost:8081/api/v1/ldap/users?match=smith&attribute=uid&attributes=uid,cn,mail&limit=50'
328
328
  ```
329
329
 
330
330
  **Response:**
@@ -340,6 +340,42 @@ curl http://localhost:8081/api/v1/ldap/users?match=uid=*smith*&attributes=uid,cn
340
340
  }
341
341
  ```
342
342
 
343
+ #### Long lists
344
+
345
+ A directory lists at most a set number of entries per search for the account
346
+ LDAP-Rest binds as (OpenLDAP: `olcSizeLimit`, default 500, or `olcLimits`;
347
+ `size.prtotal` bounds paged searches). OpenLDAP refuses a longer list as a
348
+ whole rather than cutting it short:
349
+
350
+ - **Without `limit`**, the answer is every match or `422`:
351
+
352
+ ```json
353
+ {
354
+ "error": "Too many entries to list at once: the directory's size limit was exceeded. Narrow the search, or pass `limit`."
355
+ }
356
+ ```
357
+
358
+ - **With `limit`** (a positive integer, `400` otherwise), at most `limit`
359
+ entries come back, with the header `X-Result-Truncated: true` when others
360
+ were left out. When the directory refuses the list, LDAP-Rest asks again
361
+ for `limit + 1` entries and answers what it gets, at most `limit` —
362
+ possibly fewer, if the account's own limit is lower — always with the
363
+ header: the directory does not say whether that answer was cut, so a
364
+ shorter one is not taken for complete. A request with `limit` is never
365
+ answered `422`.
366
+
367
+ The header is absent from a complete answer. LDAP-Rest sets no CORS headers:
368
+ a browser client on another origin sees `X-Result-Truncated` only if the
369
+ proxy answering for it lists the header in `Access-Control-Expose-Headers`.
370
+
371
+ A small `limit` costs a page of the directory's answer. A `limit` larger than
372
+ the directory's own limit costs a walk up to that limit, then the second
373
+ search: memory stays bounded by the directory, not by `limit`.
374
+
375
+ Only the lists of the entities this plugin serves honour `limit`,
376
+ `X-Result-Truncated` and the `422` for now. The groups list
377
+ (`GET /api/v1/ldap/groups`) ignores `limit` and still fails with `500`.
378
+
343
379
  ### Get Entry
344
380
 
345
381
  ```bash
@@ -16,7 +16,7 @@ Plugin to intercept LDAP delete operations and move entries to a trash branch in
16
16
  ### Environment Variables
17
17
 
18
18
  - `DM_TRASH_BASE`: LDAP base DN for trash (default: `ou=trash,dc=example,dc=com`)
19
- - `DM_TRASH_WATCHED_BASES`: Comma-separated list of branches to watch (default: all branches except trash)
19
+ - `DM_TRASH_WATCHED_BASES`: branches to watch, each a full DN, separated by `;` (default: all branches except trash)
20
20
  - `DM_TRASH_ADD_METADATA`: Add metadata to trashed entries (default: `true`)
21
21
  - `DM_TRASH_AUTO_CREATE`: Auto-create trash branch if missing (default: `true`)
22
22
 
@@ -25,7 +25,7 @@ Plugin to intercept LDAP delete operations and move entries to a trash branch in
25
25
  ```bash
26
26
  --plugin core/ldap/trash \
27
27
  --trash-base "ou=trash,dc=example,dc=com" \
28
- --trash-watched-bases "ou=users,dc=example,dc=com,ou=groups,dc=example,dc=com" \
28
+ --trash-watched-bases "ou=users,dc=example,dc=com;ou=groups,dc=example,dc=com" \
29
29
  --trash-add-metadata true \
30
30
  --trash-auto-create true
31
31
  ```
@@ -68,7 +68,7 @@ This watches all LDAP branches except the trash branch itself.
68
68
  ldap-rest \
69
69
  --plugin core/ldap/trash \
70
70
  --trash-base "ou=trash,dc=example,dc=com" \
71
- --trash-watched-bases "ou=users,dc=example,dc=com,ou=groups,dc=example,dc=com"
71
+ --trash-watched-bases "ou=users,dc=example,dc=com;ou=groups,dc=example,dc=com"
72
72
  ```
73
73
 
74
74
  This only intercepts deletes from `ou=users` and `ou=groups`. Deletes from other branches proceed normally.
@@ -248,7 +248,7 @@ ldap-rest \
248
248
  --ldap-pwd "admin" \
249
249
  --ldap-base "dc=example,dc=com" \
250
250
  --trash-base "ou=trash,dc=example,dc=com" \
251
- --trash-watched-bases "ou=users,dc=example,dc=com,ou=groups,dc=example,dc=com" \
251
+ --trash-watched-bases "ou=users,dc=example,dc=com;ou=groups,dc=example,dc=com" \
252
252
  --trash-add-metadata true \
253
253
  --auth-token "secret-token"
254
254
  ```
@@ -4,6 +4,62 @@ What to check before deploying, newest first. Only releases that need a
4
4
  decision or a configuration change appear here; see the
5
5
  [CHANGELOG](../../CHANGELOG.md) for everything else.
6
6
 
7
+ ## To 0.13.0
8
+
9
+ ### An OpenID Connect session ends with its access token
10
+
11
+ **Who is affected:** a deployment loading `core/auth/openidconnect`.
12
+
13
+ A session used to last as long as its cookie, a day of inactivity and a week
14
+ at most. It now ends when its access token expires, unless the provider gave
15
+ a refresh token to renew it: a browser then goes back through the provider,
16
+ silently while the provider's own session lasts, and a client asking for
17
+ JSON only (`Accept: application/json`) is answered `401`.
18
+
19
+ To keep sessions going without that round trip, have the provider issue
20
+ refresh tokens to the client
21
+ ([details](plugins/auth/oidc.md#session-lifetime)).
22
+
23
+ ### SCIM `active` reads the lock value
24
+
25
+ **Who is affected:** a SCIM client of a directory with ppolicy lockouts. An
26
+ account locked out after failed binds now reads `active: true`, is left out
27
+ of `active eq false`, and keeps its lockout on `"active": true`; only the
28
+ configured lock value reads as inactive. See
29
+ [the SCIM plugin documentation](plugins/integrations/scim.md#deactivating-an-account-active).
30
+
31
+ ### twake/instances links an organization before its members
32
+
33
+ **Who is affected:** a deployment running 0.12.0 with
34
+ `--twake-instance-provider cloudery` and `--twake-instance-organization-base`.
35
+
36
+ 0.12.0 sent each member's `user.created` at once and marked the member, while
37
+ the Cloudery refused it because the organization was not linked to its
38
+ instance. The plugin now links the organization and holds its members until
39
+ then, but only releases members that are not marked, so those already marked
40
+ are not sent again on their own.
41
+
42
+ To repair an organization, clear `--twake-instance-sent-attribute` on its
43
+ marked members, then replay its `workplace.created` or call
44
+ `ensureOrganization` for it ([details](plugins/integrations/instances.md#organizations)).
45
+
46
+ ### Trash watched bases are separated by `;`
47
+
48
+ **Who is affected:** a deployment loading `core/ldap/trash` with
49
+ `--trash-watched-bases` set.
50
+
51
+ The list was split on commas, so a DN such as `ou=users,dc=example,dc=com`
52
+ became the suffixes `ou=users`, `dc=example` and `dc=com`, and the trash
53
+ caught deletes in every branch under `dc=com`. Each base is now a whole DN,
54
+ and bases are separated by `;`:
55
+ `ou=users,dc=example,dc=com;ou=groups,dc=example,dc=com`. A single DN needs
56
+ no change. A list of full DNs joined by commas, such as
57
+ `ou=users,dc=example,dc=com,ou=groups,dc=example,dc=com`, or a list of RDNs,
58
+ such as `ou=users,ou=groups`, now reads as one DN that no entry sits under,
59
+ so the trash catches nothing and deletes there are permanent: rewrite it with
60
+ `;`, each branch as its full DN. The server logs a warning at startup for a
61
+ watched base that is not one DN under `--ldap-base`.
62
+
7
63
  ## To 0.12.0
8
64
 
9
65
  ### twake/instances replaces cozyProvision and clouderyProvision
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ldap-rest",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "Light directory manager",
5
5
  "keywords": [
6
6
  "Identity-Manager"
@@ -268,6 +268,10 @@
268
268
  "types": "./dist/src/plugins/twake/drive.d.ts",
269
269
  "import": "./dist/plugins/twake/drive.js"
270
270
  },
271
+ "./plugin-twake-groups": {
272
+ "types": "./dist/src/plugins/twake/groups.d.ts",
273
+ "import": "./dist/plugins/twake/groups.js"
274
+ },
271
275
  "./plugin-twake-instances": {
272
276
  "types": "./dist/src/plugins/twake/instances.d.ts",
273
277
  "import": "./dist/plugins/twake/instances.js"
@@ -660,6 +664,10 @@
660
664
  "import": "./static/schemas/twake/nomenclature/twakeTitle.json",
661
665
  "require": "./static/schemas/twake/nomenclature/twakeTitle.json"
662
666
  },
667
+ "./schema-twake-organizationgroups": {
668
+ "import": "./static/schemas/twake/organizationGroups.json",
669
+ "require": "./static/schemas/twake/organizationGroups.json"
670
+ },
663
671
  "./schema-twake-organizations": {
664
672
  "import": "./static/schemas/twake/organizations.json",
665
673
  "require": "./static/schemas/twake/organizations.json"
@@ -0,0 +1,32 @@
1
+ {
2
+ "strict": true,
3
+ "attributes": {
4
+ "objectClass": {
5
+ "type": "array",
6
+ "items": {
7
+ "type": "string",
8
+ "test": "^[a-zA-Z][a-zA-Z0-9-]*$"
9
+ },
10
+ "default": ["top", "groupOfNames", "twakeGroup"],
11
+ "required": true,
12
+ "fixed": true
13
+ },
14
+ "cn": {
15
+ "type": "string",
16
+ "test": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
17
+ "required": true
18
+ },
19
+ "member": {
20
+ "type": "array",
21
+ "items": {
22
+ "type": "string",
23
+ "test": "^[a-zA-Z]+=.*,__LDAP_BASE__$"
24
+ },
25
+ "required": false
26
+ },
27
+ "description": {
28
+ "type": "string",
29
+ "required": false
30
+ }
31
+ }
32
+ }