ldap-rest 0.7.0 → 0.8.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.
- package/dist/abstract/ldapFlat.js +1 -1
- package/dist/abstract/plugin.js +1 -1
- package/dist/bin/index.js +1 -1
- package/dist/config/args.js +1 -1
- package/dist/config/schema.js +1 -0
- package/dist/lib/expressFormatedResponses.js +1 -1
- package/dist/lib/ldapActions.js +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/plugins/auth/authzScope.js +1 -0
- package/dist/plugins/auth/llng.js +1 -1
- package/dist/plugins/ldap/accountLifecycle.js +1 -0
- package/dist/plugins/ldap/bulkImport.js +1 -1
- package/dist/plugins/ldap/departmentSync.js +1 -1
- package/dist/plugins/ldap/enterpriseRules.js +1 -0
- package/dist/plugins/ldap/externalUsersInGroups.js +1 -1
- package/dist/plugins/ldap/flatGeneric.js +1 -1
- package/dist/plugins/ldap/groups.js +1 -1
- package/dist/plugins/ldap/organizations.js +1 -1
- package/dist/plugins/scim/groups.js +1 -1
- package/dist/plugins/twake/calendar.js +1 -0
- package/dist/plugins/twake/calendarResources.js +1 -1
- package/dist/plugins/twake/james.js +1 -1
- package/dist/src/abstract/ldapFlat.d.ts +159 -1
- package/dist/src/abstract/plugin.d.ts +13 -0
- package/dist/src/config/args.d.ts +2 -0
- package/dist/src/config/schema.d.ts +382 -12
- package/dist/src/lib/expressFormatedResponses.d.ts +14 -0
- package/dist/src/lib/ldapActions.d.ts +33 -6
- package/dist/src/lib/utils.d.ts +65 -0
- package/dist/src/plugins/auth/authzScope.d.ts +53 -0
- package/dist/src/plugins/auth/llng.d.ts +30 -1
- package/dist/src/plugins/ldap/accountLifecycle.d.ts +56 -0
- package/dist/src/plugins/ldap/departmentSync.d.ts +43 -0
- package/dist/src/plugins/ldap/enterpriseRules.d.ts +436 -0
- package/dist/src/plugins/ldap/flatGeneric.d.ts +29 -0
- package/dist/src/plugins/ldap/groups.d.ts +22 -0
- package/dist/src/plugins/ldap/organizations.d.ts +25 -4
- package/dist/src/plugins/twake/calendar.d.ts +113 -0
- package/dist/src/plugins/twake/calendarResources.d.ts +9 -58
- package/docs/README.md +1 -0
- package/docs/client-development/api/openapi.md +1 -1
- package/docs/client-development/browser/libraries.md +1 -1
- package/docs/plugin-development/dependencies.md +10 -7
- package/docs/presentations/en.md +1 -1
- package/docs/presentations/fr.md +1 -1
- package/docs/usage/README.md +1 -0
- package/docs/usage/configuration.md +35 -15
- package/docs/usage/directory-audit.md +105 -0
- package/docs/usage/plugins/README.md +6 -6
- package/docs/usage/plugins/auth/README.md +6 -5
- package/docs/usage/plugins/auth/authz-scope.md +103 -0
- package/docs/usage/plugins/auth/llng.md +17 -0
- package/docs/usage/plugins/integrations/README.md +5 -5
- package/docs/usage/plugins/integrations/{calendar-resources.md → calendar.md} +70 -13
- package/docs/usage/plugins/ldap/README.md +13 -11
- package/docs/usage/plugins/ldap/account-lifecycle.md +129 -0
- package/docs/usage/plugins/ldap/bulk-import.md +8 -1
- package/docs/usage/plugins/ldap/enterprise-rules.md +167 -0
- package/docs/usage/plugins/ldap/flat-generic.md +172 -0
- package/docs/usage/plugins/ldap/groups.md +10 -4
- package/docs/usage/plugins/ldap/organizations.md +59 -33
- package/docs/usage/upgrading.md +322 -0
- package/package.json +50 -5
- package/static/schemas/example/README.md +36 -0
- package/static/schemas/example/groups.json +94 -0
- package/static/schemas/example/nomenclature/domains.json +49 -0
- package/static/schemas/example/organizations.json +106 -0
- package/static/schemas/example/positions.json +35 -0
- package/static/schemas/example/users.json +246 -0
- package/static/schemas/twake/groups.json +81 -15
- package/static/schemas/twake/nomenclature/twakeAccountStatus.json +26 -0
- package/static/schemas/twake/nomenclature/twakeDeliveryMode.json +22 -0
- package/static/schemas/twake/nomenclature/twakeDomain.json +60 -0
- package/static/schemas/twake/nomenclature/twakeListType.json +22 -0
- package/static/schemas/twake/nomenclature/twakeMailboxType.json +22 -0
- package/static/schemas/twake/nomenclature/twakeTitle.json +42 -0
- package/static/schemas/twake/organizations.json +92 -12
- package/static/schemas/twake/positions.json +22 -4
- package/static/schemas/twake/users.json +269 -32
package/docs/usage/upgrading.md
CHANGED
|
@@ -4,6 +4,328 @@ 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.8.0
|
|
8
|
+
|
|
9
|
+
### Node 20 is the floor
|
|
10
|
+
|
|
11
|
+
**Who is affected:** anyone installing on Node 18 or older.
|
|
12
|
+
|
|
13
|
+
`engines` declares `>=20` — the version Debian 13 ships — and the CI runs the
|
|
14
|
+
suite on 20, 22, 24 and 26. Older runtimes are neither tested nor supported.
|
|
15
|
+
|
|
16
|
+
**`core/auth/llng` is the one plugin that does not follow.** It needs
|
|
17
|
+
`lemonldap-ng-handler`, which depends on the native `re2`, and no single `re2`
|
|
18
|
+
release installs on every supported Node: up to `1.24.0` it builds on 20 but
|
|
19
|
+
not on 26, and from `1.24.1` it declares `engines: >=22`. npm answers an
|
|
20
|
+
unsatisfied dependency of this kind by leaving an _optional_ package out
|
|
21
|
+
without a word, so on some runtimes the handler is simply absent.
|
|
22
|
+
|
|
23
|
+
Everything else builds, tests and runs there regardless. A server that
|
|
24
|
+
configures `core/auth/llng` on a runtime where the handler could not be
|
|
25
|
+
installed now fails at startup, naming the plugin and the missing package,
|
|
26
|
+
rather than accepting requests an authentication plugin cannot check. Check
|
|
27
|
+
after upgrading that the runtime you deploy on carries it:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
node -e "require('lemonldap-ng-handler'); console.log('present')"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### The LLNG handler now needs a working configuration at startup
|
|
34
|
+
|
|
35
|
+
**Who is affected:** every deployment configuring `core/auth/llng`.
|
|
36
|
+
|
|
37
|
+
The handler used to do nothing at startup: `--llng-ini` was read into the
|
|
38
|
+
configuration but never handed to it, so every request failed with a `500`
|
|
39
|
+
regardless of what the file said. It is now initialized once, before the
|
|
40
|
+
server starts serving — which means the file finally matters, and two ways
|
|
41
|
+
it can be wrong now stop the server instead of answering `500`:
|
|
42
|
+
|
|
43
|
+
- `--llng-ini` defaults to `/etc/lemonldap-ng/lemonldap-ng.ini`, and a file at
|
|
44
|
+
that path with no `[node-handler] nodeVhosts` listing this server — the
|
|
45
|
+
normal state until now, since nothing read it — refuses to start where it
|
|
46
|
+
used to start and answer `500` to every request instead;
|
|
47
|
+
- an LLNG configuration store (`[configuration] baseConfigUrl` and the rest)
|
|
48
|
+
unreachable when the server boots crash-loops it under an orchestrator,
|
|
49
|
+
until the portal or config store it depends on comes up.
|
|
50
|
+
|
|
51
|
+
List this server before upgrading:
|
|
52
|
+
|
|
53
|
+
```ini
|
|
54
|
+
[node-handler]
|
|
55
|
+
nodeVhosts = api.example.com
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
and, if the LLNG configuration store starts after this server does, sequence
|
|
59
|
+
the two or give the container a restart policy that tolerates a few
|
|
60
|
+
failures at boot.
|
|
61
|
+
|
|
62
|
+
### The Twake schemas now need `core/ldap/enterpriseRules`
|
|
63
|
+
|
|
64
|
+
**Who is affected:** every deployment loading `static/schemas/twake/*`.
|
|
65
|
+
|
|
66
|
+
Those schemas mark `twakeDepartmentPath`, `twakeAccountStatus` and
|
|
67
|
+
`twakeDeliveryMode` both `required` and `generated` — a client may not send
|
|
68
|
+
them, and a plugin has to fill them. Load the one that does:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
--plugin core/ldap/enterpriseRules
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Without it, a creation answers `400` naming the attribute, on the flat routes
|
|
75
|
+
as on `POST /ldap/organizations` and `POST /ldap/groups`. That refusal is
|
|
76
|
+
deliberate: the previous release wrote the entry anyway, missing an attribute
|
|
77
|
+
its own schema called required and that no client could ever add.
|
|
78
|
+
|
|
79
|
+
**The nomenclature has to hold what the schema defaults name.** The user
|
|
80
|
+
schema points at `cn=normal,ou=twakeDeliveryMode,ou=nomenclature,<base>` and
|
|
81
|
+
`cn=active,ou=twakeAccountStatus,ou=nomenclature,<base>`. A directory never
|
|
82
|
+
seeded with those entries answers `400` on every creation, naming the DN that
|
|
83
|
+
does not resolve — rather than storing a dangling one on each account.
|
|
84
|
+
|
|
85
|
+
### Clients must stop sending the computed attributes
|
|
86
|
+
|
|
87
|
+
**Who is affected:** anything that creates or updates users, groups or
|
|
88
|
+
organizations against the Twake schemas.
|
|
89
|
+
|
|
90
|
+
`uid` (generated from the local part of `mail`), `twakeDepartmentPath`,
|
|
91
|
+
`twakeAccountStatus` and `twakeDeliveryMode` are refused in a request body
|
|
92
|
+
with a `400` naming the attribute. Drop them from the payload — the server
|
|
93
|
+
fills them.
|
|
94
|
+
|
|
95
|
+
To keep the old behaviour, copy the schema and remove the markers: each of
|
|
96
|
+
these is a `generated` or `readOnly` flag in the JSON, not code.
|
|
97
|
+
|
|
98
|
+
### Run the audit before switching
|
|
99
|
+
|
|
100
|
+
**Who is affected:** every directory holding entries written before this
|
|
101
|
+
release.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm run audit:directory -- --schema static/schemas/twake/users.json \
|
|
105
|
+
--plugin core/ldap/enterpriseRules
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
It reads the branch as it stands and reports what the schema would now refuse,
|
|
109
|
+
quoting each rule's own `hint`. Two rules tightened in ways that only show on
|
|
110
|
+
stored data:
|
|
111
|
+
|
|
112
|
+
- an array's `items.test` and `items.branch` are enforced on the flat,
|
|
113
|
+
organization and group routes, where they never were —
|
|
114
|
+
`mailAlternateAddress` has carried a pattern since 0.7.0 and accepted
|
|
115
|
+
anything — and an array of pointers must name existing entries;
|
|
116
|
+
- a pointer's `branch` is compared RDN by RDN. A DN that merely ended with the
|
|
117
|
+
branch as text, `uid=x,xou=users,dc=example,dc=com` against
|
|
118
|
+
`ou=users,dc=example,dc=com`, used to pass.
|
|
119
|
+
|
|
120
|
+
Stored values are left alone. The refusal comes at the next update of an
|
|
121
|
+
offending entry, which is why it is worth knowing beforehand.
|
|
122
|
+
|
|
123
|
+
### `employeeNumber` is unique, apart from the `UNIT` placeholder
|
|
124
|
+
|
|
125
|
+
**Who is affected:** directories where accounts share a placeholder employee
|
|
126
|
+
number other than `UNIT`.
|
|
127
|
+
|
|
128
|
+
`static/schemas/twake/users.json` declares `employeeNumber` unique, exempting
|
|
129
|
+
the value `UNIT` — the placeholder the interface these schemas replace uses
|
|
130
|
+
for an account standing for a unit rather than a person. Once
|
|
131
|
+
`core/ldap/enterpriseRules` is loaded, every creation or update carrying a
|
|
132
|
+
value another account already holds answers `409`; `UNIT` is let through
|
|
133
|
+
however many accounts carry it.
|
|
134
|
+
|
|
135
|
+
A deployment whose placeholder is spelled otherwise has to say so, or its
|
|
136
|
+
second holder of that value is refused, and every existing holder at its next
|
|
137
|
+
update of the attribute. The audit cannot warn: uniqueness spans the whole
|
|
138
|
+
directory, and it only checks what each entry says on its own.
|
|
139
|
+
|
|
140
|
+
Find the shared values before switching:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
ldapsearch -LLL -b "ou=users,<base>" "(employeeNumber=*)" employeeNumber \
|
|
144
|
+
| awk '/^employeeNumber:/ {print $2}' | sort | uniq -cd
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Then name yours in a copy of the schema, as
|
|
148
|
+
`static/schemas/example/users.json` does:
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
"unique": { "sentinel": "YOUR-PLACEHOLDER" }
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Calendar resource ids, and the branch they are looked for in
|
|
155
|
+
|
|
156
|
+
**Who is affected:** deployments loading `core/twake/calendar` (the plugin
|
|
157
|
+
`calendarResources` was renamed to).
|
|
158
|
+
|
|
159
|
+
Three things changed in how an LDAP entry is matched to a Calendar resource.
|
|
160
|
+
A resource whose DN is `cn=…` or `uid=…` directly under the configured base,
|
|
161
|
+
with no escape in its value, keeps the identifier it had and needs nothing.
|
|
162
|
+
|
|
163
|
+
**`--calendar-resource-base` must be a full DN.** The branch was tested by
|
|
164
|
+
asking whether the DN _contained_ the configured value as text, so a partial
|
|
165
|
+
value like `ou=resources` matched — and matched a sibling branch such as
|
|
166
|
+
`ou=resourcesArchive` with it. It is compared by DN components now, which
|
|
167
|
+
also fixes the other direction: a DN written with spaces after its commas was
|
|
168
|
+
not recognised as a resource at all. Give the option the whole DN; the plugin
|
|
169
|
+
warns at startup when the value is not one under `--ldap-base`.
|
|
170
|
+
|
|
171
|
+
**The identifier is read from the entry's own RDN.** It used to come from an
|
|
172
|
+
unanchored search for `cn=` or `uid=` anywhere in the DN, so an entry whose
|
|
173
|
+
own RDN was neither borrowed the one of an ancestor; failing that, creation
|
|
174
|
+
fell back to a slug of the entry's name, under which modification and
|
|
175
|
+
deletion then found nothing. An RDN value carrying an escape — `cn=Salle\, 2`
|
|
176
|
+
— was cut at the escape.
|
|
177
|
+
|
|
178
|
+
So an entry of either of those shapes is now created, patched and deleted in
|
|
179
|
+
Calendar under a different identifier than before. **The resource Calendar
|
|
180
|
+
already holds under the old one is not migrated**: rename it there, or delete
|
|
181
|
+
it and let the next write recreate it. List what is affected before
|
|
182
|
+
upgrading — every resource whose RDN attribute is neither `cn` nor `uid`, and
|
|
183
|
+
every one whose RDN value contains a `\`.
|
|
184
|
+
|
|
185
|
+
**A modification or a deletion outside the base no longer reaches Calendar.**
|
|
186
|
+
Only creation checked the branch; the other two acted on any entry of the
|
|
187
|
+
entity whose DN yielded an identifier.
|
|
188
|
+
|
|
189
|
+
### Renaming an entry, and what the cascade reaches
|
|
190
|
+
|
|
191
|
+
**Who is affected:** anyone calling the new
|
|
192
|
+
`POST /v1/ldap/{resource}/{id}/rename`. Nothing changes for a deployment
|
|
193
|
+
that does not.
|
|
194
|
+
|
|
195
|
+
The identifier of a flat entry can be changed. The entry's DN changes with
|
|
196
|
+
it, so everything naming that DN has to be rewritten, and the endpoint waits
|
|
197
|
+
for that before it answers.
|
|
198
|
+
|
|
199
|
+
What it rewrites is read from the schemas, never from a list of attribute
|
|
200
|
+
names: every `pointer` — single or in an array — whose `branch` admits the
|
|
201
|
+
renamed entry, and every attribute carrying the `members` or `owners` role.
|
|
202
|
+
**Load `core/ldap/enterpriseRules`**: without it the entry is renamed and
|
|
203
|
+
nothing else is touched.
|
|
204
|
+
|
|
205
|
+
What it cannot reach is a DN held in a plain `string` attribute that carries
|
|
206
|
+
no role. Declare it a `pointer`, or give it its role, and it is covered.
|
|
207
|
+
|
|
208
|
+
A directory running OpenLDAP's `refint` overlay already fixes the attributes
|
|
209
|
+
it is configured for — usually `member`, `owner`, `uniqueMember` and
|
|
210
|
+
`memberOf` — in a task of its own, after the rename has answered. The server
|
|
211
|
+
converges with it rather than fighting it: what the overlay has already
|
|
212
|
+
fixed counts as done. `refint` is not a substitute, though. It never sees a
|
|
213
|
+
deployment's own pointers, `twakeManagerLink` and `twakeLocalAdminLink`
|
|
214
|
+
among them — and losing the second one silently costs an administrator every
|
|
215
|
+
branch they administer.
|
|
216
|
+
|
|
217
|
+
### A rename answers `207` when it could not finish
|
|
218
|
+
|
|
219
|
+
The rename of the entry and the rewrite of what points at it are separate
|
|
220
|
+
writes, and a directory has no transaction to hold them together.
|
|
221
|
+
|
|
222
|
+
If the rename itself fails, nothing else has happened and the directory's own
|
|
223
|
+
refusal is what you get. Once it succeeds the rename is a fact, and an error
|
|
224
|
+
would tell you the opposite — so a rewrite that fails answers `207` with the
|
|
225
|
+
attributes and the counts that could not be written. The referring DNs are
|
|
226
|
+
not in the body, where the caller has no business reading them; they are in
|
|
227
|
+
the log, at `error`, with the attribute and both DNs.
|
|
228
|
+
|
|
229
|
+
**Re-issue the identical request to finish it.** A rename whose source is
|
|
230
|
+
already gone and whose target is already there runs the rewrite alone. That
|
|
231
|
+
is also what to do if the server dies between the two writes.
|
|
232
|
+
|
|
233
|
+
### `unique` is not widened on a rename
|
|
234
|
+
|
|
235
|
+
A schema marking its identifier `unique: { "branches": [...] }` gets the
|
|
236
|
+
RDN's own branch checked on a rename, and nothing more: the wider check runs
|
|
237
|
+
from the add and modify hooks, which a rename does not go through. The
|
|
238
|
+
endpoint's own description says so too.
|
|
239
|
+
|
|
240
|
+
### Creating an entry that already exists answers `409`
|
|
241
|
+
|
|
242
|
+
**Who is affected:** any client that reads a failed creation by its status
|
|
243
|
+
code or by the text of its message — a bulk import recording per-line errors
|
|
244
|
+
most of all.
|
|
245
|
+
|
|
246
|
+
Two creations of the same entry racing past the existence checks answered
|
|
247
|
+
`500`, the add path having wrapped every LDAP error in a plain `Error`. The
|
|
248
|
+
directory's own `entryAlreadyExists` is now recognised once, in
|
|
249
|
+
`lib/ldapActions`, so every creation route answers `409` alike: the flat
|
|
250
|
+
routes, `POST /ldap/groups`, the organization routes, and the external
|
|
251
|
+
members `core/ldap/externalUsersInGroups` inserts.
|
|
252
|
+
|
|
253
|
+
The message is `Entry <dn> already exists`. The flat routes used to say
|
|
254
|
+
`<entity> <dn> already exists`, and `core/ldap/bulkImport` used to record
|
|
255
|
+
`LDAP add error: …` in its per-line `errors[]`; both read the same now.
|
|
256
|
+
SCIM still answers `uniqueness`, the numeric code being carried on the
|
|
257
|
+
error it is given.
|
|
258
|
+
|
|
259
|
+
### A flat schema may not claim a URL an LDAP plugin serves
|
|
260
|
+
|
|
261
|
+
**Who is affected:** anyone passing `--ldap-flat-schema` a schema whose
|
|
262
|
+
`entity.pluralName` is `groups`, `organizations`, `raw` or `bulk-import`
|
|
263
|
+
beside the plugin of the same name — `static/schemas/twake/groups.json` is
|
|
264
|
+
exactly that.
|
|
265
|
+
|
|
266
|
+
Both used to load, sharing a hook prefix and a URL; Express answered with
|
|
267
|
+
whichever registered first, and the loser stayed advertised by the
|
|
268
|
+
configuration API while being unreachable in fact. The schema is now dropped
|
|
269
|
+
with an error naming the holder. **Load one or the other, not both.**
|
|
270
|
+
|
|
271
|
+
### Organization paths: nothing to convert
|
|
272
|
+
|
|
273
|
+
**Who is affected:** directories written before the path order was settled.
|
|
274
|
+
|
|
275
|
+
`twakeDepartmentPath` reads from the root down, the entry's own name last.
|
|
276
|
+
What is already stored in the old order — the entry's own name first, the top
|
|
277
|
+
organization's last — is still accepted as it stands, so no organization
|
|
278
|
+
becomes unwritable on upgrade. Only paths the server computes from now on
|
|
279
|
+
follow the new order, and the two forms coexist until an entry is rewritten.
|
|
280
|
+
|
|
281
|
+
### The search cache works now, and is off by default
|
|
282
|
+
|
|
283
|
+
**Who is affected:** deployments that set `--ldap-cache-ttl` (or
|
|
284
|
+
`DM_LDAP_CACHE_TTL`) explicitly. Everyone else keeps exactly the behaviour
|
|
285
|
+
they had.
|
|
286
|
+
|
|
287
|
+
The base-scope search cache stored nothing since it was written: the branch
|
|
288
|
+
that would have kept a result could not be reached, so every read went to the
|
|
289
|
+
directory whatever the option said. It caches now — which means a deployment
|
|
290
|
+
that had set the option gets a working cache for the first time, where it used
|
|
291
|
+
to get none.
|
|
292
|
+
|
|
293
|
+
What a write through this service drops is what it changed, its subtree
|
|
294
|
+
included, whatever case the DN is written in. What this service cannot know
|
|
295
|
+
about, it cannot drop: another replica of it, an LSC synchronisation, a
|
|
296
|
+
hand-run `ldapmodify`. A cached read stays as it was until its TTL runs out.
|
|
297
|
+
|
|
298
|
+
So `--ldap-cache-ttl` now defaults to `0`, which means no caching at all. Set
|
|
299
|
+
it only where this service is the sole writer of the entries it reads, and
|
|
300
|
+
keep the value under the staleness you are willing to serve.
|
|
301
|
+
|
|
302
|
+
### `calendarResources` is renamed `calendar`
|
|
303
|
+
|
|
304
|
+
**Who is affected:** deployments loading `core/twake/calendarResources`, and
|
|
305
|
+
plugins importing `ldap-rest/plugin-twake-calendarresources` or looking the
|
|
306
|
+
plugin up by name.
|
|
307
|
+
|
|
308
|
+
Besides calendar resources, the plugin propagates user email and name changes
|
|
309
|
+
to the Twake Calendar registered users; the old name hid that. Nothing breaks
|
|
310
|
+
yet: `core/twake/calendarResources` still loads the same plugin, registered
|
|
311
|
+
under its old name, and logs a deprecation warning. It will be removed in a
|
|
312
|
+
future major release, so switch now by **replacing** the old name with the new
|
|
313
|
+
one:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# before
|
|
317
|
+
DM_PLUGINS="…,core/twake/calendarResources"
|
|
318
|
+
# after
|
|
319
|
+
DM_PLUGINS="…,core/twake/calendar"
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Do not list both: they register under different names, so the loader does not
|
|
323
|
+
see a duplicate and runs two instances — every resource change and every user
|
|
324
|
+
email or name change is then sent to Calendar twice.
|
|
325
|
+
|
|
326
|
+
Plugin code should import `ldap-rest/plugin-twake-calendar` and look the
|
|
327
|
+
plugin up as `calendar`. Configuration flags (`--calendar-*`) are unchanged.
|
|
328
|
+
|
|
7
329
|
## To 0.7.0
|
|
8
330
|
|
|
9
331
|
### `externalId` on Groups is no longer served
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ldap-rest",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Light directory manager",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Identity-Manager"
|
|
@@ -56,6 +56,10 @@
|
|
|
56
56
|
"types": "./dist/src/plugins/auth/authzPerRoute.d.ts",
|
|
57
57
|
"import": "./dist/plugins/auth/authzPerRoute.js"
|
|
58
58
|
},
|
|
59
|
+
"./plugin-auth-authzscope": {
|
|
60
|
+
"types": "./dist/src/plugins/auth/authzScope.d.ts",
|
|
61
|
+
"import": "./dist/plugins/auth/authzScope.js"
|
|
62
|
+
},
|
|
59
63
|
"./plugin-auth-crowdsec": {
|
|
60
64
|
"types": "./dist/src/plugins/auth/crowdsec.d.ts",
|
|
61
65
|
"import": "./dist/plugins/auth/crowdsec.js"
|
|
@@ -96,6 +100,10 @@
|
|
|
96
100
|
"types": "./dist/src/plugins/demo/helloworld.d.ts",
|
|
97
101
|
"import": "./dist/plugins/demo/helloworld.js"
|
|
98
102
|
},
|
|
103
|
+
"./plugin-ldap-accountlifecycle": {
|
|
104
|
+
"types": "./dist/src/plugins/ldap/accountLifecycle.d.ts",
|
|
105
|
+
"import": "./dist/plugins/ldap/accountLifecycle.js"
|
|
106
|
+
},
|
|
99
107
|
"./plugin-ldap-bulkimport": {
|
|
100
108
|
"types": "./dist/src/plugins/ldap/bulkImport.d.ts",
|
|
101
109
|
"import": "./dist/plugins/ldap/bulkImport.js"
|
|
@@ -104,6 +112,10 @@
|
|
|
104
112
|
"types": "./dist/src/plugins/ldap/departmentSync.d.ts",
|
|
105
113
|
"import": "./dist/plugins/ldap/departmentSync.js"
|
|
106
114
|
},
|
|
115
|
+
"./plugin-ldap-enterpriserules": {
|
|
116
|
+
"types": "./dist/src/plugins/ldap/enterpriseRules.d.ts",
|
|
117
|
+
"import": "./dist/plugins/ldap/enterpriseRules.js"
|
|
118
|
+
},
|
|
107
119
|
"./plugin-ldap-externalusersingroups": {
|
|
108
120
|
"types": "./dist/src/plugins/ldap/externalUsersInGroups.d.ts",
|
|
109
121
|
"import": "./dist/plugins/ldap/externalUsersInGroups.js"
|
|
@@ -208,6 +220,10 @@
|
|
|
208
220
|
"types": "./dist/src/plugins/twake/appAccountsConsistency.d.ts",
|
|
209
221
|
"import": "./dist/plugins/twake/appAccountsConsistency.js"
|
|
210
222
|
},
|
|
223
|
+
"./plugin-twake-calendar": {
|
|
224
|
+
"types": "./dist/src/plugins/twake/calendar.d.ts",
|
|
225
|
+
"import": "./dist/plugins/twake/calendar.js"
|
|
226
|
+
},
|
|
211
227
|
"./plugin-twake-calendarresources": {
|
|
212
228
|
"types": "./dist/src/plugins/twake/calendarResources.d.ts",
|
|
213
229
|
"import": "./dist/plugins/twake/calendarResources.js"
|
|
@@ -480,6 +496,26 @@
|
|
|
480
496
|
"import": "./static/schemas/ad/users.json",
|
|
481
497
|
"require": "./static/schemas/ad/users.json"
|
|
482
498
|
},
|
|
499
|
+
"./schema-example-groups": {
|
|
500
|
+
"import": "./static/schemas/example/groups.json",
|
|
501
|
+
"require": "./static/schemas/example/groups.json"
|
|
502
|
+
},
|
|
503
|
+
"./schema-example-nomenclature-domains": {
|
|
504
|
+
"import": "./static/schemas/example/nomenclature/domains.json",
|
|
505
|
+
"require": "./static/schemas/example/nomenclature/domains.json"
|
|
506
|
+
},
|
|
507
|
+
"./schema-example-organizations": {
|
|
508
|
+
"import": "./static/schemas/example/organizations.json",
|
|
509
|
+
"require": "./static/schemas/example/organizations.json"
|
|
510
|
+
},
|
|
511
|
+
"./schema-example-positions": {
|
|
512
|
+
"import": "./static/schemas/example/positions.json",
|
|
513
|
+
"require": "./static/schemas/example/positions.json"
|
|
514
|
+
},
|
|
515
|
+
"./schema-example-users": {
|
|
516
|
+
"import": "./static/schemas/example/users.json",
|
|
517
|
+
"require": "./static/schemas/example/users.json"
|
|
518
|
+
},
|
|
483
519
|
"./schema-obm-organizations": {
|
|
484
520
|
"import": "./static/schemas/obm/organizations.json",
|
|
485
521
|
"require": "./static/schemas/obm/organizations.json"
|
|
@@ -564,6 +600,10 @@
|
|
|
564
600
|
"import": "./static/schemas/twake/nomenclature/twakeDeliveryMode.json",
|
|
565
601
|
"require": "./static/schemas/twake/nomenclature/twakeDeliveryMode.json"
|
|
566
602
|
},
|
|
603
|
+
"./schema-twake-nomenclature-twakedomain": {
|
|
604
|
+
"import": "./static/schemas/twake/nomenclature/twakeDomain.json",
|
|
605
|
+
"require": "./static/schemas/twake/nomenclature/twakeDomain.json"
|
|
606
|
+
},
|
|
567
607
|
"./schema-twake-nomenclature-twakelisttype": {
|
|
568
608
|
"import": "./static/schemas/twake/nomenclature/twakeListType.json",
|
|
569
609
|
"require": "./static/schemas/twake/nomenclature/twakeListType.json"
|
|
@@ -607,6 +647,7 @@
|
|
|
607
647
|
"package.json"
|
|
608
648
|
],
|
|
609
649
|
"scripts": {
|
|
650
|
+
"audit:directory": "tsx scripts/audit-directory.ts",
|
|
610
651
|
"build": "npm run build:prod",
|
|
611
652
|
"build:browser": "rollup -c rollup.browser.config.mjs",
|
|
612
653
|
"build:dev": "./.dev.mk builddev",
|
|
@@ -615,7 +656,8 @@
|
|
|
615
656
|
"build:pages": "tsx scripts/build-pages.ts",
|
|
616
657
|
"build:prod": "npm run clean && NODE_ENV=production rollup -c && npm run build:browser && node scripts/moveBrowserLibs.mjs && npm run build:dockerfile",
|
|
617
658
|
"build:watch": "rollup -c --watch",
|
|
618
|
-
"check": "npm run check:ts && npm run lint && npm run format:check",
|
|
659
|
+
"check": "npm run check:ts && npm run lint && npm run format:check && npm run check:no-client-values",
|
|
660
|
+
"check:no-client-values": "sh scripts/check-no-client-values.sh",
|
|
619
661
|
"check:ts": "tsc --noEmit",
|
|
620
662
|
"clean": "rimraf dist/* static/browser/* coverage",
|
|
621
663
|
"coverage": "c8 --config .c8rc.json mocha",
|
|
@@ -643,7 +685,7 @@
|
|
|
643
685
|
},
|
|
644
686
|
"dependencies": {
|
|
645
687
|
"body-parser": "^2.2.0",
|
|
646
|
-
"csv-parse": "^
|
|
688
|
+
"csv-parse": "^7.0.2",
|
|
647
689
|
"express": "^5.1.0",
|
|
648
690
|
"express-rate-limit": "^8.1.0",
|
|
649
691
|
"ldapts": "^8.0.9",
|
|
@@ -668,7 +710,7 @@
|
|
|
668
710
|
"@types/supertest": "^6.0.3",
|
|
669
711
|
"@typescript-eslint/eslint-plugin": "^8.44.0",
|
|
670
712
|
"@typescript-eslint/parser": "^8.44.0",
|
|
671
|
-
"c8": "^
|
|
713
|
+
"c8": "^12.0.0",
|
|
672
714
|
"chai": "^6.0.1",
|
|
673
715
|
"eslint": "^9.35.0",
|
|
674
716
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -678,7 +720,7 @@
|
|
|
678
720
|
"fast-glob": "^3.3.3",
|
|
679
721
|
"globals": "^14.0.0",
|
|
680
722
|
"js-yaml": "^4.1.1",
|
|
681
|
-
"mocha": "^
|
|
723
|
+
"mocha": "^12.0.0",
|
|
682
724
|
"nock": "^14.0.10",
|
|
683
725
|
"prettier": "^3.6.2",
|
|
684
726
|
"rimraf": "^6.0.1",
|
|
@@ -695,5 +737,8 @@
|
|
|
695
737
|
"express-openid-connect": "^2.19.2",
|
|
696
738
|
"lemonldap-ng-handler": "^0.7.3",
|
|
697
739
|
"re2": "^1.22.1"
|
|
740
|
+
},
|
|
741
|
+
"engines": {
|
|
742
|
+
"node": ">=20"
|
|
698
743
|
}
|
|
699
744
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Example deployment configuration
|
|
2
|
+
|
|
3
|
+
These schemas show what a concrete deployment looks like once every
|
|
4
|
+
client-specific value has been moved out of the code: national phone and
|
|
5
|
+
postal-code formats, the mail domain accounts must belong to, the payroll
|
|
6
|
+
number format and the placeholder that exempts a value from its uniqueness
|
|
7
|
+
check, the mailbox quota default, and the naming rules the directory already
|
|
8
|
+
enforces on its existing data.
|
|
9
|
+
|
|
10
|
+
They are **not** part of the product. Nothing in `src/` refers to them, and the
|
|
11
|
+
`npm run check:no-client-values` guard makes sure nothing ever does. Copy the
|
|
12
|
+
directory, replace the patterns and the domains, and the core serves another
|
|
13
|
+
directory unchanged.
|
|
14
|
+
|
|
15
|
+
Each `test` carries the `hint` that explains it, so a client can show the
|
|
16
|
+
expected format under the field instead of only rejecting what was typed.
|
|
17
|
+
|
|
18
|
+
Point a server at them with:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
node bin/index.mjs \
|
|
22
|
+
--ldap-base dc=example,dc=org \
|
|
23
|
+
--ldap-top-organization ou=organization,dc=example,dc=org \
|
|
24
|
+
--ldap-group-base ou=lists,ou=groups,dc=example,dc=org \
|
|
25
|
+
--group-schema static/schemas/example/groups.json \
|
|
26
|
+
--organization-schema static/schemas/example/organizations.json \
|
|
27
|
+
--ldap-flat-schema static/schemas/example/users.json \
|
|
28
|
+
--ldap-flat-schema static/schemas/example/positions.json \
|
|
29
|
+
--ldap-flat-schema static/schemas/example/nomenclature/domains.json \
|
|
30
|
+
--plugin core/ldap/flatGeneric \
|
|
31
|
+
--plugin core/ldap/groups \
|
|
32
|
+
--plugin core/ldap/organizations \
|
|
33
|
+
--plugin core/ldap/enterpriseRules \
|
|
34
|
+
--plugin core/ldap/accountLifecycle \
|
|
35
|
+
--plugin core/auth/authzScope
|
|
36
|
+
```
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entity": {
|
|
3
|
+
"name": "mailingList",
|
|
4
|
+
"mainAttribute": "cn",
|
|
5
|
+
"objectClass": ["top", "groupOfNames", "twakeStaticGroup"],
|
|
6
|
+
"singularName": "mailingList",
|
|
7
|
+
"pluralName": "mailingLists",
|
|
8
|
+
"base": "ou=lists,ou=groups,__ldap_base__"
|
|
9
|
+
},
|
|
10
|
+
"strict": true,
|
|
11
|
+
"attributes": {
|
|
12
|
+
"objectClass": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"items": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"test": "^[a-zA-Z][a-zA-Z0-9-]*$"
|
|
17
|
+
},
|
|
18
|
+
"default": ["top", "groupOfNames", "twakeStaticGroup"],
|
|
19
|
+
"required": true,
|
|
20
|
+
"fixed": true
|
|
21
|
+
},
|
|
22
|
+
"cn": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"searchable": true,
|
|
25
|
+
"test": "^(?!.*\\.\\.)[a-zA-Z0-9!#$%&'*+/=?^_`{|}.-]{1,64}$",
|
|
26
|
+
"hint": "Must match an email local part, at most 64 characters, no consecutive dots",
|
|
27
|
+
"required": true,
|
|
28
|
+
"role": "identifier",
|
|
29
|
+
"label": "Mailing list name"
|
|
30
|
+
},
|
|
31
|
+
"description": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"searchable": true,
|
|
34
|
+
"required": false
|
|
35
|
+
},
|
|
36
|
+
"mail": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"searchable": true,
|
|
39
|
+
"test": "^[^@\\s]{1,64}@([^@\\s]{1,245}\\.)?example\\.org$",
|
|
40
|
+
"hint": "Expected an address in example.org or one of its subdomains",
|
|
41
|
+
"required": true,
|
|
42
|
+
"unique": {
|
|
43
|
+
"attributes": ["mailAlternateAddress"],
|
|
44
|
+
"branches": ["__LDAP_BASE__"]
|
|
45
|
+
},
|
|
46
|
+
"mailDomainScope": "organization",
|
|
47
|
+
"allowSubdomains": true,
|
|
48
|
+
"role": "primaryEmail"
|
|
49
|
+
},
|
|
50
|
+
"businessCategory": {
|
|
51
|
+
"type": "pointer",
|
|
52
|
+
"branch": ["ou=twakeListType,ou=nomenclature,__LDAP_BASE__"],
|
|
53
|
+
"required": false,
|
|
54
|
+
"role": "writePolicy",
|
|
55
|
+
"label": "List type"
|
|
56
|
+
},
|
|
57
|
+
"member": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"items": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"test": "^[a-zA-Z]+=.*,__LDAP_BASE__$",
|
|
62
|
+
"hint": "Expected the DN of an entry of this directory"
|
|
63
|
+
},
|
|
64
|
+
"required": false,
|
|
65
|
+
"deleteGuard": "nonEmpty",
|
|
66
|
+
"role": "members",
|
|
67
|
+
"label": "Members"
|
|
68
|
+
},
|
|
69
|
+
"owner": {
|
|
70
|
+
"type": "array",
|
|
71
|
+
"items": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"test": "^[a-zA-Z]+=.*,__LDAP_BASE__$",
|
|
74
|
+
"hint": "Expected the DN of an entry of this directory"
|
|
75
|
+
},
|
|
76
|
+
"required": false,
|
|
77
|
+
"role": "owners"
|
|
78
|
+
},
|
|
79
|
+
"twakeDepartmentLink": {
|
|
80
|
+
"type": "pointer",
|
|
81
|
+
"branch": ["ou=organization,__LDAP_BASE__"],
|
|
82
|
+
"required": true,
|
|
83
|
+
"role": "organizationLink",
|
|
84
|
+
"label": "Department"
|
|
85
|
+
},
|
|
86
|
+
"twakeDepartmentPath": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"test": "^[^\\r\\n]{1,4096}$",
|
|
89
|
+
"required": true,
|
|
90
|
+
"generated": true,
|
|
91
|
+
"role": "organizationPath"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entity": {
|
|
3
|
+
"name": "domain",
|
|
4
|
+
"mainAttribute": "dc",
|
|
5
|
+
"objectClass": ["top", "domain", "domainRelatedObject"],
|
|
6
|
+
"singularName": "domain",
|
|
7
|
+
"pluralName": "domains",
|
|
8
|
+
"base": "ou=domains,ou=nomenclature,{ldap_base}",
|
|
9
|
+
"label": {
|
|
10
|
+
"en": "Mail domains",
|
|
11
|
+
"fr": "Domaines de messagerie"
|
|
12
|
+
},
|
|
13
|
+
"singularLabel": {
|
|
14
|
+
"en": "domain",
|
|
15
|
+
"fr": "domaine"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"strict": true,
|
|
19
|
+
"attributes": {
|
|
20
|
+
"objectClass": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": {
|
|
23
|
+
"type": "string"
|
|
24
|
+
},
|
|
25
|
+
"default": ["top", "domain", "domainRelatedObject"],
|
|
26
|
+
"required": true,
|
|
27
|
+
"fixed": true
|
|
28
|
+
},
|
|
29
|
+
"dc": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"test": "^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$",
|
|
32
|
+
"hint": "A single domain label: letters, digits and hyphens, e.g. example",
|
|
33
|
+
"required": true,
|
|
34
|
+
"role": "identifier"
|
|
35
|
+
},
|
|
36
|
+
"associatedDomain": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"test": "^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$|^\\*$",
|
|
39
|
+
"hint": "A fully qualified domain name (example.org), or * to allow every domain",
|
|
40
|
+
"required": true,
|
|
41
|
+
"role": "domainName",
|
|
42
|
+
"label": "Mail domain"
|
|
43
|
+
},
|
|
44
|
+
"description": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"required": false
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|