ldap-rest 1.1.0 → 1.2.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 (97) hide show
  1. package/bin/index.mjs +8 -7
  2. package/dist/abstract/ldapFlat.js +1 -1
  3. package/dist/abstract/plugin.js +1 -1
  4. package/dist/abstract/twakePlugin.js +1 -1
  5. package/dist/bin/index.js +1 -1
  6. package/dist/config/args.js +1 -1
  7. package/dist/config/schema.js +1 -1
  8. package/dist/lib/changeContext.js +1 -1
  9. package/dist/lib/entitySchema/validator.js +1 -0
  10. package/dist/lib/expressFormatedResponses.js +1 -1
  11. package/dist/lib/health.js +1 -0
  12. package/dist/lib/instanceProviders.js +1 -1
  13. package/dist/lib/ldapActions.js +1 -1
  14. package/dist/lib/metrics.js +1 -0
  15. package/dist/lib/requestContext.js +1 -1
  16. package/dist/lib/shutdown.js +1 -0
  17. package/dist/lib/staticFiles.js +1 -0
  18. package/dist/lib/storage/store.js +1 -1
  19. package/dist/lib/utils.js +1 -1
  20. package/dist/logger/winston.js +1 -1
  21. package/dist/plugins/auth/crowdsec.js +1 -1
  22. package/dist/plugins/auth/openidconnect.js +1 -1
  23. package/dist/plugins/demo/helloworld.js +1 -1
  24. package/dist/plugins/ldap/bulkImport.js +1 -1
  25. package/dist/plugins/ldap/flatGeneric.js +1 -1
  26. package/dist/plugins/ldap/groups.js +1 -1
  27. package/dist/plugins/ldap/organizations.js +1 -1
  28. package/dist/plugins/metrics.js +1 -0
  29. package/dist/plugins/rabbitmq.js +1 -1
  30. package/dist/plugins/scim/scim.js +1 -1
  31. package/dist/plugins/static.js +1 -1
  32. package/dist/plugins/storage/file.js +1 -1
  33. package/dist/plugins/storage/postgres.js +1 -1
  34. package/dist/plugins/storage/valkey.js +1 -1
  35. package/dist/plugins/storage.js +1 -1
  36. package/dist/plugins/twake/calendar.js +1 -1
  37. package/dist/plugins/twake/clouderyProvision.js +1 -1
  38. package/dist/plugins/twake/cozyProvision.js +1 -1
  39. package/dist/plugins/twake/drive.js +1 -1
  40. package/dist/plugins/twake/james.js +1 -1
  41. package/dist/plugins/twake/migrationProxy.js +1 -0
  42. package/dist/plugins/weblogs.js +1 -1
  43. package/dist/src/abstract/ldapFlat.d.ts +3 -13
  44. package/dist/src/abstract/plugin.d.ts +20 -0
  45. package/dist/src/abstract/twakePlugin.d.ts +23 -1
  46. package/dist/src/bin/index.d.ts +46 -1
  47. package/dist/src/config/args.d.ts +12 -1
  48. package/dist/src/lib/changeContext.d.ts +11 -0
  49. package/dist/src/lib/entitySchema/validator.d.ts +64 -0
  50. package/dist/src/lib/expressFormatedResponses.d.ts +19 -0
  51. package/dist/src/lib/health.d.ts +63 -0
  52. package/dist/src/lib/instanceProviders.d.ts +4 -0
  53. package/dist/src/lib/ldapActions.d.ts +28 -0
  54. package/dist/src/lib/metrics.d.ts +44 -0
  55. package/dist/src/lib/requestContext.d.ts +40 -0
  56. package/dist/src/lib/shutdown.d.ts +20 -0
  57. package/dist/src/lib/staticFiles.d.ts +33 -0
  58. package/dist/src/lib/storage/store.d.ts +8 -0
  59. package/dist/src/lib/utils.d.ts +9 -0
  60. package/dist/src/logger/winston.d.ts +4 -0
  61. package/dist/src/plugins/ldap/groups.d.ts +18 -3
  62. package/dist/src/plugins/ldap/organizations.d.ts +3 -5
  63. package/dist/src/plugins/metrics.d.ts +17 -0
  64. package/dist/src/plugins/rabbitmq.d.ts +37 -1
  65. package/dist/src/plugins/static.d.ts +12 -0
  66. package/dist/src/plugins/storage/file.d.ts +4 -0
  67. package/dist/src/plugins/storage/postgres.d.ts +7 -14
  68. package/dist/src/plugins/storage/valkey.d.ts +2 -15
  69. package/dist/src/plugins/storage.d.ts +8 -0
  70. package/dist/src/plugins/twake/drive.d.ts +8 -0
  71. package/dist/src/plugins/twake/james.d.ts +2 -0
  72. package/dist/src/plugins/twake/migrationProxy.d.ts +71 -0
  73. package/docs/client-development/api/reference.md +5 -9
  74. package/docs/client-development/api/rest-api.md +4 -10
  75. package/docs/client-development/schemas/README.md +60 -21
  76. package/docs/plugin-development/README.md +15 -13
  77. package/docs/plugin-development/dependencies.md +8 -0
  78. package/docs/plugin-development/hooks.md +1 -1
  79. package/docs/usage/README.md +1 -0
  80. package/docs/usage/configuration.md +49 -11
  81. package/docs/usage/operations.md +193 -0
  82. package/docs/usage/plugins/README.md +2 -0
  83. package/docs/usage/plugins/auth/authz-per-branch.md +2 -1
  84. package/docs/usage/plugins/integrations/README.md +8 -4
  85. package/docs/usage/plugins/integrations/mail-migration.md +102 -0
  86. package/docs/usage/plugins/integrations/scim.md +4 -2
  87. package/docs/usage/plugins/ldap/bulk-import.md +3 -1
  88. package/docs/usage/plugins/ldap/flat-generic.md +5 -2
  89. package/docs/usage/plugins/ldap/on-change.md +2 -1
  90. package/docs/usage/plugins/ldap/organizations.md +7 -7
  91. package/docs/usage/plugins/utilities/metrics.md +21 -0
  92. package/docs/usage/plugins/utilities/static.md +16 -8
  93. package/docs/usage/plugins/utilities/storage.md +7 -0
  94. package/docs/usage/plugins/utilities/weblogs.md +51 -30
  95. package/docs/usage/upgrading.md +71 -0
  96. package/package.json +11 -2
  97. package/static/schemas/example/README.md +1 -0
@@ -18,6 +18,9 @@ No additional configuration required.
18
18
 
19
19
  ### Request Logging
20
20
 
21
+ Each line carries the `requestId` of the answer's `X-Request-Id`, see
22
+ [operations](../../operations.md#request-id).
23
+
21
24
  Each request is logged when it arrives:
22
25
 
23
26
  ```
@@ -31,26 +34,31 @@ Each response is logged when completed at **notice** level (between info and war
31
34
  ```json
32
35
  {
33
36
  "level": "notice",
34
- "message": "GET /api/v1/ldap/users 200 45ms",
35
- "user": "admin",
36
- "method": "GET",
37
- "url": "/api/v1/ldap/users",
38
- "status": 200,
39
- "responseTime": "45ms"
37
+ "message": {
38
+ "requestId": "6effd260-b079-4335-b727-544189ed33a6",
39
+ "method": "GET",
40
+ "url": "/api/v1/ldap/users",
41
+ "status": 200,
42
+ "duration": 45,
43
+ "ip": "10.0.9.12",
44
+ "user": "admin"
45
+ },
46
+ "requestId": "6effd260-b079-4335-b727-544189ed33a6",
47
+ "timestamp": "2026-10-26T18:07:03.286Z"
40
48
  }
41
49
  ```
42
50
 
43
51
  ## Log Fields
44
52
 
45
- | Field | Description | Example |
46
- | -------------- | ------------------------------- | --------------------------------- |
47
- | `message` | Summary line | `GET /api/v1/ldap/users 200 45ms` |
48
- | `user` | Authenticated user (if any) | `admin` |
49
- | `method` | HTTP method | `GET`, `POST`, `PUT`, `DELETE` |
50
- | `url` | Full URL including query params | `/api/v1/ldap/users?match=*` |
51
- | `status` | HTTP status code | `200`, `404`, `500` |
52
- | `responseTime` | Request duration | `45ms`, `123ms` |
53
- | `error` | Error message (if applicable) | `Connection timeout` |
53
+ | Field | Description | Example |
54
+ | ----------- | ------------------------------- | ------------------------------ |
55
+ | `user` | Authenticated user (if any) | `admin` |
56
+ | `requestId` | The answer's `X-Request-Id` | `6effd260-b079-…` |
57
+ | `method` | HTTP method | `GET`, `POST`, `PUT`, `DELETE` |
58
+ | `url` | Full URL including query params | `/api/v1/ldap/users?match=*` |
59
+ | `status` | HTTP status code | `200`, `404`, `500` |
60
+ | `duration` | Request duration, in ms | `45`, `123` |
61
+ | `error` | Error message (if applicable) | `Connection timeout` |
54
62
 
55
63
  ## Enriching the Log Line From Another Plugin
56
64
 
@@ -77,6 +85,7 @@ Output:
77
85
  "level": "notice",
78
86
  "message": {
79
87
  "uids": ["jdoe", "asmith"],
88
+ "requestId": "6effd260-b079-4335-b727-544189ed33a6",
80
89
  "method": "POST",
81
90
  "url": "/api/_getldapentries",
82
91
  "status": 200,
@@ -89,7 +98,7 @@ Output:
89
98
 
90
99
  Notes:
91
100
 
92
- - Core fields (`method`, `url`, `status`, `duration`, `ip`, `user`, `error`)
101
+ - Core fields (`requestId`, `method`, `url`, `status`, `duration`, `ip`, `user`, `error`)
93
102
  always take precedence: a plugin cannot overwrite them.
94
103
  - The value must be set **before** the response completes (i.e. before
95
104
  `res.json()` / `res.end()`), since the entry is written on the `finish` event.
@@ -319,27 +328,40 @@ res.on('close', () => {
319
328
  Logs are in JSON format for easy parsing:
320
329
 
321
330
  ```bash
322
- cat app.log | jq 'select(.status >= 400)'
331
+ cat app.log | jq 'select(.message.status? >= 400)'
323
332
  ```
324
333
 
325
334
  Filters 4xx and 5xx responses:
326
335
 
327
336
  ```json
328
337
  {
329
- "level": "info",
330
- "message": "GET /api/v1/ldap/users/notfound 404 12ms",
331
- "status": 404,
332
- "method": "GET",
333
- "url": "/api/v1/ldap/users/notfound"
338
+ "level": "notice",
339
+ "message": {
340
+ "requestId": "6effd260-b079-4335-b727-544189ed33a6",
341
+ "method": "GET",
342
+ "url": "/api/v1/ldap/users/notfound",
343
+ "status": 404,
344
+ "duration": 12,
345
+ "ip": "10.0.9.12"
346
+ },
347
+ "requestId": "6effd260-b079-4335-b727-544189ed33a6",
348
+ "timestamp": "2026-10-26T18:07:03.286Z"
334
349
  }
335
350
  ```
336
351
 
352
+ The lines of one request, the access line and what the server logged while
353
+ serving it:
354
+
355
+ ```bash
356
+ cat app.log | jq 'select(.requestId == "6effd260-b079-4335-b727-544189ed33a6")'
357
+ ```
358
+
337
359
  ### Statistics
338
360
 
339
361
  Generate request statistics:
340
362
 
341
363
  ```bash
342
- cat app.log | jq -r '.message' | awk '{print $1}' | sort | uniq -c
364
+ cat app.log | jq -r 'select(.message.method?) | .message.method' | sort | uniq -c
343
365
  ```
344
366
 
345
367
  Output:
@@ -356,8 +378,7 @@ Output:
356
378
  Average response time per endpoint:
357
379
 
358
380
  ```bash
359
- cat app.log | jq -r 'select(.responseTime) | "\(.url) \(.responseTime)"' | \
360
- sed 's/ms$//' | \
381
+ cat app.log | jq -r 'select(.message.duration?) | "\(.message.url) \(.message.duration)"' | \
361
382
  awk '{sum[$1]+=$2; count[$1]++} END {for (url in sum) print url, sum[url]/count[url] "ms"}'
362
383
  ```
363
384
 
@@ -377,16 +398,16 @@ For high-traffic deployments:
377
398
 
378
399
  ## Log Rotation
379
400
 
380
- Configure log rotation using environment or external tools:
401
+ Rotate the log file with external tools:
381
402
 
382
- ### Using Winston File Transport
403
+ ### Writing to a file
383
404
 
384
405
  ```bash
385
- --log-file ./logs/ldap-rest.log \
386
- --log-max-size 10485760 \ # 10MB
387
- --log-max-files 5
406
+ --logger file --log-file ./logs/ldap-rest.log
388
407
  ```
389
408
 
409
+ The server does not rotate the file itself.
410
+
390
411
  ### Using logrotate (Linux)
391
412
 
392
413
  ```
@@ -4,6 +4,77 @@ 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.1 to 1.2.0
8
+
9
+ ### The process stops on an uncaught exception
10
+
11
+ **Who is affected:** every deployment; a supervisor, Kubernetes or systemd,
12
+ restarts the process, as it should.
13
+
14
+ An exception nothing caught was logged and the process kept serving in a
15
+ state nobody knew. The binary now shuts down and exits with 1. On
16
+ `SIGTERM` it also drains what is in flight within `--shutdown-timeout`
17
+ (20 s) and closes its connections before the signal ends it, where the
18
+ signal ended the process at once, the broker connection closed first when
19
+ `core/rabbitmq` was loaded. A host assembling the server in its own
20
+ process gets neither unless it asks: `run()` no longer installs the handlers
21
+ that logged an uncaught exception or an unhandled rejection and kept the
22
+ process alive, so without `exitOnCrash()` or handlers of its own, Node's
23
+ action applies and the process ends on either. See
24
+ [shutdown](operations.md#shutdown).
25
+
26
+ ### The readiness of a pod follows the directory
27
+
28
+ **Who is affected:** a deployment of the Helm chart, whose probes now call
29
+ `GET /healthz` and `GET /readyz` instead of opening the listen port.
30
+
31
+ A pod leaves the endpoints while the directory does not answer, where it
32
+ kept receiving requests and answering 500; a rollout started during a
33
+ directory outage waits for the directory. Both routes are served without
34
+ authentication: an Ingress routing `/` exposes them, keep the two paths to
35
+ the cluster. See [operations](operations.md#health-and-readiness).
36
+
37
+ ### `--ldap-dn` and `--ldap-pwd` are required
38
+
39
+ **Who is affected:** a deployment that left one of them unset, and so bound
40
+ as `cn=admin,dc=example,dc=com` or with the password `admin`; the published
41
+ Docker image carried both in its `ENV`, and the Helm chart's example only set
42
+ the password.
43
+
44
+ The server now stops at startup naming the missing option. Set `DM_LDAP_DN`
45
+ and `DM_LDAP_PWD` (or `--ldap-dn` and `--ldap-pwd`) to the values that were
46
+ implied.
47
+
48
+ ### Every entity route checks the shape of a value
49
+
50
+ **Who is affected:** a script or connector writing through `/api/v1/ldap/<entity>`,
51
+ `/api/v1/ldap/groups` or `/api/v1/ldap/organizations` text that is not a
52
+ number for a `number` or `integer`, a `delete` of a `required` attribute as
53
+ a whole, or a change naming an attribute a strict schema does not declare;
54
+ one writing through `/api/v1/ldap/<entity>` several values for an attribute
55
+ the schema does not type `array`; and one changing a `fixed` attribute of a
56
+ group, which the server dropped from the change and answered 200. The
57
+ server now answers 400, naming the attribute. A group creation sending a
58
+ value under a `fixed` attribute now writes the schema's default, or the
59
+ value when the schema gives none, where it wrote nothing. The shipped LSC connector sends shapes that stay
60
+ accepted, a list of one in a `replace` and one value for an `array` on a
61
+ creation; its whole-attribute delete is refused for a `required` attribute,
62
+ as any, and a group synchronisation that forces `objectClass` or another
63
+ `fixed` attribute now has the whole update refused: keep those attributes
64
+ out of the group updates.
65
+
66
+ Send one value for a single-valued attribute, a list for an `array` (one
67
+ value, or a list of one, is taken either way), numbers as numbers or digits,
68
+ and `null` or `[]` in a `replace` to clear an attribute. The rules are in
69
+ [what a value must look like](../client-development/schemas/README.md#what-a-value-must-look-like).
70
+
71
+ ### `--logger`: `console` or `file`
72
+
73
+ A `DM_LOGGER` set to anything else wrote `error.log` in the working
74
+ directory; the server now stops at startup. Set `DM_LOGGER=file` to keep
75
+ that file, and `DM_LOG_FILE` to name another one. An empty `DM_LOGGER` is
76
+ `console`.
77
+
7
78
  ## From 1.0 to 1.1.0
8
79
 
9
80
  ### The static files can no longer be framed by another origin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ldap-rest",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Light directory manager",
5
5
  "keywords": [
6
6
  "Identity-Manager"
@@ -160,6 +160,10 @@
160
160
  "types": "./dist/src/plugins/ldap/trash.d.ts",
161
161
  "import": "./dist/plugins/ldap/trash.js"
162
162
  },
163
+ "./plugin-metrics": {
164
+ "types": "./dist/src/plugins/metrics.d.ts",
165
+ "import": "./dist/plugins/metrics.js"
166
+ },
163
167
  "./plugin-rabbitmq": {
164
168
  "types": "./dist/src/plugins/rabbitmq.d.ts",
165
169
  "import": "./dist/plugins/rabbitmq.js"
@@ -288,6 +292,10 @@
288
292
  "types": "./dist/src/plugins/twake/lifecycleEvents.d.ts",
289
293
  "import": "./dist/plugins/twake/lifecycleEvents.js"
290
294
  },
295
+ "./plugin-twake-migrationproxy": {
296
+ "types": "./dist/src/plugins/twake/migrationProxy.d.ts",
297
+ "import": "./dist/plugins/twake/migrationProxy.js"
298
+ },
291
299
  "./plugin-twake-spaces": {
292
300
  "types": "./dist/src/plugins/twake/spaces.d.ts",
293
301
  "import": "./dist/plugins/twake/spaces.js"
@@ -754,6 +762,7 @@
754
762
  "multer": "^2.0.2",
755
763
  "node-fetch": "^3.3.2",
756
764
  "p-limit": "^7.1.1",
765
+ "prom-client": "^15.1.3",
757
766
  "winston": "^3.17.0"
758
767
  },
759
768
  "devDependencies": {
@@ -795,7 +804,7 @@
795
804
  "typescript": "^5.9.2"
796
805
  },
797
806
  "optionalDependencies": {
798
- "@linagora/rabbitmq-client": "^0.1.2",
807
+ "@linagora/rabbitmq-client": "^0.7.2",
799
808
  "express-openid-connect": "^2.19.2",
800
809
  "iovalkey": "^0.4.0",
801
810
  "lemonldap-ng-handler": "^0.7.3",
@@ -20,6 +20,7 @@ Point a server at them with:
20
20
  ```sh
21
21
  node bin/index.mjs \
22
22
  --ldap-base dc=example,dc=org \
23
+ --ldap-dn cn=admin,dc=example,dc=org --ldap-pwd secret \
23
24
  --ldap-top-organization ou=organization,dc=example,dc=org \
24
25
  --ldap-group-base ou=lists,ou=groups,dc=example,dc=org \
25
26
  --group-schema static/schemas/example/groups.json \