ldap-rest 0.26.0 → 1.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.
- package/dist/abstract/ldapFlat.js +1 -1
- package/dist/abstract/twakePlugin.js +1 -1
- package/dist/bin/index.js +1 -1
- package/dist/config/args.js +1 -1
- package/dist/lib/authz/base.js +1 -1
- package/dist/lib/authz/composition.js +1 -1
- package/dist/lib/ldapActions.js +1 -1
- package/dist/lib/mailDomains.js +1 -0
- package/dist/lib/requestContext.js +1 -0
- package/dist/lib/utils.js +1 -1
- package/dist/plugins/auth/authzDynamic.js +1 -1
- package/dist/plugins/auth/authzLinid1.js +1 -1
- package/dist/plugins/auth/authzPerBranch.js +1 -1
- package/dist/plugins/auth/authzScope.js +1 -1
- package/dist/plugins/configApi.js +1 -1
- package/dist/plugins/ldap/bulkImport.js +1 -1
- package/dist/plugins/ldap/departmentSync.js +1 -1
- package/dist/plugins/ldap/enterpriseRules.js +1 -1
- package/dist/plugins/ldap/externalUsersInGroups.js +1 -1
- package/dist/plugins/ldap/groups.js +1 -1
- package/dist/plugins/ldap/onChange.js +1 -1
- package/dist/plugins/ldap/organizations.js +1 -1
- package/dist/plugins/ldap/passwordPolicy.js +1 -1
- package/dist/plugins/ldap/raw.js +1 -1
- package/dist/plugins/ldap/trash.js +1 -1
- package/dist/plugins/rabbitmq.js +1 -1
- package/dist/plugins/scim/groups.js +1 -1
- package/dist/plugins/scim/users.js +1 -1
- package/dist/plugins/storage/ldap.js +1 -1
- package/dist/plugins/storage/postgres.js +1 -1
- package/dist/plugins/storage/valkey.js +1 -1
- package/dist/plugins/twake/appAccountsApi.js +1 -1
- package/dist/plugins/twake/appAccountsConsistency.js +1 -1
- package/dist/plugins/twake/clouderyProvision.js +1 -1
- package/dist/plugins/twake/groups.js +1 -1
- package/dist/plugins/twake/instances.js +1 -1
- package/dist/plugins/twake/james.js +1 -1
- package/dist/plugins/twake/lifecycleEvents.js +1 -1
- package/dist/plugins/twake/spaces.js +1 -1
- package/dist/plugins/twake/tombstone.js +1 -1
- package/dist/src/abstract/ldapFlat.d.ts +25 -6
- package/dist/src/abstract/plugin.d.ts +1 -1
- package/dist/src/bin/index.d.ts +12 -0
- package/dist/src/lib/authz/base.d.ts +87 -4
- package/dist/src/lib/authz/composition.d.ts +21 -0
- package/dist/src/lib/ldapActions.d.ts +139 -18
- package/dist/src/lib/mailDomains.d.ts +41 -0
- package/dist/src/lib/requestContext.d.ts +23 -0
- package/dist/src/lib/utils.d.ts +41 -2
- package/dist/src/plugins/auth/authzDynamic.d.ts +5 -12
- package/dist/src/plugins/auth/authzLinid1.d.ts +3 -1
- package/dist/src/plugins/auth/authzPerBranch.d.ts +5 -9
- package/dist/src/plugins/ldap/accountLifecycle.d.ts +2 -2
- package/dist/src/plugins/ldap/bulkImport.d.ts +30 -5
- package/dist/src/plugins/ldap/enterpriseRules.d.ts +11 -1
- package/dist/src/plugins/ldap/externalUsersInGroups.d.ts +20 -3
- package/dist/src/plugins/ldap/groups.d.ts +48 -1
- package/dist/src/plugins/ldap/organizations.d.ts +4 -4
- package/dist/src/plugins/twake/appAccountsApi.d.ts +3 -67
- package/dist/src/plugins/twake/groups.d.ts +46 -2
- package/dist/src/plugins/twake/james.d.ts +0 -11
- package/docs/README.md +25 -0
- package/docs/plugin-development/README.md +61 -15
- package/docs/plugin-development/dependencies.md +8 -2
- package/docs/plugin-development/hooks.md +4 -5
- package/docs/usage/configuration.md +11 -8
- package/docs/usage/plugins/auth/README.md +22 -3
- package/docs/usage/plugins/auth/authz-linid1.md +14 -5
- package/docs/usage/plugins/auth/authz-per-branch.md +60 -9
- package/docs/usage/plugins/integrations/app-accounts.md +5 -0
- package/docs/usage/plugins/integrations/groups.md +36 -3
- package/docs/usage/plugins/integrations/scim.md +7 -0
- package/docs/usage/plugins/integrations/spaces.md +3 -1
- package/docs/usage/plugins/ldap/bulk-import.md +20 -0
- package/docs/usage/plugins/ldap/enterprise-rules.md +12 -2
- package/docs/usage/plugins/ldap/external-users.md +8 -3
- package/docs/usage/plugins/ldap/flat-generic.md +3 -0
- package/docs/usage/plugins/ldap/groups.md +6 -0
- package/docs/usage/plugins/ldap/organizations.md +17 -0
- package/docs/usage/plugins/ldap/raw.md +1 -1
- package/docs/usage/upgrading.md +228 -0
- package/package.json +1 -1
- package/static/schemas/example/groups.json +1 -0
- package/static/schemas/example/organizations.json +1 -0
- package/static/schemas/example/users.json +3 -0
- package/static/schemas/twake/groups.json +2 -0
- package/static/schemas/twake/organizations.json +1 -0
- package/static/schemas/twake/teamMailboxes.json +3 -1
- package/static/schemas/twake/users.json +3 -0
|
@@ -320,6 +320,13 @@ the caller sent, and so does a group `PUT` for its members, hidden ones
|
|
|
320
320
|
included. A group left with no member the caller saw gets the
|
|
321
321
|
`--group-dummy-user` placeholder.
|
|
322
322
|
|
|
323
|
+
A member is resolved as the caller sees the directory, whether it is given
|
|
324
|
+
by id or by DN: an account the caller cannot read is left out of the group,
|
|
325
|
+
as one that does not exist is. Under an authorization model judging by
|
|
326
|
+
branch, a member in a branch the caller may not read refuses the whole write
|
|
327
|
+
instead, whether the account exists or not. A DN outside the user base is
|
|
328
|
+
left out.
|
|
329
|
+
|
|
323
330
|
### Schema advertisement
|
|
324
331
|
|
|
325
332
|
Schemas listed under `GET /scim/v2/Schemas` are read from
|
|
@@ -179,7 +179,9 @@ Member events compare each moved user's roles before and after a change:
|
|
|
179
179
|
## Routes
|
|
180
180
|
|
|
181
181
|
Every route answers 404 `ORGANIZATION_NOT_FOUND` when the organization entry
|
|
182
|
-
is missing, and 410 `ORGANIZATION_DELETED` when
|
|
182
|
+
is missing or the caller cannot read it, and 410 `ORGANIZATION_DELETED` when
|
|
183
|
+
it is deleted. A user the caller cannot read is not found
|
|
184
|
+
(`USER_NOT_FOUND`). Errors are
|
|
183
185
|
`{ "error": "...", "code": "..." }`. A request an authorization plugin refuses
|
|
184
186
|
answers 403 `REFUSED`. A write answers `{ "success": true }` unless stated
|
|
185
187
|
otherwise.
|
|
@@ -318,6 +318,26 @@ curl -X POST "http://localhost:8081/api/v1/ldap/bulk-import/users" \
|
|
|
318
318
|
- Check organization DNs exist
|
|
319
319
|
- Estimate import duration
|
|
320
320
|
|
|
321
|
+
A dry run is judged as the import is: a row the caller may not write fails,
|
|
322
|
+
and a row updating an existing entry counts as `updated`.
|
|
323
|
+
|
|
324
|
+
### Authorization
|
|
325
|
+
|
|
326
|
+
Each row's write is judged by the authorization plugins before anything is
|
|
327
|
+
looked for in the directory: the entry, its organization, whether it exists.
|
|
328
|
+
A row is let through when the caller may create its entry, or change the
|
|
329
|
+
entry it names (an administrator updating the accounts of their organization
|
|
330
|
+
from a file naming none). Otherwise it fails with
|
|
331
|
+
`Token does not have permission on this branch` (`code: forbidden`), whether
|
|
332
|
+
its entry and its organization are there or not.
|
|
333
|
+
|
|
334
|
+
A row the caller may write tells, as a single creation does, whether its
|
|
335
|
+
identifier is taken: it is then skipped, or updated with `updateExisting`.
|
|
336
|
+
|
|
337
|
+
The organization link of a row names one organization: a row naming several,
|
|
338
|
+
or holding an empty value (`;` in a cell), fails as invalid, reported in
|
|
339
|
+
`errors` (a 400 answer with `continueOnError=false`).
|
|
340
|
+
|
|
321
341
|
### Update Existing Mode
|
|
322
342
|
|
|
323
343
|
Update entries that already exist:
|
|
@@ -63,8 +63,18 @@ check that justified it.
|
|
|
63
63
|
non-individual entries.
|
|
64
64
|
- `filter` narrows the search, e.g. `(objectClass=twakeAccount)`.
|
|
65
65
|
|
|
66
|
-
On an update the entry
|
|
67
|
-
|
|
66
|
+
On an update, a value the entry already holds, as stored and spelled the
|
|
67
|
+
same, is not looked for again; only the values it does not hold yet are.
|
|
68
|
+
Re-sending an unchanged value conflicts neither with the entry itself nor
|
|
69
|
+
with the copies other plugins keep of it — the applicative accounts of
|
|
70
|
+
`appAccountsConsistency` carry the account's mail. A value held under
|
|
71
|
+
another attribute the constraint spans, an alias becoming the mail, is
|
|
72
|
+
skipped only when that attribute's own constraint looks for it everywhere
|
|
73
|
+
this one does; otherwise it is checked. A duplicate the directory already
|
|
74
|
+
holds, from before the rule or another tool, is therefore not reported on a
|
|
75
|
+
re-save. A value spelled differently is a new one, and looked for: changing
|
|
76
|
+
only the case of an account's mail is refused while an applicative copy
|
|
77
|
+
holds the old spelling, which the directory takes for the same address.
|
|
68
78
|
|
|
69
79
|
### Mail domains
|
|
70
80
|
|
|
@@ -37,7 +37,7 @@ This plugin intercepts group member validation and automatically creates missing
|
|
|
37
37
|
the address escaped as any DN value (RFC 4514): `mail=a\+b@partner.com,…`
|
|
38
38
|
creates an entry whose `mail` is `a+b@partner.com`. Only the direct children of the
|
|
39
39
|
branch are external members.
|
|
40
|
-
- **Domain Protection**: External users cannot be created if their mail
|
|
40
|
+
- **Domain Protection**: External users cannot be created if their mail is in a managed domain, compared case-insensitively. The managed domains are those of `--mail-domain` and those the directory declares in its domain branches: the entries of a flat schema declaring the `domainName` role, or else `ou=domains,ou=nomenclature,<base>` with `--enterprise-domain-name-attribute` (`associatedDomain`). A subdomain of a managed domain is not managed: `user@sub.example.com` is external unless `sub.example.com` is declared itself
|
|
41
41
|
- **Automatic Creation**: Users are created automatically when added to a group if they don't exist
|
|
42
42
|
|
|
43
43
|
### Config API
|
|
@@ -56,7 +56,8 @@ where to send them:
|
|
|
56
56
|
|
|
57
57
|
A client turns an address into a member with
|
|
58
58
|
`rdnAttribute=<escaped address>,<branch>`, and can refuse a `managedDomains`
|
|
59
|
-
address before sending it.
|
|
59
|
+
address before sending it. The list holds the domains declared in the
|
|
60
|
+
directory as well as those of `--mail-domain`.
|
|
60
61
|
|
|
61
62
|
## How It Works
|
|
62
63
|
|
|
@@ -225,6 +226,9 @@ Protect multiple domains from external user creation:
|
|
|
225
226
|
--mail-domain example.com,company.org,subsidiary.net
|
|
226
227
|
```
|
|
227
228
|
|
|
229
|
+
The domains declared in the directory are protected without being listed
|
|
230
|
+
here.
|
|
231
|
+
|
|
228
232
|
### Custom Attributes via Hooks
|
|
229
233
|
|
|
230
234
|
Add organization-specific attributes to external users:
|
|
@@ -261,7 +265,8 @@ hooks: {
|
|
|
261
265
|
**Solutions:**
|
|
262
266
|
|
|
263
267
|
1. Use the correct branch for internal users: `ou=users,dc=example,dc=com`
|
|
264
|
-
2. Remove domain from `--mail-domain
|
|
268
|
+
2. Remove the domain from `--mail-domain`, or from the directory's domain
|
|
269
|
+
branches, if it should allow external users
|
|
265
270
|
3. This is expected behavior for internal domains - create user in users branch instead
|
|
266
271
|
|
|
267
272
|
### Malformed Member DN
|
|
@@ -120,6 +120,9 @@ Each schema file must include an `entity` metadata section and attribute definit
|
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
Pointer validation ensures the DN exists and is within allowed branches.
|
|
123
|
+
The DN is looked for as the server, whoever writes: a caller may point at
|
|
124
|
+
an entry it cannot read, and learns from the answer whether one is there,
|
|
125
|
+
never what it holds.
|
|
123
126
|
|
|
124
127
|
#### Fixed Attributes
|
|
125
128
|
|
|
@@ -410,6 +410,12 @@ The plugin supports JSON schema validation for group attributes. Example schema:
|
|
|
410
410
|
}
|
|
411
411
|
```
|
|
412
412
|
|
|
413
|
+
A `pointer` attribute is checked as in
|
|
414
|
+
[flat schemas](flat-generic.md#pointer-dn-reference): the DN must name an
|
|
415
|
+
entry in the allowed branches, looked for as the server, whoever writes.
|
|
416
|
+
A caller may point at an entry it cannot read, and learns from the answer
|
|
417
|
+
whether one is there, never what it holds.
|
|
418
|
+
|
|
413
419
|
## Hooks
|
|
414
420
|
|
|
415
421
|
The plugin emits and listens to lifecycle hooks:
|
|
@@ -174,6 +174,14 @@ curl "http://localhost:8081/api/v1/ldap/organizations/ou%3DIT%2Co%3Dgov%2Cc%3Dmu
|
|
|
174
174
|
]
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
+
**Where the attached entries are looked for**
|
|
178
|
+
|
|
179
|
+
Below the parent of `--ldap-top-organization`, as the caller. A caller an
|
|
180
|
+
authorization plugin does not let search there — `authzPerBranch` judging
|
|
181
|
+
by DN, a token of `authzDynamic` — gets the entries attached within the
|
|
182
|
+
organization's own branch; those attached from elsewhere are not listed.
|
|
183
|
+
`/subnodes/search` looks the same way.
|
|
184
|
+
|
|
177
185
|
**When the answer is partial**
|
|
178
186
|
|
|
179
187
|
Two things bound what comes back, and both are announced in the list rather
|
|
@@ -715,6 +723,15 @@ The plugin validates organizations against configured object classes. Example sc
|
|
|
715
723
|
}
|
|
716
724
|
```
|
|
717
725
|
|
|
726
|
+
A `pointer` attribute is checked as in
|
|
727
|
+
[flat schemas](flat-generic.md#pointer-dn-reference): the DN must name an
|
|
728
|
+
entry in the allowed branches, looked for as the server, whoever writes.
|
|
729
|
+
A caller may point at an entry it cannot read, and learns from the answer
|
|
730
|
+
whether one is there, never what it holds. A pointer that grants rights
|
|
731
|
+
follows the same rule: an organization administrator may name, in
|
|
732
|
+
`twakeLocalAdminLink`, an account they cannot read, and so hand the
|
|
733
|
+
administration of their organization to it.
|
|
734
|
+
|
|
718
735
|
## See Also
|
|
719
736
|
|
|
720
737
|
- [LDAP Flat Plugin](./ldapFlat.md) - For managing flat LDAP entities (users, positions, nomenclature) through schema-driven approach
|
|
@@ -199,7 +199,7 @@ Combine with an authentication plugin ([token](../auth/token.md), [oidc](../auth
|
|
|
199
199
|
|
|
200
200
|
- **Schema fetch**: the root DSE is read for `subschemaSubentry` (falling back to `cn=Subschema`), then the subschema entry is fetched and parsed once per `--ldap-raw-schema-cache-ttl`. Concurrent callers share a single fetch, and so does the [object class repair](flat-generic.md#entries-created-by-other-tools), which loads it even without this plugin.
|
|
201
201
|
- **Operational attributes**: entries are read with `*` and `+`. When the server rejects `+`, the read is retried with `*` alone.
|
|
202
|
-
- **Missing entries**: the LDAP "no such object" result (code 32) is mapped to a 404.
|
|
202
|
+
- **Missing entries**: the LDAP "no such object" result (code 32) is mapped to a 404. A search whose base the caller cannot read answers the same 404 as one whose base does not exist.
|
|
203
203
|
|
|
204
204
|
## Limitations
|
|
205
205
|
|
package/docs/usage/upgrading.md
CHANGED
|
@@ -4,6 +4,234 @@ 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 0.26 to 1.0.0
|
|
8
|
+
|
|
9
|
+
Every breaking change since 0.26.0. Authorization fails closed in 1.0.0:
|
|
10
|
+
what used to pass because a call site forgot the request, or because a
|
|
11
|
+
name was spelled otherwise, is now judged, and may be refused.
|
|
12
|
+
|
|
13
|
+
### Attribute names in a write: one name per attribute, no option
|
|
14
|
+
|
|
15
|
+
**Who is affected:** a client, a CSV of the bulk import or a plugin writing
|
|
16
|
+
an attribute with an option other than `;binary` (`title;lang-fr`,
|
|
17
|
+
`member;x-tag`), or naming one attribute twice in one part of a write
|
|
18
|
+
(`mail` and `MAIL`, `mail` and `rfc822Mailbox`).
|
|
19
|
+
|
|
20
|
+
Every add and modify now names its attributes as this server does before
|
|
21
|
+
it is judged: the name is resolved through the directory schema, so `MAIL`,
|
|
22
|
+
`rfc822Mailbox` and `0.9.2342.19200300.100.1.3` are all written `mail`.
|
|
23
|
+
The spelling kept is the configuration's or a loaded schema's, else the
|
|
24
|
+
first name the directory declares. An option other than `;binary` is
|
|
25
|
+
refused with a 400: a tagged value is one of its own, which the rules
|
|
26
|
+
guarding the attribute would not see. Two names for one attribute in one
|
|
27
|
+
`add`, `replace` or `delete` are refused with a 400 too. Values already
|
|
28
|
+
stored under an option are still read, but cannot be written or deleted
|
|
29
|
+
through ldap-rest: clean them up in the directory.
|
|
30
|
+
|
|
31
|
+
A rename gives the entry one value of the attribute it is already named by
|
|
32
|
+
(`cn=new` for a group named by `cn`): a new RDN holding several values, or
|
|
33
|
+
naming another attribute, is refused with a 400. The directory writes the
|
|
34
|
+
values of the new RDN on the entry, where no check of a write saw them.
|
|
35
|
+
|
|
36
|
+
When the directory schema cannot be read, the names, aliases and OIDs of
|
|
37
|
+
the standard types (`cn`, `mail`, `uid`, `ou`…) are still resolved, as are
|
|
38
|
+
the spellings of the configuration and the schemas. A name given as another
|
|
39
|
+
OID is refused, and another alias is written as given.
|
|
40
|
+
|
|
41
|
+
### SCIM group members are looked up as the caller
|
|
42
|
+
|
|
43
|
+
**Who is affected:** a SCIM client writing group members by DN, and one
|
|
44
|
+
that tells an absent user from a hidden one by the message of
|
|
45
|
+
`PUT /Users/{id}`.
|
|
46
|
+
|
|
47
|
+
A member given by DN in `POST`, `PUT` or `PATCH /Groups` was taken as it
|
|
48
|
+
stood. It is now looked up as the caller, as a member given by id is: a DN
|
|
49
|
+
the caller cannot read, or that names no entry, is left out of the group,
|
|
50
|
+
and under a model judging by branch a member in a refused branch makes the
|
|
51
|
+
whole write refused. A DN outside the user base is left out, compared RDN by
|
|
52
|
+
RDN. A client that wrote members it may not read asks an administrator
|
|
53
|
+
whose rules cover them.
|
|
54
|
+
|
|
55
|
+
`PUT /Users/{id}` on a user that is not there answered `Resource not found`;
|
|
56
|
+
it now answers `User {id} not found`, as it did for a user the caller may
|
|
57
|
+
not read. The status, `404`, is unchanged.
|
|
58
|
+
|
|
59
|
+
### The group routes take a DN for a group only
|
|
60
|
+
|
|
61
|
+
**Who is affected:** a client calling `/api/v1/ldap/groups/{cn}` with a
|
|
62
|
+
DN rather than a name.
|
|
63
|
+
|
|
64
|
+
A DN given to these routes, to read, write, delete, create or rename a
|
|
65
|
+
group, must name a group below the groups branch (`--ldap-group-base`),
|
|
66
|
+
named by the groups' main attribute (`--ldap-groups-main-attribute`, `cn`
|
|
67
|
+
by default) and of a group class (`--group-class`, or a standard one) the
|
|
68
|
+
caller reads; anything else answers `404 Group not found`, and a DN the
|
|
69
|
+
directory would refuse `400`. A client that managed the organization
|
|
70
|
+
groups of `core/twake/groups` by DN through these routes, when they lie
|
|
71
|
+
outside `--ldap-group-base`, uses `/api/v1/organizations/{id}/groups`.
|
|
72
|
+
A deployment whose groups carry a class of their own, and none of the
|
|
73
|
+
standard ones, declares it in `--group-class`.
|
|
74
|
+
|
|
75
|
+
### Reference data still in use is not deleted
|
|
76
|
+
|
|
77
|
+
**Who is affected:** a deployment loading the shipped `twake` or `example`
|
|
78
|
+
schemas with `core/ldap/enterpriseRules`.
|
|
79
|
+
|
|
80
|
+
Deleting a mail domain an organization links, or a nomenclature value an
|
|
81
|
+
entry holds (account status, delivery mode, personal title, list type,
|
|
82
|
+
mailbox type), answers `409 still referenced` instead of leaving the
|
|
83
|
+
reference naming nothing. Change the referrers first. A deployment with
|
|
84
|
+
its own schemas marks its pointers `"referentialIntegrity": "restrict"` to
|
|
85
|
+
get the same. References are looked for under the base each entity is
|
|
86
|
+
configured with; a base the directory does not hold is logged once and
|
|
87
|
+
holds none.
|
|
88
|
+
|
|
89
|
+
### Plugins: an operation naming no request is judged as the caller
|
|
90
|
+
|
|
91
|
+
**Who is affected:** a third-party plugin calling `this.server.ldap.search`,
|
|
92
|
+
`add`, `modify`, `rename` or `delete` without a request, from a route, while
|
|
93
|
+
an authorization plugin is loaded. `move` is the server's and stays unjudged:
|
|
94
|
+
move an entry for a caller with `rename`.
|
|
95
|
+
|
|
96
|
+
Such an operation used to pass unjudged, as the server's. While an
|
|
97
|
+
authenticated request is being served, it is now judged as that request's
|
|
98
|
+
caller, and refused where the caller may not. Hand the request down
|
|
99
|
+
(`ldap.forRequest(req)`), or, for the server's own work, go through
|
|
100
|
+
`ldap.system`. Hooks run outside the request and keep acting as the server.
|
|
101
|
+
|
|
102
|
+
Under `core/auth/authzDynamic`, a hook's operations that named no request
|
|
103
|
+
used to be judged against the token, through an async context of the
|
|
104
|
+
plugin's own; they now act as the server, as under the other authorization
|
|
105
|
+
plugins. A hook writing on the caller's behalf passes the request it is
|
|
106
|
+
handed. That context, `authzContext`, is no longer exported: read the token
|
|
107
|
+
off the request (`req.authzToken`).
|
|
108
|
+
|
|
109
|
+
### An authorization plugin needs an authentication plugin
|
|
110
|
+
|
|
111
|
+
**Who is affected:** a deployment loading `core/auth/authzPerBranch`,
|
|
112
|
+
`core/auth/authzLinid1` or `core/auth/authzPerRoute` without any
|
|
113
|
+
authentication plugin.
|
|
114
|
+
|
|
115
|
+
Such a server no longer starts. No request carried an identity, so each of
|
|
116
|
+
these plugins stepped aside and every request passed, whatever the rules
|
|
117
|
+
said. Load the authentication plugin the rules were written for, or remove
|
|
118
|
+
the authorization plugins to serve the directory openly, as it already was.
|
|
119
|
+
|
|
120
|
+
### Nested branch rules of `authzPerBranch`
|
|
121
|
+
|
|
122
|
+
**Who is affected:** a deployment of `core/auth/authzPerBranch` whose rules
|
|
123
|
+
for one user, or one group, name a branch and a sub-branch of it.
|
|
124
|
+
|
|
125
|
+
The rule of the sub-branch now applies to everything below it, whatever
|
|
126
|
+
order the two are written in. It used to apply only where it matched
|
|
127
|
+
exactly — the sub-branch entry, and writes on its direct children, judged
|
|
128
|
+
on their parent — and below that the first rule written won. A sub-branch rule that
|
|
129
|
+
was ignored, a restriction or a grant, now takes effect in single reads and
|
|
130
|
+
writes and in searches: a search on the parent no longer lists what the
|
|
131
|
+
sub-branch rule refuses, so a listing may now be shorter. Check such rules:
|
|
132
|
+
they now mean what they say.
|
|
133
|
+
|
|
134
|
+
`default` no longer adds to what the rules grant: it applies to a
|
|
135
|
+
permission none of the rules that apply sets. A rule's `read`, `write` or
|
|
136
|
+
`delete` set to `false` now refuses, where `"default": {"read": true}` used
|
|
137
|
+
to grant it anyway. A permission a rule leaves unset is still the
|
|
138
|
+
default's, not the broader rule's.
|
|
139
|
+
|
|
140
|
+
Renaming or moving an entry a rule names, or one holding such an entry, now
|
|
141
|
+
needs read and write permission at each of those rules, and deleting it
|
|
142
|
+
needs delete there; changing the entry itself needs that rule's write
|
|
143
|
+
permission, as does creating it. A write setting the organization link and
|
|
144
|
+
changing anything besides the link and its path needs write where the
|
|
145
|
+
entry stands — its branch, or under `--authz-filter-attached-entries` the
|
|
146
|
+
organization it leaves: it used to need only read there, so the rest of
|
|
147
|
+
the write — a password, say — went through. Moving an entry between
|
|
148
|
+
organizations is unchanged.
|
|
149
|
+
|
|
150
|
+
### Organization groups need read access to the organization
|
|
151
|
+
|
|
152
|
+
**Who is affected:** a deployment loading an authorization plugin together
|
|
153
|
+
with `core/twake/groups` (and `core/twake/spaces`, which goes through it).
|
|
154
|
+
|
|
155
|
+
The `/organizations/{id}/groups` and `/organizations/{id}/spaces` routes read
|
|
156
|
+
as the caller. A client calling them must be granted read access to the
|
|
157
|
+
organization, its groups and its users, write access to change them and
|
|
158
|
+
delete access to delete one. An organization it cannot read answers `404`,
|
|
159
|
+
and a user it cannot read `404 USER_NOT_FOUND` where it used to get `403`.
|
|
160
|
+
|
|
161
|
+
Under `core/auth/authzLinid1` or `--authz-filter-attached-entries`, groups
|
|
162
|
+
written in an organization's branch now carry its link
|
|
163
|
+
(`--ldap-organization-link-attribute`): creating or changing a group needs
|
|
164
|
+
write on the organization, and a group without a link is read and changed by
|
|
165
|
+
every administrator. Link the groups written before; this script is an
|
|
166
|
+
example to adapt to your patterns, attribute names and organization names
|
|
167
|
+
(it expects plain ones, without spaces or commas):
|
|
168
|
+
|
|
169
|
+
```sh
|
|
170
|
+
# Connection to the directory, as for any ldapsearch
|
|
171
|
+
OPTS="-H ldap://localhost -D cn=admin,dc=example,dc=com -w secret"
|
|
172
|
+
# --twake-group-organization-dn and --twake-group-base, {org} left out
|
|
173
|
+
ORGS="dc=example,dc=com"
|
|
174
|
+
for org in $(ldapsearch $OPTS -LLL -s one -b "$ORGS" '(ou=*)' ou |
|
|
175
|
+
sed -n 's/^ou: //p'); do
|
|
176
|
+
ldapsearch $OPTS -LLL -o ldif-wrap=no -s one \
|
|
177
|
+
-b "ou=groups,ou=$org,$ORGS" '(!(twakeDepartmentLink=*))' objectClass |
|
|
178
|
+
awk -v link="ou=$org,$ORGS" '
|
|
179
|
+
function flush() {
|
|
180
|
+
if (!dn) return
|
|
181
|
+
print "dn: " dn "\nchangetype: modify"
|
|
182
|
+
# The class is added only to a group lacking one allowing the link
|
|
183
|
+
if (!held) print "add: objectClass\nobjectClass: twakeGroup\n-"
|
|
184
|
+
print "add: twakeDepartmentLink\ntwakeDepartmentLink: " link "\n"
|
|
185
|
+
dn = ""
|
|
186
|
+
}
|
|
187
|
+
/^dn: / { flush(); dn = substr($0, 5); held = 0 }
|
|
188
|
+
tolower($0) ~ /^objectclass: (twakegroup|twakestaticgroup)$/ { held = 1 }
|
|
189
|
+
END { flush() }'
|
|
190
|
+
done | ldapmodify $OPTS
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### The organization link holds one organization
|
|
194
|
+
|
|
195
|
+
**Who is affected:** a client or a CSV writing several organization links on
|
|
196
|
+
one entry, or an empty one.
|
|
197
|
+
|
|
198
|
+
A write giving the organization link (`--ldap-organization-link-attribute`)
|
|
199
|
+
several values, or an empty value, is refused with a 400: only one of them
|
|
200
|
+
was judged. Under any spelling of the attribute, adding a link to an entry
|
|
201
|
+
that had none is judged as a move: write where it goes and, judged by
|
|
202
|
+
branch, read where it stands.
|
|
203
|
+
|
|
204
|
+
### Smaller changes in answers
|
|
205
|
+
|
|
206
|
+
- `GET /api/v1/ldap/raw/search` below a base the caller may not read
|
|
207
|
+
answers 404, as for a base that is not there; under `authzPerBranch`
|
|
208
|
+
judging by DN it answered 403.
|
|
209
|
+
- Changing only the case of an account's mail is refused (409) while an
|
|
210
|
+
applicative copy holds the old spelling, which the directory takes for
|
|
211
|
+
the same address.
|
|
212
|
+
|
|
213
|
+
### External members in a domain the directory declares
|
|
214
|
+
|
|
215
|
+
**Who is affected:** a deployment of `core/ldap/externalUsersInGroups`
|
|
216
|
+
adding as external members addresses in a mail domain the directory
|
|
217
|
+
declares, without listing it in `--mail-domain`.
|
|
218
|
+
|
|
219
|
+
Such a member is refused, as one in a `--mail-domain` already was. The
|
|
220
|
+
managed domains are now also those of the directory's domain branches,
|
|
221
|
+
[docs](plugins/ldap/external-users.md#important-notes); `GET /config` lists
|
|
222
|
+
them in `managedDomains`.
|
|
223
|
+
|
|
224
|
+
### Quota and app accounts need read access to the account
|
|
225
|
+
|
|
226
|
+
**Who is affected:** a deployment loading an authorization plugin
|
|
227
|
+
(`core/auth/authzPerBranch`, `authzLinid1`, `authzDynamic`) together with
|
|
228
|
+
`core/twake/james` or `core/twake/appAccountsApi`.
|
|
229
|
+
|
|
230
|
+
`GET /users/{user}/quota-usage` and the app account routes answer `404` for
|
|
231
|
+
an account the caller cannot read. A client calling them must be granted
|
|
232
|
+
read access to the accounts it asks about, as it already needed write
|
|
233
|
+
access to create or delete an app account.
|
|
234
|
+
|
|
7
235
|
## To 0.24.0
|
|
8
236
|
|
|
9
237
|
### Links to deleted accounts
|
package/package.json
CHANGED
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
"personalTitle": {
|
|
78
78
|
"type": "pointer",
|
|
79
79
|
"branch": ["ou=twakeTitle,ou=nomenclature,__LDAP_BASE__"],
|
|
80
|
+
"referentialIntegrity": "restrict",
|
|
80
81
|
"required": false,
|
|
81
82
|
"label": "Title"
|
|
82
83
|
},
|
|
@@ -203,6 +204,7 @@
|
|
|
203
204
|
"twakeAccountStatus": {
|
|
204
205
|
"type": "pointer",
|
|
205
206
|
"branch": ["ou=twakeAccountStatus,ou=nomenclature,__LDAP_BASE__"],
|
|
207
|
+
"referentialIntegrity": "restrict",
|
|
206
208
|
"required": true,
|
|
207
209
|
"generated": true,
|
|
208
210
|
"role": "accountStatus",
|
|
@@ -220,6 +222,7 @@
|
|
|
220
222
|
"type": "pointer",
|
|
221
223
|
"branch": ["ou=twakeDeliveryMode,ou=nomenclature,__LDAP_BASE__"]
|
|
222
224
|
},
|
|
225
|
+
"referentialIntegrity": "restrict",
|
|
223
226
|
"required": true,
|
|
224
227
|
"generated": true,
|
|
225
228
|
"default": [
|
|
@@ -82,6 +82,7 @@
|
|
|
82
82
|
"businessCategory": {
|
|
83
83
|
"type": "pointer",
|
|
84
84
|
"branch": ["ou=twakeListType,ou=nomenclature,__LDAP_BASE__"],
|
|
85
|
+
"referentialIntegrity": "restrict",
|
|
85
86
|
"required": false,
|
|
86
87
|
"role": "writePolicy",
|
|
87
88
|
"label": {
|
|
@@ -98,6 +99,7 @@
|
|
|
98
99
|
"twakeMailboxType": {
|
|
99
100
|
"type": "pointer",
|
|
100
101
|
"branch": ["ou=twakeMailboxType,ou=nomenclature,__LDAP_BASE__"],
|
|
102
|
+
"referentialIntegrity": "restrict",
|
|
101
103
|
"group": {
|
|
102
104
|
"en": "Mailbox Settings",
|
|
103
105
|
"fr": "Paramètres de messagerie",
|
|
@@ -80,7 +80,9 @@
|
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
82
|
"twakeMailboxType": {
|
|
83
|
-
"type": "
|
|
83
|
+
"type": "pointer",
|
|
84
|
+
"branch": ["ou=twakeMailboxType,ou=nomenclature,__LDAP_BASE__"],
|
|
85
|
+
"referentialIntegrity": "restrict",
|
|
84
86
|
"default": "cn=teamMailbox,ou=twakeMailboxType,ou=nomenclature,__LDAP_BASE__",
|
|
85
87
|
"fixed": true,
|
|
86
88
|
"required": true,
|
|
@@ -124,6 +124,7 @@
|
|
|
124
124
|
"personalTitle": {
|
|
125
125
|
"type": "pointer",
|
|
126
126
|
"branch": ["ou=twakeTitle,ou=nomenclature,__LDAP_BASE__"],
|
|
127
|
+
"referentialIntegrity": "restrict",
|
|
127
128
|
"required": false,
|
|
128
129
|
"label": {
|
|
129
130
|
"en": "Title",
|
|
@@ -324,6 +325,7 @@
|
|
|
324
325
|
"twakeAccountStatus": {
|
|
325
326
|
"type": "pointer",
|
|
326
327
|
"branch": ["ou=twakeAccountStatus,ou=nomenclature,__LDAP_BASE__"],
|
|
328
|
+
"referentialIntegrity": "restrict",
|
|
327
329
|
"required": true,
|
|
328
330
|
"generated": true,
|
|
329
331
|
"role": "accountStatus",
|
|
@@ -346,6 +348,7 @@
|
|
|
346
348
|
"type": "pointer",
|
|
347
349
|
"branch": ["ou=twakeDeliveryMode,ou=nomenclature,__LDAP_BASE__"]
|
|
348
350
|
},
|
|
351
|
+
"referentialIntegrity": "restrict",
|
|
349
352
|
"required": true,
|
|
350
353
|
"generated": true,
|
|
351
354
|
"default": [
|