ldap-rest 0.16.2 → 0.17.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 (39) hide show
  1. package/bin/sync-james.mjs +4 -4
  2. package/dist/abstract/ldapFlat.js +1 -1
  3. package/dist/abstract/twakePlugin.js +1 -1
  4. package/dist/bin/index.js +1 -1
  5. package/dist/config/args.js +1 -1
  6. package/dist/lib/ldapActions.js +1 -1
  7. package/dist/lib/ldapCodes.js +1 -1
  8. package/dist/lib/schemaFile.js +1 -0
  9. package/dist/plugins/ldap/bulkImport.js +1 -1
  10. package/dist/plugins/ldap/flatGeneric.js +1 -1
  11. package/dist/plugins/ldap/groups.js +1 -1
  12. package/dist/plugins/ldap/onChange.js +1 -1
  13. package/dist/plugins/ldap/organizations.js +1 -1
  14. package/dist/plugins/scim/discovery.js +1 -1
  15. package/dist/plugins/scim/errors.js +1 -1
  16. package/dist/plugins/static.js +1 -1
  17. package/dist/plugins/twake/james.js +1 -1
  18. package/dist/src/abstract/ldapFlat.d.ts +9 -0
  19. package/dist/src/abstract/twakePlugin.d.ts +18 -1
  20. package/dist/src/bin/index.d.ts +10 -0
  21. package/dist/src/config/args.d.ts +2 -0
  22. package/dist/src/hooks.d.ts +3 -1
  23. package/dist/src/lib/ldapCodes.d.ts +7 -0
  24. package/dist/src/lib/schemaFile.d.ts +64 -0
  25. package/dist/src/plugins/ldap/flatGeneric.d.ts +7 -0
  26. package/dist/src/plugins/ldap/groups.d.ts +9 -0
  27. package/dist/src/plugins/static.d.ts +15 -1
  28. package/dist/src/plugins/twake/james.d.ts +1 -0
  29. package/docs/client-development/api/rest-api.md +26 -0
  30. package/docs/client-development/schemas/README.md +86 -0
  31. package/docs/plugin-development/dependencies.md +6 -0
  32. package/docs/presentations/en.md +2 -2
  33. package/docs/presentations/fr.md +2 -2
  34. package/docs/usage/configuration.md +9 -1
  35. package/docs/usage/plugins/integrations/james-mail.md +44 -14
  36. package/docs/usage/plugins/ldap/on-change.md +7 -1
  37. package/docs/usage/upgrading.md +14 -0
  38. package/package.json +1 -1
  39. package/static/schemas/twake/users.json +0 -10
@@ -13,6 +13,7 @@ LDAP-Rest uses JSON schemas to define LDAP entity structure, validation rules, a
13
13
  - [Semantic Roles](#semantic-roles)
14
14
  - [Validation Rules](#validation-rules)
15
15
  - [Predefined Schemas](#predefined-schemas)
16
+ - [Extending a Schema](#extending-a-schema)
16
17
 
17
18
  ---
18
19
 
@@ -1055,6 +1056,91 @@ npx ldap-rest \
1055
1056
 
1056
1057
  ---
1057
1058
 
1059
+ ## Extending a Schema
1060
+
1061
+ To change a few points of a schema, shipped or not, write a file holding only
1062
+ the differences and naming the schema it `extends`. The deployment keeps the
1063
+ later fixes of the base schema, which a copy would miss:
1064
+
1065
+ ```json
1066
+ {
1067
+ "extends": "/app/node_modules/ldap-rest/static/schemas/twake/users.json",
1068
+ "attributes": { "personalTitle": null }
1069
+ }
1070
+ ```
1071
+
1072
+ Point the option at this file as at any schema:
1073
+ `DM_LDAP_FLAT_SCHEMA=/etc/ldap-rest/schemas/users.json`.
1074
+
1075
+ ### Naming the Base
1076
+
1077
+ `extends` takes:
1078
+
1079
+ - `ldap-rest:<path>`: a schema shipped with LDAP-Rest, `<path>` being relative
1080
+ to its `static/schemas` directory, wherever the package is installed and
1081
+ whatever `--schemas-path` says: `"extends": "ldap-rest:twake/users.json"`
1082
+ - an absolute path
1083
+ - any other path, relative to the directory of the extending file:
1084
+ `"extends": "../base/users.json"`
1085
+
1086
+ The base may extend another file in turn. A loop (`a.json` extends `b.json`,
1087
+ which extends `a.json`) is an error.
1088
+
1089
+ ### Merge Rules
1090
+
1091
+ The extending file is applied on its base:
1092
+
1093
+ - objects are merged key by key, at every depth; the extending file wins
1094
+ - `null` deletes the key
1095
+ - arrays and other values replace those of the base: an `objectClass` list
1096
+ must be written whole
1097
+ - `extends` itself is not part of the result
1098
+
1099
+ Removing an attribute and changing only the French label of another:
1100
+
1101
+ ```json
1102
+ {
1103
+ "extends": "ldap-rest:twake/users.json",
1104
+ "attributes": {
1105
+ "personalTitle": null,
1106
+ "cn": { "label": { "fr": "Nom complet" } }
1107
+ }
1108
+ }
1109
+ ```
1110
+
1111
+ An attribute added by the extending file comes after the inherited ones, and
1112
+ so does its field in the forms built from the schema.
1113
+
1114
+ The `__ldap_base__` style placeholders are replaced in each file of the chain
1115
+ before the merge: inherited values get them, and `extends` may hold one.
1116
+ `{ldap_base}` in `entity.base` is resolved on the merged schema.
1117
+
1118
+ ### Where It Applies
1119
+
1120
+ Every schema file is read this way: `--ldap-flat-schema`, `--group-schema`,
1121
+ `--organization-schema`, `--bulk-import-schemas`, the SCIM schemas of
1122
+ `--schemas-path`, and the schemas `core/static` serves. A file whose base is
1123
+ missing or invalid is not loaded, as an unreadable file; `core/static` answers
1124
+ it with a 500.
1125
+
1126
+ ### Served Schemas
1127
+
1128
+ `core/static` serves the merged schema. The configuration API gives each
1129
+ entity its merged schema inline (`schema`) and, with `core/static`, a
1130
+ `schemaUrl` made of the file path from its first `schemas` directory:
1131
+ `/etc/ldap-rest/schemas/users.json` gets `/static/schemas/users.json`.
1132
+ `core/static` serves a configured schema file at that URL even when it is
1133
+ outside `--static-path`, and before a shipped schema of the same URL:
1134
+ `/etc/ldap-rest/schemas/twake/users.json` is what
1135
+ `/static/schemas/twake/users.json` returns. An option left at its default
1136
+ does not count: without `--group-schema`, a `--static-path` of your own
1137
+ still serves its `schemas/twake/groups.json`. If two configured files get
1138
+ the same URL, the first one is served there and a warning names the other.
1139
+ A file outside any `schemas` directory has no `schemaUrl`; its schema is
1140
+ still inline.
1141
+
1142
+ ---
1143
+
1058
1144
  ## Best Practices
1059
1145
 
1060
1146
  ### 1. Always Define ObjectClass
@@ -18,6 +18,12 @@ dependencies = {
18
18
  };
19
19
  ```
20
20
 
21
+ A dependency is loaded when the plugin registers, unless a plugin of that
22
+ name already is. A module requested again under the same name and without
23
+ overrides — a dependency also given to `--plugin`, or an entry listed twice —
24
+ is loaded once: the second request waits for the first. Only an entry with
25
+ overrides builds a second instance, and it needs a name of its own.
26
+
21
27
  ### Hook Dependencies
22
28
 
23
29
  **Hook Providers**: Plugins that implement hooks (via `hooks: Hooks = { ... }`)
@@ -148,7 +148,7 @@ James WebAdmin: POST /users/alice@.../rename/alice.smith@...
148
148
  ## 2. Quota Update
149
149
 
150
150
  ```
151
- LDAP: mailQuota = 1000000000 → 5000000000 (1GB → 5GB)
151
+ LDAP: mailQuotaSize = 1000000000 → 5000000000 (1GB → 5GB)
152
152
  ↓ onChange detects the change
153
153
  ↓ Hook onLdapQuotaChange triggered
154
154
  ↓
@@ -584,7 +584,7 @@ npx ldap-rest \
584
584
  --james-webadmin-url http://james:8000 \
585
585
  --james-webadmin-token "admin-token" \
586
586
  --mail-attribute mail \
587
- --quota-attribute mailQuota \
587
+ --quota-attribute mailQuotaSize \
588
588
  --alias-attribute mailAlternateAddress
589
589
  ```
590
590
 
@@ -148,7 +148,7 @@ James WebAdmin: POST /users/alice@.../rename/alice.smith@...
148
148
  ## 2. Mise à jour de quota
149
149
 
150
150
  ```
151
- LDAP: mailQuota = 1000000000 → 5000000000 (1GB → 5GB)
151
+ LDAP: mailQuotaSize = 1000000000 → 5000000000 (1GB → 5GB)
152
152
  ↓ onChange détecte le changement
153
153
  ↓ Hook onLdapQuotaChange déclenché
154
154
  ↓
@@ -584,7 +584,7 @@ npx ldap-rest \
584
584
  --james-webadmin-url http://james:8000 \
585
585
  --james-webadmin-token "admin-token" \
586
586
  --mail-attribute mail \
587
- --quota-attribute mailQuota \
587
+ --quota-attribute mailQuotaSize \
588
588
  --alias-attribute mailAlternateAddress
589
589
  ```
590
590
 
@@ -156,6 +156,10 @@ to keep the default.
156
156
  | `--mail-domain` | `--mail-domains` | `DM_MAIL_DOMAIN` | `[]` | Mail domains |
157
157
  | `--schemas-path` | | `DM_SCHEMAS_PATH` | `static/schemas` | Path to JSON schemas |
158
158
 
159
+ A schema file named by an option may extend another one, shipped or not, and
160
+ hold only the differences:
161
+ [extending a schema](../client-development/schemas/README.md#extending-a-schema).
162
+
159
163
  ## LDAP Connection
160
164
 
161
165
  | CLI | Plural | Env | Default | Description |
@@ -192,7 +196,7 @@ at `0` otherwise.
192
196
  | CLI | Env | Default | Description |
193
197
  | -------------------------- | --------------------------- | ----------------------- | ---------------------- |
194
198
  | `--mail-attribute` | `DM_MAIL_ATTRIBUTE` | `mail` | Email attribute |
195
- | `--quota-attribute` | `DM_QUOTA_ATTRIBUTE` | `mailQuota` | Quota attribute |
199
+ | `--quota-attribute` | `DM_QUOTA_ATTRIBUTE` | `mailQuotaSize` | Quota attribute |
196
200
  | `--delegation-attribute` | `DM_DELEGATION_ATTRIBUTE` | `twakeDelegatedUsers` | Delegation attribute |
197
201
  | `--alias-attribute` | `DM_ALIAS_ATTRIBUTE` | `mailAlternateAddress` | Email alias attribute |
198
202
  | `--forward-attribute` | `DM_FORWARD_ATTRIBUTE` | `mailForwardingAddress` | Forward attribute |
@@ -455,10 +459,14 @@ whichever of the two names it selects. A value other than `req.user` or
455
459
  | `--james-signature-template` | | `DM_JAMES_SIGNATURE_TEMPLATE` | | Email signature template |
456
460
  | `--james-concurrency` | | `DM_JAMES_CONCURRENCY` | `10` | James API concurrency |
457
461
  | `--james-init-delay` | | `DM_JAMES_INIT_DELAY` | `1000` | Init delay (ms) |
462
+ | `--james-retry-attempts` | | `DM_JAMES_RETRY_ATTEMPTS` | `5` | Attempts per user call |
463
+ | `--james-retry-delay` | | `DM_JAMES_RETRY_DELAY` | `1000` | Base retry delay (ms) |
458
464
  | `--james-mailing-list-branch` | `--james-mailing-list-branches` | `DM_JAMES_MAILING_LIST_BRANCHES` | `[]` | Mailing list branches |
459
465
  | `--james-mailbox-type-attribute` | | `DM_JAMES_MAILBOX_TYPE_ATTRIBUTE` | `twakeMailboxType` | Mailbox type attribute |
460
466
  | `--ldap-concurrency` | | `DM_LDAP_CONCURRENCY` | `10` | LDAP operations concurrency |
461
467
 
468
+ The wait before each retry grows with the attempt, so the total wait is `--james-retry-delay` × n(n−1)/2 for n attempts: 10 s with the defaults, 190 s with 20 attempts.
469
+
462
470
  #### `core/twake/calendar`
463
471
 
464
472
  Formerly `core/twake/calendarResources`, still accepted as a deprecated alias.
@@ -20,7 +20,7 @@ The `twake/james` plugin automatically synchronizes email address and quota chan
20
20
  --plugin twake/james \
21
21
  --ldap-flat-schema ./static/schemas/twake/users.json \
22
22
  --mail-attribute mail \
23
- --quota-attribute mailQuota \
23
+ --quota-attribute mailQuotaSize \
24
24
  --james-webadmin-url http://james:8000 \
25
25
  --james-webadmin-token "your-admin-token"
26
26
  ```
@@ -37,9 +37,11 @@ DM_JAMES_WEBADMIN_TOKEN="your-admin-token"
37
37
  - `--james-webadmin-url`: James WebAdmin API base URL (required)
38
38
  - `--james-webadmin-token`: Bearer token for James WebAdmin authentication (optional)
39
39
  - `--mail-attribute`: LDAP attribute for email (default: `mail`)
40
- - `--quota-attribute`: LDAP attribute for quota (default: `mailQuota`)
40
+ - `--quota-attribute`: LDAP attribute for quota (default: `mailQuotaSize`)
41
41
  - `--alias-attribute`: LDAP attribute for email aliases (default: `mailAlternateAddress`)
42
42
  - `--james-mailbox-type-attribute`: LDAP attribute for mailbox type (default: `twakeMailboxType`)
43
+ - `--james-retry-attempts`: attempts in all of a call about a user James may not see yet (default: `5`), see [Replication lag](#replication-lag)
44
+ - `--james-retry-delay`: base delay between those attempts, in ms (default: `1000`); the total wait is delay × n(n−1)/2 for n attempts
43
45
 
44
46
  ## How It Works
45
47
 
@@ -130,7 +132,7 @@ When a user's quota changes:
130
132
  PUT /api/v1/ldap/users/jdoe
131
133
  {
132
134
  "replace": {
133
- "mailQuota": "5000000000"
135
+ "mailQuotaSize": "5000000000"
134
136
  }
135
137
  }
136
138
  ```
@@ -646,7 +648,7 @@ Alternative authentication methods (Basic Auth, JWT) can be configured via rever
646
648
  --plugin twake/james \
647
649
  --ldap-flat-schema ./schemas/twake/users.json \
648
650
  --mail-attribute mail \
649
- --quota-attribute mailQuota \
651
+ --quota-attribute mailQuotaSize \
650
652
  --james-webadmin-url http://james:8000
651
653
  ```
652
654
 
@@ -663,7 +665,7 @@ Alternative authentication methods (Basic Auth, JWT) can be configured via rever
663
665
  --ldap-flat-schema ./schemas/twake/users.json \
664
666
  --ldap-flat-schema ./schemas/twake/positions.json \
665
667
  --mail-attribute mail \
666
- --quota-attribute mailQuota \
668
+ --quota-attribute mailQuotaSize \
667
669
  --james-webadmin-url http://james:8000 \
668
670
  --james-webadmin-token "james-admin-token" \
669
671
  --static-path ./static \
@@ -707,6 +709,34 @@ The plugin logs all operations:
707
709
 
708
710
  ## Error Handling
709
711
 
712
+ ### Replication lag
713
+
714
+ James reads its users from LDAP, and may read them from a replica that has
715
+ not received the latest write yet: for up to a few seconds after a user is
716
+ created or renamed, James does not know the new address. A call about that
717
+ address fails meanwhile, with 400 (rename) or 404 (quota, alias, identity).
718
+
719
+ The calls that need James to know the user are therefore retried on 400 and
720
+ 404: the rename, the quota, an alias or a forward added, a delegation added,
721
+ and the identity. Each failed attempt logs a warning
722
+ (`"result": "retry"`, with the attempt number); the error is logged when the
723
+ last attempt fails. With the defaults, 5 attempts, the waits are 1, 2, 3 and
724
+ 4 seconds: `--james-retry-delay` times the number of attempts made, so
725
+ `--james-retry-delay` × n(n−1)/2 in all for n attempts: 10 seconds with the
726
+ defaults, 190 seconds with 20 attempts. `--james-retry-attempts 1` disables
727
+ the retries. Other
728
+ failures, such as 401 or 500, are not retried, nor are deletions.
729
+
730
+ The group calls are not retried: James does not look the members up in its
731
+ users repository there. Address groups (`/address/groups`) accept any valid
732
+ address, and the member routes of a TMail team mailbox do not check the user;
733
+ their only 404 is a missing team mailbox, which is avoided by creating it
734
+ before adding the members.
735
+
736
+ The retries run in the background, after the LDAP write has answered; a
737
+ call waiting for its next attempt does not take a slot of
738
+ `--james-concurrency`.
739
+
710
740
  ### Non-Existent User
711
741
 
712
742
  If mail account doesn't exist in James:
@@ -770,7 +800,7 @@ curl http://james:8000/users/john.doe@company.com
770
800
  curl -X PUT http://localhost:8081/api/v1/ldap/users/jdoe \
771
801
  -H "Authorization: Bearer admin-token" \
772
802
  -H "Content-Type: application/json" \
773
- -d '{"replace": {"mailQuota": "5000000000"}}'
803
+ -d '{"replace": {"mailQuotaSize": "5000000000"}}'
774
804
 
775
805
  # Verify in James
776
806
  curl http://james:8000/quota/users/jdoe@company.com/size
@@ -780,12 +810,12 @@ curl http://james:8000/quota/users/jdoe@company.com/size
780
810
 
781
811
  Quota values are in **bytes**:
782
812
 
783
- | Size | Bytes | Example |
784
- | --------- | ----------- | ---------------------------- |
785
- | 1 GB | 1000000000 | `"mailQuota": "1000000000"` |
786
- | 5 GB | 5000000000 | `"mailQuota": "5000000000"` |
787
- | 10 GB | 10000000000 | `"mailQuota": "10000000000"` |
788
- | Unlimited | -1 | `"mailQuota": "-1"` |
813
+ | Size | Bytes | Example |
814
+ | --------- | ----------- | -------------------------------- |
815
+ | 1 GB | 1000000000 | `"mailQuotaSize": "1000000000"` |
816
+ | 5 GB | 5000000000 | `"mailQuotaSize": "5000000000"` |
817
+ | 10 GB | 10000000000 | `"mailQuotaSize": "10000000000"` |
818
+ | Unlimited | -1 | `"mailQuotaSize": "-1"` |
789
819
 
790
820
  ## Synchronization Scenarios
791
821
 
@@ -875,7 +905,7 @@ James deletes the entire address group.
875
905
 
876
906
  1. **One-way sync**: LDAP → James only. James changes don't sync back to LDAP.
877
907
  2. **No account creation**: Plugin doesn't create James accounts, only updates existing ones.
878
- 3. **No user deletion**: Plugin doesn't delete James accounts when LDAP users are deleted.
908
+ 3. **No user deletion**: deleting an LDAP user leaves its James data (mailboxes, quota, aliases) as it is; purging it is left to James.
879
909
  4. **Synchronous**: James API calls block LDAP response. Slow James = slow LDAP.
880
910
  5. **No send restrictions**: James doesn't enforce list type restrictions (open/member/owner-only). These are metadata in LDAP only.
881
911
 
@@ -941,7 +971,7 @@ hooks: {
941
971
 
942
972
  ```bash
943
973
  --mail-attribute mail
944
- --quota-attribute mailQuota
974
+ --quota-attribute mailQuotaSize
945
975
  ```
946
976
 
947
977
  3. Enable debug logging:
@@ -8,7 +8,7 @@ LDAP-Rest, and hooks for the attributes other plugins follow.
8
8
  ```bash
9
9
  --plugin core/ldap/onChange \
10
10
  --mail-attribute mail \
11
- --quota-attribute mailQuota
11
+ --quota-attribute mailQuotaSize
12
12
  ```
13
13
 
14
14
  ## Hooks
@@ -46,6 +46,12 @@ The other hooks are derived from it:
46
46
  | `onLdapDriveQuotaChange` | `--drive-quota-attribute` changed | `(dn, oldQuota, newQuota)` |
47
47
  | `onLdapDisplayNameChange` | the name from `--display-name-attribute`, else `cn`, else `givenName` and `sn`, changed | `(dn, oldName, newName)` |
48
48
 
49
+ A delete fires `onLdapChange` and `onLdapMailChange`, whose `newMail` is
50
+ `null`, and none of the others: the entry has nothing left to apply, and the
51
+ services fed by these hooks keep the data of a deleted user. The hooks given a
52
+ `mail` fire only while the entry has one: a write removing the mail attribute
53
+ does not fire them.
54
+
49
55
  `changes` maps each attribute that changed to `[oldValues, newValues]`, the
50
56
  full values on each side, `null` where the attribute is absent:
51
57
 
@@ -4,6 +4,20 @@ 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.17.0
8
+
9
+ ### The quota attribute defaults to `mailQuotaSize`
10
+
11
+ **Who is affected:** a deployment that loads `twake/james` or
12
+ `core/ldap/onChange`, or runs `bin/sync-james.mjs`, without setting
13
+ `--quota-attribute` (`DM_QUOTA_ATTRIBUTE`).
14
+
15
+ The default is now `mailQuotaSize`, the attribute the Twake schemas define,
16
+ instead of `mailQuota`. A directory that holds the quota in `mailQuota` must
17
+ set `DM_QUOTA_ATTRIBUTE=mailQuota` (or `--quota-attribute mailQuota`):
18
+ without it, quota changes are no longer sent to the mail server. A deployment
19
+ that already sets the option is unchanged.
20
+
7
21
  ## To 0.16.0
8
22
 
9
23
  ### Space events need the organization domain attribute
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ldap-rest",
3
- "version": "0.16.2",
3
+ "version": "0.17.1",
4
4
  "description": "Light directory manager",
5
5
  "keywords": [
6
6
  "Identity-Manager"
@@ -171,16 +171,6 @@
171
171
  "fr": "Autres boîtes"
172
172
  }
173
173
  },
174
- "mailQuota": {
175
- "type": "number",
176
- "required": false,
177
- "role": "emailQuota",
178
- "group": "Mailbox Settings",
179
- "label": {
180
- "en": "Mail quota",
181
- "fr": "Quota"
182
- }
183
- },
184
174
  "mailQuotaSize": {
185
175
  "type": "number",
186
176
  "required": false,