ldap-rest 1.3.0 → 2.0.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.
Files changed (71) hide show
  1. package/dist/abstract/ldapFlat.js +1 -1
  2. package/dist/abstract/plugin.js +1 -1
  3. package/dist/bin/index.js +1 -1
  4. package/dist/config/options.js +1 -1
  5. package/dist/config/schema.js +1 -1
  6. package/dist/lib/authz/base.js +1 -1
  7. package/dist/lib/errors.js +1 -1
  8. package/dist/lib/ldap/callerView.js +1 -0
  9. package/dist/lib/ldap/pool.js +1 -0
  10. package/dist/lib/ldap/schema.js +1 -0
  11. package/dist/lib/ldap/searchCache.js +1 -0
  12. package/dist/lib/ldapActions.js +1 -1
  13. package/dist/lib/parseConfig.js +1 -1
  14. package/dist/lib/services/index.js +1 -0
  15. package/dist/lib/utils.js +1 -1
  16. package/dist/plugins/ldap/accountLifecycle.js +1 -1
  17. package/dist/plugins/ldap/enterpriseRules.js +1 -1
  18. package/dist/plugins/ldap/groups.js +1 -1
  19. package/dist/plugins/ldap/organizations.js +1 -1
  20. package/dist/plugins/ldap/passwordPolicy.js +1 -1
  21. package/dist/plugins/ldap/raw.js +1 -1
  22. package/dist/plugins/ldap/trash.js +1 -1
  23. package/dist/plugins/scim/mapping.js +1 -1
  24. package/dist/plugins/scim/patch.js +1 -1
  25. package/dist/plugins/storage/file.js +1 -1
  26. package/dist/plugins/twake/instances.js +1 -1
  27. package/dist/plugins/twake/lifecycleAttributes.js +1 -1
  28. package/dist/plugins/twake/lifecycleEvents.js +1 -1
  29. package/dist/plugins/twake/tombstone.js +1 -1
  30. package/dist/src/abstract/ldapFlat.d.ts +13 -0
  31. package/dist/src/abstract/plugin.d.ts +21 -0
  32. package/dist/src/bin/index.d.ts +18 -0
  33. package/dist/src/config/options.d.ts +16 -1
  34. package/dist/src/lib/errors.d.ts +2 -1
  35. package/dist/src/lib/ldap/callerView.d.ts +45 -0
  36. package/dist/src/lib/ldap/pool.d.ts +108 -0
  37. package/dist/src/lib/ldap/schema.d.ts +157 -0
  38. package/dist/src/lib/ldap/searchCache.d.ts +112 -0
  39. package/dist/src/lib/ldapActions.d.ts +25 -262
  40. package/dist/src/lib/parseConfig.d.ts +26 -2
  41. package/dist/src/lib/services/index.d.ts +56 -0
  42. package/dist/src/lib/utils.d.ts +72 -3
  43. package/dist/src/plugins/ldap/enterpriseRules.d.ts +0 -14
  44. package/dist/src/plugins/ldap/passwordPolicy.d.ts +3 -2
  45. package/dist/src/plugins/scim/mapping.d.ts +16 -3
  46. package/dist/src/plugins/scim/types.d.ts +2 -0
  47. package/dist/src/plugins/twake/instances.d.ts +31 -0
  48. package/dist/src/plugins/twake/lifecycleAttributes.d.ts +13 -2
  49. package/dist/src/plugins/twake/tombstone.d.ts +9 -2
  50. package/docs/README.md +16 -2
  51. package/docs/client-development/api/rest-api.md +4 -0
  52. package/docs/development/contracts.md +109 -0
  53. package/docs/plugin-development/README.md +8 -1
  54. package/docs/plugin-development/dependencies.md +13 -2
  55. package/docs/plugin-development/hooks.md +2 -1
  56. package/docs/plugin-development/options.md +40 -13
  57. package/docs/plugin-development/services.md +95 -0
  58. package/docs/usage/operations.md +9 -0
  59. package/docs/usage/plugins/integrations/instances.md +11 -3
  60. package/docs/usage/plugins/integrations/lifecycle-events.md +4 -1
  61. package/docs/usage/plugins/integrations/scim.md +20 -18
  62. package/docs/usage/plugins/integrations/tombstone.md +10 -6
  63. package/docs/usage/plugins/ldap/flat-generic.md +8 -1
  64. package/docs/usage/plugins/ldap/groups.md +4 -1
  65. package/docs/usage/plugins/ldap/password-policy.md +7 -0
  66. package/docs/usage/upgrading.md +59 -0
  67. package/package.json +17 -12
  68. package/static/browser/ldap-tree-viewer/LdapTreeViewer.js.map +1 -1
  69. package/static/browser/ldap-tree-viewer.esm.js.map +1 -1
  70. package/static/browser/ldap-tree-viewer.js.map +1 -1
  71. package/static/browser/ldap-tree-viewer.min.js.map +1 -1
@@ -42,17 +42,43 @@ export type ExampleOptions = OptionsOf<typeof exampleOptions>;
42
42
  The key is the `Config` name: `example_timeout` is `--example-timeout`,
43
43
  `DM_EXAMPLE_TIMEOUT` when the descriptor names that variable.
44
44
 
45
+ ### Deprecating an option
46
+
47
+ Rename an option by giving the old name as an alias: the parser still reads
48
+ it, and the server warns once at startup. Keep the alias for two minor
49
+ releases, as [CONTRIBUTING.md](../../CONTRIBUTING.md#deprecating-a-published-contract)
50
+ requires.
51
+
52
+ ```typescript
53
+ export const exampleOptions = defineOptions({
54
+ example_url: opt.string({
55
+ env: 'DM_EXAMPLE_URL',
56
+ aliases: ['example-uri'],
57
+ envAliases: ['DM_EXAMPLE_URI'],
58
+ }),
59
+ });
60
+ ```
61
+
62
+ A CLI alias takes one value per occurrence, as the singular form does, and an
63
+ array option's plural form has no alias: renaming it is a breaking change. An
64
+ environment alias is read when `env` is unset, or empty for a type where an
65
+ empty value means nothing, and only for a non-array option when its canonical
66
+ CLI name is absent; an array option combines the environment value with the
67
+ CLI one, so its alias is still read.
68
+
45
69
  Each declaration takes:
46
70
 
47
- | Field | Meaning |
48
- | ---------- | ------------------------------------------------------------------- |
49
- | `env` | Environment variable read when the CLI does not set the option |
50
- | `default` | Value kept when neither the CLI nor the environment sets the option |
51
- | `required` | Refuse an empty value instead of reading it as unset |
52
- | `plural` | Array only: CLI name of the plural form, without the leading `--` |
53
- | `kind` | Array only: how a value splits, see `ArrayKind` in `parseConfig.ts` |
54
- | `min/max` | Number only: bounds checked when the option is read |
55
- | `choices` | String only: the values the option accepts |
71
+ | Field | Meaning |
72
+ | ------------ | --------------------------------------------------------------------- |
73
+ | `env` | Environment variable read when the CLI does not set the option |
74
+ | `default` | Value kept when neither the CLI nor the environment sets the option |
75
+ | `required` | Refuse an empty value instead of reading it as unset |
76
+ | `plural` | Array only: CLI name of the plural form, without the leading `--` |
77
+ | `kind` | Array only: how a value splits, see `ArrayKind` in `parseConfig.ts` |
78
+ | `min/max` | Number only: bounds checked when the option is read |
79
+ | `choices` | String only: the values the option accepts |
80
+ | `aliases` | Former CLI names still accepted, without the leading `--` |
81
+ | `envAliases` | Former environment variables still accepted, read when `env` is unset |
56
82
 
57
83
  ## Reading options
58
84
 
@@ -103,7 +129,8 @@ moved. A migrated plugin no longer reads them: it uses `options()`.
103
129
 
104
130
  Plugins outside the repository cannot add to `args.ts` yet, but they can
105
131
  declare options the same way and name them in `optionDefinitions`: the startup
106
- report subtracts those keys, and `options()` reads a declared `env` variable
107
- when the parser did not carry the key. An option without a declaration still
108
- reaches `this.config` from an unknown `--key value` pair, and the startup
109
- report names it.
132
+ report subtracts those keys and their aliases, and `options()` reads a declared
133
+ `env` variable or a deprecated CLI alias the parser did not carry, warning at
134
+ startup for the alias as it does for a core option. An option without a
135
+ declaration still reaches `this.config` from an unknown `--key value` pair, and
136
+ the startup report names it.
@@ -0,0 +1,95 @@
1
+ # Plugin services
2
+
3
+ A plugin sometimes needs what another plugin holds: the group operations, the
4
+ entity schemas, the trash. A service is a typed interface a plugin provides
5
+ under a token, and another plugin reaches with `this.service(token)`.
6
+
7
+ The token carries the interface. The compiler checks that an instance bound
8
+ with `with()` answers it, and gives a consumer the type it declared. Whether a
9
+ provider exists is a runtime question: a plugin that stops declaring the token
10
+ still compiles, and its consumers get `null` and a warning. The plugin
11
+ registry name stays what it is for: loading a plugin and naming it in the
12
+ logs.
13
+
14
+ ## Declaring a service
15
+
16
+ Put the token and its interface in a module both sides import — a plugin
17
+ family's own service in its own module. A service shared between families is
18
+ declared in `src/lib/services/` and re-exported from `ldap-rest/plugin`, one
19
+ of the package's entry points; a module under `src/lib/` has no export path of
20
+ its own.
21
+
22
+ ```typescript
23
+ import { serviceToken } from 'ldap-rest/plugin';
24
+
25
+ export interface GroupsApi {
26
+ listGroups(): Promise<string[]>;
27
+ }
28
+
29
+ export const GroupsService = serviceToken<GroupsApi>('groups');
30
+ ```
31
+
32
+ Declare a token once. The object is the registry key; calling `serviceToken`
33
+ again under the same name declares another service, not the same one. The name
34
+ only identifies it in the logs.
35
+
36
+ ## Providing a service
37
+
38
+ Bind the plugin instance to each token it provides:
39
+
40
+ ```typescript
41
+ import DmPlugin from 'ldap-rest/plugin';
42
+
43
+ export default class LdapGroups extends DmPlugin {
44
+ name = 'ldapGroups';
45
+ provides = [GroupsService.with(this)];
46
+
47
+ async listGroups(): Promise<string[]> {
48
+ /* … */
49
+ }
50
+ }
51
+ ```
52
+
53
+ `with(this)` is checked by the compiler: an instance that does not answer the
54
+ interface does not compile. A plugin can provide several services. A service
55
+ is registered once its plugin's registration completes — after `api()` and
56
+ the hooks — so a consumer loaded afterwards finds it, and a provider does not
57
+ find its own service there. A plugin needing another one declares it in
58
+ [`dependencies`](dependencies.md#explicit-dependencies) so that it loads
59
+ first.
60
+
61
+ The first provider to register wins. A second one offering the same token is
62
+ ignored and named in a warning: which one answered would otherwise depend on
63
+ load order. Loading the same module twice under two names gives two instances
64
+ but one module-level token, so the second instance cannot provide it either.
65
+
66
+ When a host registers a plugin after startup, its `assertComposition` runs
67
+ before the service is registered, so a provider does not find its own service
68
+ there; the configuration's plugins are all registered before their
69
+ `assertComposition` runs.
70
+
71
+ ## Consuming a service
72
+
73
+ ```typescript
74
+ const groups = this.service(GroupsService);
75
+ if (!groups) {
76
+ // Nothing provides it: the plugin skips the feature it would serve
77
+ return;
78
+ }
79
+ await groups.listGroups();
80
+ ```
81
+
82
+ `service()` answers the provided instance, or `null` when nothing provides the
83
+ token. It logs one warning per plugin and service rather than one per call. A
84
+ plugin that cannot work without a service declares the provider in
85
+ `dependencies`, checks the answer in `assertComposition`, and throws naming the
86
+ service — that stops the server at startup instead of leaving a feature
87
+ silently absent. A constructor runs before the dependencies load, so it cannot
88
+ be the place for that check.
89
+
90
+ The registry belongs to the server and is shared by the views `withConfig`
91
+ builds for plugins loaded with overrides. It has no removal: a plugin
92
+ registers once and keeps its services for the life of the server.
93
+
94
+ `this.requirePlugin(name)` remains for a plugin whose interface is not yet
95
+ declared as a service; new code reaches another plugin through a token.
@@ -53,6 +53,15 @@ The image runs as its unprivileged `node` user: with `docker run`, a
53
53
  `DM_PORT` below 1024 stops the server at startup, where the default 8081
54
54
  changes nothing.
55
55
 
56
+ The image is built in two stages: a builder produces the package and installs
57
+ its production dependencies, the runtime stage copies them and carries no
58
+ compiler nor package manager cache. Its base image is pinned by digest in
59
+ `scripts/buildDockerfile.ts`, so the image built is the one reviewed even if
60
+ the tag moves; the digest follows the tag it names, taken from the
61
+ [Docker Hub tag page](https://hub.docker.com/_/node/tags?name=25-alpine) and
62
+ updated by hand with it. Dependabot ignores the image: it would open a change
63
+ to the generated `Dockerfile`, which the drift check refuses.
64
+
56
65
  ### What the shipped plugins report
57
66
 
58
67
  | Plugin | `degraded` when |
@@ -19,7 +19,13 @@ gets no instance.
19
19
  An entry holding `--twake-instance-skip-value` in
20
20
  `--twake-instance-skip-attribute`, a technical account for instance, gets
21
21
  none, and neither does a tombstone
22
- (`--twake-lifecycle-deleted-attribute`) put back in place.
22
+ (`--twake-lifecycle-deleted-attribute`) put back in place. Each expression
23
+ is tried against the canonical DN first, then against the DN as sent: an
24
+ account created under a branch written with other spacing or case is still
25
+ one, and an expression written with the directory's own spelling (`\2C`, a
26
+ doubled space) still matches. An expression naming a value that holds a
27
+ comma writes it `\2C` or `\,`: a value comma never stands in for an RDN
28
+ separator in the form matched.
23
29
  - The address (`--twake-instance-fqdn-attribute`) and the mark
24
30
  (`--twake-instance-sent-attribute`) are this plugin's to write: an add that
25
31
  carries them has them removed, and an address written by a modify is taken
@@ -80,8 +86,10 @@ before it takes its members' `user.created`. Then `organization.created` is
80
86
  published on the b2b exchange, and the entry is marked. The message carries
81
87
  the organization's id, address, domain, and its name as `organization`, read
82
88
  from `--twake-instance-organization-name-attribute`, which
83
- `ensureOrganization` fills from `name` when the entry has none. The mark, not
84
- the address, says the organization is done.
89
+ `ensureOrganization` fills from `name` when the entry has none. An id captured
90
+ from a `--twake-instance-dn` group is read from the organization entry when
91
+ one exists, so the case a caller spells the branch in is not sent. The mark,
92
+ not the address, says the organization is done.
85
93
 
86
94
  With the Cloudery, a member's `user.created` waits for its organization's: a
87
95
  member under an organization entry that is not marked yet gets its address
@@ -110,7 +110,10 @@ operational one such as `pwdAccountLockedTime` is seen on both sides.
110
110
  means entries are never tombstones, and only a removal publishes `deleted`.
111
111
  - `--twake-lifecycle-deleted-at-attribute`,
112
112
  `--twake-lifecycle-deleted-at-format` (`iso8601`, the default, or
113
- `generalizedTime`): the deletion date. It is always published as ISO 8601.
113
+ `generalizedTime`): the deletion date. It is read whether it is written as
114
+ a GeneralizedTime or as an ISO 8601 date, and always published as ISO 8601;
115
+ a value of neither shape is left out. A GeneralizedTime needs its zone; an
116
+ ISO 8601 date and time without one is read in the server's time zone.
114
117
  - `--twake-lifecycle-member-attribute` (default `member`): the attribute
115
118
  `memberAdded`, `memberRemoved` and member lists read.
116
119
  - `--twake-lifecycle-rules`: a JSON file, or the JSON itself.
@@ -223,16 +223,17 @@ and `--scim-group-mapping`.
223
223
 
224
224
  Each element of `entries` is a `MappingEntry` with the following optional fields:
225
225
 
226
- | Field | Purpose |
227
- | --------------- | -------------------------------------------------------------------------------------------------------------------- |
228
- | `scim` | **Required.** Top-level SCIM attribute name (`userName`, `displayName`, `emails`, …). |
229
- | `ldap` | Simple 1:1 mapping to an LDAP attribute. |
230
- | `ldapPrimary` | For multi-valued SCIM attributes (`emails`, `phoneNumbers`, …), the LDAP attribute that stores the _primary_ value. |
231
- | `ldapSecondary` | LDAP attribute that stores the _non-primary_ values of the same SCIM attribute (array). |
232
- | `sub` | Object that maps SCIM sub-attributes to LDAP attributes. Used for complex SCIM values like `name.familyName` → `sn`. |
233
- | `multi` | `"array"` or `"single"` (default) — hints array serialization for `ldap`. |
234
- | `readOnly` | `true` to skip this attribute on write (create/update ignore it). |
235
- | `operational` | `true` for LDAP operational attributes (e.g. `entryUUID`) — loaded on read only. |
226
+ | Field | Purpose |
227
+ | --------------- | ----------------------------------------------------------------------------------------------------------------------------- |
228
+ | `scim` | **Required.** Top-level SCIM attribute name (`userName`, `displayName`, `emails`, …). |
229
+ | `ldap` | Simple 1:1 mapping to an LDAP attribute. |
230
+ | `ldapPrimary` | For multi-valued SCIM attributes (`emails`, `phoneNumbers`, …), the LDAP attribute that stores the _primary_ value. |
231
+ | `ldapSecondary` | LDAP attribute that stores the _non-primary_ values of the same SCIM attribute (array). |
232
+ | `sub` | Object that maps SCIM sub-attributes to LDAP attributes. Used for complex SCIM values like `name.familyName` → `sn`. |
233
+ | `subDn` | Sub-attribute names in `sub` whose value is a DN (for example `["value"]` for `manager`); they are held to RFC 4514 on write. |
234
+ | `multi` | `"array"` or `"single"` (default) — hints array serialization for `ldap`. |
235
+ | `readOnly` | `true` to skip this attribute on write (create/update ignore it). |
236
+ | `operational` | `true` for LDAP operational attributes (e.g. `entryUUID`) — loaded on read only. |
236
237
 
237
238
  ### Merging
238
239
 
@@ -269,7 +270,8 @@ override only what's different and inherit the rest.
269
270
  },
270
271
  {
271
272
  "scim": "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager",
272
- "sub": { "value": "manager" }
273
+ "sub": { "value": "manager" },
274
+ "subDn": ["value"]
273
275
  }
274
276
  ]
275
277
  }
@@ -317,7 +319,11 @@ of them; a complex attribute takes an object whose sub-attributes are scalars
317
319
  multi-valued complex attribute such as `emails` takes a list of objects whose
318
320
  `value` is a scalar. Anything else is answered
319
321
  `400 invalidValue`, as is a PATCH operation that is not an object or whose
320
- `op` is not a string.
322
+ `op` is not a string. A reference the mapping declares a DN (`subDn`) is
323
+ refused with `400 invalidValue` when its value holds an escape RFC 4514 does
324
+ not define, as the flat routes hold a client-written DN. The directory
325
+ refuses such a value itself on an attribute of DN syntax; this names the
326
+ escape in the SCIM answer and applies wherever the mapping declares one.
321
327
 
322
328
  A PATCH `add`, or a `remove` naming values, changes only the values it names:
323
329
  values the caller's read did not show, such as group members an
@@ -715,18 +721,14 @@ Response shape (truncated):
715
721
  "id",
716
722
  "active"
717
723
  ],
718
- "mapping": [
719
- /* same shape as default-mapping.json */
720
- ]
724
+ "mapping": [/* same shape as default-mapping.json */]
721
725
  },
722
726
  {
723
727
  "id": "Group",
724
728
  "endpoint": "/scim/v2/Groups",
725
729
  "schema": "urn:ietf:params:scim:schemas:core:2.0:Group",
726
730
  "rdnAttribute": "cn",
727
- "mapping": [
728
- /* ... */
729
- ]
731
+ "mapping": [/* ... */]
730
732
  }
731
733
  ],
732
734
  "idStrategy": "rdn",
@@ -10,10 +10,13 @@ is old enough, or right away when forced.
10
10
 
11
11
  ## Behaviour
12
12
 
13
- - A delete of an entry whose DN matches `--twake-tombstone-dn` writes, in one
14
- modify: the deleted attribute, the deletion date (now), the reason, the
15
- lock, and removes `--twake-tombstone-clear-attributes`. Other entries are
16
- deleted as usual.
13
+ - A delete of an entry whose DN matches `--twake-tombstone-dn` — under any
14
+ spelling the directory folds, so spaces, case and escapes do not matter —
15
+ writes, in one modify: the deleted attribute, the deletion date (now), the
16
+ reason, the lock, and removes `--twake-tombstone-clear-attributes`. Other
17
+ entries are deleted as usual. A multi-valued RDN may not match: an
18
+ expression anchored on `uid=` misses a DN written `cn=…+uid=…`, and that
19
+ delete removes the entry for good.
17
20
  - A delete of an existing tombstone writes nothing and answers as a delete.
18
21
  It fires the `twakedeletionreplay` hook with the tombstone, so
19
22
  [lifecycle events](lifecycle-events.md) publishes the deletion again, with
@@ -62,8 +65,9 @@ Content-Type: application/json
62
65
 
63
66
  - `200`: erased.
64
67
  - `404`: no tombstone at this DN.
65
- - `409`: the deletion is younger than `--twake-tombstone-erase-min-age` and
66
- `force` is not true.
68
+ - `409`: `force` is not true, and either the deletion is younger than
69
+ `--twake-tombstone-erase-min-age` or its date cannot be read as
70
+ [lifecycle events](lifecycle-events.md) reads it.
67
71
 
68
72
  Other plugins call `erase(dn, { force, req })`.
69
73
 
@@ -453,7 +453,14 @@ Content-Type: application/json
453
453
 
454
454
  The identifier may also be the whole DN of the entry, in the entity's
455
455
  branch: `"uid": "uid=Smith\\,John,ou=users,dc=example,dc=com"` creates the
456
- entry `Smith,John`, whose DN spells the name with its escapes.
456
+ entry `Smith,John`, whose DN spells the name with its escapes. The DN must
457
+ use the escapes RFC 4514 defines; an escape it does not define (`\q`) is
458
+ refused with a `400`. Send the bare value to have the server escape it.
459
+
460
+ The name written is the value the RDN gives under the folding the directory
461
+ applies (Unicode NFKC, and its insignificant spaces): `uid=\ zoë` and
462
+ `uid=zoë` name one entry, written `uid=zoë`, and a name sent decomposed is
463
+ stored composed.
457
464
 
458
465
  ### Modify Entry
459
466
 
@@ -157,7 +157,10 @@ POST /api/v1/ldap/groups
157
157
 
158
158
  **Notes:**
159
159
 
160
- - `cn` is required
160
+ - `cn` is required; it is a name, or the group's DN in the groups branch,
161
+ whose first RDN gives the name, folded as the directory folds it. The DN
162
+ form follows the same
163
+ [escape rule as a flat entity's identifier](flat-generic.md#create-entry)
161
164
  - `member` can be a string (single member) or array (multiple members)
162
165
  - If no members provided, a dummy member is automatically added, and it is
163
166
  left out of `member` when the group is read back, on both the list and the
@@ -155,6 +155,10 @@ curl "http://localhost:8081/api/v1/users/uid%3Djohn.doe%2Cou%3Dusers%2Cdc%3Dexam
155
155
  }
156
156
  ```
157
157
 
158
+ A `pwdMaxAge` large enough to put the expiry beyond the representable date
159
+ range is reported as no expiry: `expiresAt` and `daysUntilExpiration` are
160
+ `null`, and `isExpired` stays `false`.
161
+
158
162
  ### Unlock User Account
159
163
 
160
164
  ```http
@@ -337,6 +341,9 @@ curl -X PATCH "http://localhost:8081/api/v1/password-policies/default" \
337
341
 
338
342
  - `pwdAttribute` and `pwdCheckModule` are read-only. Before OpenLDAP 2.6,
339
343
  `pwdCheckModule` is the path of a library slapd loads; set it in slapd.
344
+ - Durations and counts are integers from 0 to 9007199254740991 (2⁵³ − 1),
345
+ the largest a JSON number holds exactly: a larger value, or one sent as an
346
+ exponent such as `1e21`, is refused with a `400`.
340
347
  - `pwdCheckModuleArg`, the configuration of the quality-check module (ppm,
341
348
  for instance), is a string, usually of several lines. Writing it, or
342
349
  `pwdUseCheckModule`, gives the entry the `pwdPolicyChecker` class. Both
@@ -4,6 +4,65 @@ 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
+ ## From 1.3.0
8
+
9
+ ### A plugin with its own `service` member
10
+
11
+ **Who is affected:** a plugin built outside this repository that declares a
12
+ field, getter or method named `service`.
13
+
14
+ `DmPlugin` now has `service(token)`, which returns the instance another plugin
15
+ provides under a service token. Rename the plugin's own `service` member. See
16
+ [plugin services](../plugin-development/services.md).
17
+
18
+ ### A plugin with its own `provides` member
19
+
20
+ **Who is affected:** a plugin built outside this repository that declares a
21
+ field named `provides`.
22
+
23
+ `DmPlugin` now has `provides`, the services a plugin offers to others. Rename
24
+ the plugin's own `provides` member, or it becomes the list of services the
25
+ plugin declares. See
26
+ [plugin services](../plugin-development/services.md#providing-a-service).
27
+
28
+ ### A path parameter is percent-decoded once
29
+
30
+ **Who is affected:** a client that encoded a path parameter twice to reach an
31
+ identifier holding `%`, sending `/users/100%2525` for the entry `100%`.
32
+
33
+ The parameter is decoded once now, as RFC 3986 spells it: an identifier
34
+ holding `%` goes out as `100%25`. A parameter still encoded twice names the
35
+ escaped text, so `/users/100%2525` addresses the entry `100%25`
36
+ ([details](../client-development/api/rest-api.md#url-encoding)).
37
+
38
+ ### A paged search opens its connection on the first page
39
+
40
+ **Who is affected:** a plugin calling `ldap.search()` with paging, leaving the
41
+ returned generator unread and expecting a connection to have been opened, or
42
+ expecting a connection or hook error from the call itself.
43
+
44
+ A paged search takes its pooled connection when the first page is read, not
45
+ when `search()` is called. A generator that is never walked holds no
46
+ connection, and the next request can use the slot. A connection or bind
47
+ failure, the 30 s wait for a pooled connection and an `ldapsearchresult` hook
48
+ that refuses are answered on the first page, not by the call; a read that
49
+ must fail when it is made uses `paged: false`.
50
+
51
+ ### A deletion date written in another shape
52
+
53
+ **Who is affected:** a deployment whose writer writes the lifecycle deletion
54
+ date as something other than an ISO 8601 date or a GeneralizedTime with its
55
+ zone — for example `date '+%F %T'` (`2025-01-01 12:00:00`), an RFC 2822 date,
56
+ or a US-style `01/02/2025`.
57
+
58
+ The deletion date is read only in those two shapes now, where the default
59
+ `iso8601` mode handed anything else to `new Date()`. An unreadable date makes
60
+ `erase` answer `409` without `force`, leaves `deletedAt` out of the events,
61
+ and a rule whose payload holds only `$changed.<date>` publishes nothing. Write
62
+ an ISO 8601 date, such as with `date -u '+%Y-%m-%dT%H:%M:%SZ'`, or a
63
+ GeneralizedTime with its zone, and see
64
+ [lifecycle events](plugins/integrations/lifecycle-events.md#configuration).
65
+
7
66
  ## From 1.2.1
8
67
 
9
68
  ### A plugin written against the old hooks API
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ldap-rest",
3
- "version": "1.3.0",
3
+ "version": "2.0.0",
4
4
  "description": "Light directory manager",
5
5
  "keywords": [
6
6
  "Identity-Manager"
@@ -790,6 +790,7 @@
790
790
  "dist",
791
791
  "static",
792
792
  "docs",
793
+ "!docs/usage/options.snapshot.json",
793
794
  "examples",
794
795
  "LICENSE",
795
796
  "package.json"
@@ -805,11 +806,13 @@
805
806
  "build:pages": "tsx scripts/build-pages.ts",
806
807
  "build:prod": "npm run clean && NODE_ENV=production rollup -c && npm run build:browser && node scripts/moveBrowserLibs.mjs && npm run build:dockerfile",
807
808
  "build:watch": "rollup -c --watch",
808
- "check": "npm run check:ts && npm run lint && npm run format:check && npm run check:hooks && npm run check:no-client-values && npm run check:openapi",
809
+ "check": "npm run check:ts && npm run lint && npm run format:check && npm run check:hooks && npm run check:no-client-values && npm run check:openapi && npm run check:options",
809
810
  "check:hooks": "tsx scripts/hooks-doc.ts",
810
811
  "check:no-client-values": "sh scripts/check-no-client-values.sh",
811
812
  "check:openapi": "tsx scripts/check-openapi.ts",
812
- "check:ts": "tsc --noEmit",
813
+ "check:options": "tsx scripts/options-snapshot.ts --check",
814
+ "check:ts": "node ./node_modules/typescript/bin/tsc --noEmit",
815
+ "check:ts7": "node ./node_modules/typescript7/bin/tsc --noEmit",
813
816
  "clean": "rimraf dist/* static/browser/* coverage",
814
817
  "coverage": "c8 --config .c8rc.json mocha",
815
818
  "coverage:check": "npm run check:ts && c8 --config .c8rc.json --exclude 'src/browser/**' --check-coverage --statements 84 --lines 84 --branches 75 --functions 86 mocha",
@@ -820,8 +823,9 @@
820
823
  "format:fix": "prettier --write .",
821
824
  "format:fix-diff": "prettier --write $(git checkout|grep -E '^(M|A)'|cut -f2)",
822
825
  "generate:openapi": "tsx scripts/generate-openapi.ts",
823
- "lint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
824
- "lint:fix": "eslint \"src/**/*.{ts,tsx,js,jsx}\" --fix",
826
+ "lint": "eslint \"src/**/*.{ts,tsx,js,jsx}\" \"test/**/*.ts\"",
827
+ "lint:fix": "eslint \"src/**/*.{ts,tsx,js,jsx}\" \"test/**/*.ts\" --fix",
828
+ "snapshot:options": "tsx scripts/options-snapshot.ts",
825
829
  "start:dev": "./.dev.mk start",
826
830
  "test": "npm run check:ts && mocha",
827
831
  "test:dev": "./.dev.mk test",
@@ -829,9 +833,8 @@
829
833
  },
830
834
  "overrides": {
831
835
  "diff": ">=8.0.3",
832
- "fast-xml-parser": ">=5.3.6",
833
- "glob": ">=11.0.4",
834
- "minimatch": "^10.2.1",
836
+ "glob": ">=11.1.0",
837
+ "minimatch": "^10.2.3",
835
838
  "postcss-selector-parser": "^7.1.6",
836
839
  "serialize-javascript": ">=7.0.3"
837
840
  },
@@ -849,6 +852,7 @@
849
852
  "winston": "^3.17.0"
850
853
  },
851
854
  "devDependencies": {
855
+ "@eslint/js": "^10.0.1",
852
856
  "@rollup/plugin-commonjs": "^29.0.3",
853
857
  "@rollup/plugin-json": "^6.1.0",
854
858
  "@rollup/plugin-terser": "^1.0.2",
@@ -865,15 +869,15 @@
865
869
  "@typescript-eslint/parser": "^8.44.0",
866
870
  "c8": "^12.0.0",
867
871
  "chai": "^6.0.1",
868
- "eslint": "^9.39.0",
872
+ "eslint": "^10.13.0",
869
873
  "eslint-config-prettier": "^10.1.8",
870
- "eslint-plugin-import": "^2.32.0",
874
+ "eslint-plugin-import-x": "^4.17.1",
871
875
  "eslint-plugin-prettier": "^5.5.4",
872
876
  "globals": "^17.13.0",
873
877
  "js-yaml": "^5.5.0",
874
878
  "mocha": "^12.0.0",
875
879
  "nock": "^14.0.10",
876
- "prettier": "^3.6.2",
880
+ "prettier": "^3.9.10",
877
881
  "rimraf": "^6.0.1",
878
882
  "rollup": "^4.50.2",
879
883
  "rollup-plugin-postcss": "^4.0.2",
@@ -882,7 +886,8 @@
882
886
  "tinyglobby": "^0.2.17",
883
887
  "tslib": "^2.8.1",
884
888
  "tsx": "^4.20.5",
885
- "typescript": "^5.9.2"
889
+ "typescript": "^6.0.3",
890
+ "typescript7": "npm:typescript@^7.0.2"
886
891
  },
887
892
  "optionalDependencies": {
888
893
  "@linagora/rabbitmq-client": "^0.7.2",
@@ -1 +1 @@
1
- {"version":3,"file":"LdapTreeViewer.js","sources":["../../../../src/browser/ldap-tree-viewer/LdapTreeViewer.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LdapTreeViewer.js","sources":["../../../../src/browser/ldap-tree-viewer/LdapTreeViewer.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}