ldap-rest 0.6.2 → 0.8.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 (99) hide show
  1. package/dist/abstract/ldapFlat.js +1 -1
  2. package/dist/abstract/plugin.js +1 -1
  3. package/dist/bin/index.js +1 -1
  4. package/dist/config/args.js +1 -1
  5. package/dist/config/schema.js +1 -0
  6. package/dist/lib/expressFormatedResponses.js +1 -1
  7. package/dist/lib/ldapActions.js +1 -1
  8. package/dist/lib/utils.js +1 -1
  9. package/dist/plugins/auth/authzScope.js +1 -0
  10. package/dist/plugins/auth/llng.js +1 -1
  11. package/dist/plugins/ldap/accountLifecycle.js +1 -0
  12. package/dist/plugins/ldap/bulkImport.js +1 -1
  13. package/dist/plugins/ldap/departmentSync.js +1 -1
  14. package/dist/plugins/ldap/enterpriseRules.js +1 -0
  15. package/dist/plugins/ldap/externalUsersInGroups.js +1 -1
  16. package/dist/plugins/ldap/flatGeneric.js +1 -1
  17. package/dist/plugins/ldap/groups.js +1 -1
  18. package/dist/plugins/ldap/organizations.js +1 -1
  19. package/dist/plugins/scim/bulk.js +1 -1
  20. package/dist/plugins/scim/errors.js +1 -1
  21. package/dist/plugins/scim/filter.js +1 -1
  22. package/dist/plugins/scim/groups.js +1 -1
  23. package/dist/plugins/scim/list.js +1 -1
  24. package/dist/plugins/scim/mapping.js +1 -1
  25. package/dist/plugins/scim/patch.js +1 -1
  26. package/dist/plugins/scim/projection.js +1 -0
  27. package/dist/plugins/scim/scim.js +1 -1
  28. package/dist/plugins/scim/users.js +1 -1
  29. package/dist/plugins/twake/calendar.js +1 -0
  30. package/dist/plugins/twake/calendarResources.js +1 -1
  31. package/dist/plugins/twake/james.js +1 -1
  32. package/dist/src/abstract/ldapFlat.d.ts +159 -1
  33. package/dist/src/abstract/plugin.d.ts +13 -0
  34. package/dist/src/config/args.d.ts +5 -0
  35. package/dist/src/config/schema.d.ts +382 -12
  36. package/dist/src/lib/expressFormatedResponses.d.ts +14 -0
  37. package/dist/src/lib/ldapActions.d.ts +59 -6
  38. package/dist/src/lib/utils.d.ts +65 -0
  39. package/dist/src/plugins/auth/authzScope.d.ts +53 -0
  40. package/dist/src/plugins/auth/llng.d.ts +30 -1
  41. package/dist/src/plugins/ldap/accountLifecycle.d.ts +56 -0
  42. package/dist/src/plugins/ldap/departmentSync.d.ts +43 -0
  43. package/dist/src/plugins/ldap/enterpriseRules.d.ts +436 -0
  44. package/dist/src/plugins/ldap/flatGeneric.d.ts +29 -0
  45. package/dist/src/plugins/ldap/groups.d.ts +22 -0
  46. package/dist/src/plugins/ldap/organizations.d.ts +25 -4
  47. package/dist/src/plugins/scim/errors.d.ts +17 -4
  48. package/dist/src/plugins/scim/filter.d.ts +11 -1
  49. package/dist/src/plugins/scim/groups.d.ts +2 -0
  50. package/dist/src/plugins/scim/list.d.ts +3 -5
  51. package/dist/src/plugins/scim/mapping.d.ts +90 -9
  52. package/dist/src/plugins/scim/patch.d.ts +16 -1
  53. package/dist/src/plugins/scim/projection.d.ts +15 -0
  54. package/dist/src/plugins/scim/scim.d.ts +5 -0
  55. package/dist/src/plugins/scim/users.d.ts +21 -0
  56. package/dist/src/plugins/twake/calendar.d.ts +113 -0
  57. package/dist/src/plugins/twake/calendarResources.d.ts +9 -58
  58. package/docs/README.md +2 -0
  59. package/docs/client-development/api/openapi.md +1 -1
  60. package/docs/client-development/browser/libraries.md +1 -1
  61. package/docs/plugin-development/README.md +22 -0
  62. package/docs/plugin-development/dependencies.md +10 -7
  63. package/docs/presentations/en.md +1 -1
  64. package/docs/presentations/fr.md +1 -1
  65. package/docs/usage/README.md +2 -0
  66. package/docs/usage/configuration.md +35 -15
  67. package/docs/usage/directory-audit.md +105 -0
  68. package/docs/usage/plugins/README.md +6 -6
  69. package/docs/usage/plugins/auth/README.md +6 -5
  70. package/docs/usage/plugins/auth/authz-scope.md +103 -0
  71. package/docs/usage/plugins/auth/llng.md +17 -0
  72. package/docs/usage/plugins/integrations/README.md +5 -5
  73. package/docs/usage/plugins/integrations/{calendar-resources.md → calendar.md} +70 -13
  74. package/docs/usage/plugins/integrations/scim.md +147 -38
  75. package/docs/usage/plugins/ldap/README.md +13 -11
  76. package/docs/usage/plugins/ldap/account-lifecycle.md +129 -0
  77. package/docs/usage/plugins/ldap/bulk-import.md +8 -1
  78. package/docs/usage/plugins/ldap/enterprise-rules.md +167 -0
  79. package/docs/usage/plugins/ldap/flat-generic.md +172 -0
  80. package/docs/usage/plugins/ldap/groups.md +10 -4
  81. package/docs/usage/plugins/ldap/organizations.md +59 -33
  82. package/docs/usage/upgrading.md +444 -0
  83. package/package.json +54 -5
  84. package/static/schemas/example/README.md +36 -0
  85. package/static/schemas/example/groups.json +94 -0
  86. package/static/schemas/example/nomenclature/domains.json +49 -0
  87. package/static/schemas/example/organizations.json +106 -0
  88. package/static/schemas/example/positions.json +35 -0
  89. package/static/schemas/example/users.json +246 -0
  90. package/static/schemas/twake/groups.json +81 -15
  91. package/static/schemas/twake/nomenclature/twakeAccountStatus.json +26 -0
  92. package/static/schemas/twake/nomenclature/twakeDeliveryMode.json +22 -0
  93. package/static/schemas/twake/nomenclature/twakeDomain.json +60 -0
  94. package/static/schemas/twake/nomenclature/twakeListType.json +22 -0
  95. package/static/schemas/twake/nomenclature/twakeMailboxType.json +22 -0
  96. package/static/schemas/twake/nomenclature/twakeTitle.json +42 -0
  97. package/static/schemas/twake/organizations.json +92 -12
  98. package/static/schemas/twake/positions.json +22 -4
  99. package/static/schemas/twake/users.json +269 -32
@@ -0,0 +1,105 @@
1
+ # Auditing a directory before a migration
2
+
3
+ Tightening a validation pattern changes what the server accepts **from now
4
+ on**. It says nothing about the entries already stored: a directory whose
5
+ addresses were never anchored, or whose organization names predate the rule
6
+ that now governs them, loads fine and then refuses the first update of every
7
+ offending entry — which is discovered one support ticket at a time.
8
+
9
+ `npm run audit:directory` reads a branch as it is and reports what the schema
10
+ would refuse, so the work is known before the switch rather than after it.
11
+
12
+ ## Running it
13
+
14
+ ```sh
15
+ export DM_LDAP_URL=ldap://directory.example.org
16
+ export DM_LDAP_DN='cn=admin,dc=example,dc=com'
17
+ export DM_LDAP_PWD='…'
18
+ export DM_LDAP_BASE='dc=example,dc=com'
19
+
20
+ npm run audit:directory -- --schema static/schemas/twake/users.json \
21
+ --plugin core/ldap/enterpriseRules
22
+ ```
23
+
24
+ | Option | Default | Purpose |
25
+ | ----------------- | ---------------------------- | ----------------------------------- |
26
+ | `--schema` | _(required)_ | Schema to check against |
27
+ | `--base` | the schema's own entity base | Branch to read |
28
+ | `--filter` | `(objectClass=*)` | Narrows what is audited |
29
+ | `--url` | `DM_LDAP_URL` | Directory to read |
30
+ | `--bind-dn` | `DM_LDAP_DN` | Identity to read with |
31
+ | `--bind-password` | `DM_LDAP_PWD` | Its password |
32
+ | `--samples` | `5` | Offending entries named per finding |
33
+ | `--plugin` | _(none)_ | A plugin the server loads, repeated |
34
+
35
+ `--plugin` matters for the attributes a schema marks both `required` and
36
+ `generated`. The server exempts one from its check only when something fills
37
+ it: the entity itself (`generatedFrom`), or a loaded plugin. Name the plugins
38
+ the server loads and the audit exempts what they fill; without them it reports
39
+ those attributes as `missing, and no --plugin fills it` — which is what every
40
+ creation would be refused for on a server that does not load them.
41
+
42
+ It exits `0` when the branch is clean, `1` when the schema would refuse
43
+ something, and `2` when the audit itself could not run — an unreadable schema,
44
+ a directory that would not answer — so a pipeline can tell "dirty" from
45
+ "broken" rather than gating on both alike.
46
+
47
+ `--bind-password` puts the password in `ps` and in the shell history of
48
+ whoever runs it. `DM_LDAP_PWD` is the path to prefer, and the flag is there
49
+ for parity with the server's own `--ldap-pwd`.
50
+
51
+ ## What it checks, and what only the server can
52
+
53
+ The audit reads a schema and an entry, so it answers what an entry alone can
54
+ answer: required attributes, the `test` pattern of a value and of an array's
55
+ elements, and the branch a DN must land in. It replaces `__KEY__` placeholders
56
+ and `{config_key}` in `entity.base` from the `DM_*` environment exactly as the
57
+ server does, leaving a placeholder the configuration does not name.
58
+
59
+ What it does not answer needs the rest of the directory, and belongs to a dry
60
+ run rather than here: uniqueness across a namespace, the mail domains an
61
+ organization owns, and anything a plugin computes after validation. A clean
62
+ audit is not a promise that every entry will survive every rule — it is a
63
+ promise that none of them fails on its own contents.
64
+
65
+ ## Reading the report
66
+
67
+ ```
68
+ 12632 entries read, 47 value(s) the schema would refuse:
69
+
70
+ mail: does not match — 41
71
+ expected: Expected an address in the example.org domain, at most 64 characters before the @
72
+ uid=jean.dupont,ou=users,dc=example,dc=com → "jean.dupont@old-domain.example"
73
+ …and 36 more
74
+
75
+ postalCode: does not match — 6
76
+ expected: Expected pattern 9999, A9999 or R9999
77
+ uid=marie.martin,ou=users,dc=example,dc=com → "1234"
78
+ ```
79
+
80
+ Each finding names the attribute, why it was refused, and — from the schema's
81
+ `hint` — what a valid value looks like. The same wording reaches the person
82
+ filling the form, so the fix and the rule are described identically.
83
+
84
+ ## What it checks, and what it cannot
85
+
86
+ Checked, because an entry answers for itself:
87
+
88
+ - `test` patterns, on single and multi-valued attributes
89
+ - `required` attributes that are absent — except the ones the server computes
90
+ (`generated`), which an entry is not expected to carry yet
91
+ - the `branch` a `pointer` must land in
92
+
93
+ Not checked, because it takes the whole directory or a live server:
94
+
95
+ - uniqueness across a shared namespace
96
+ - mail addresses against the domains an organization owns
97
+ - anything a hook computes rather than validates
98
+
99
+ For those, run the target configuration against a copy of the directory and
100
+ replay the writes.
101
+
102
+ ## See also
103
+
104
+ - [flat-generic](plugins/ldap/flat-generic.md) — where `test` and `hint` live
105
+ - [enterprise-rules](plugins/ldap/enterprise-rules.md) — the rules a dry run covers
@@ -52,12 +52,12 @@ Protection and rate limiting:
52
52
 
53
53
  Connect to external systems:
54
54
 
55
- | Plugin | Description |
56
- | -------------------------------------------------------- | ---------------------------- |
57
- | [james-mail](integrations/james-mail.md) | Apache James synchronization |
58
- | [james-mailboxes](integrations/james-mailboxes.md) | Apache James team mailboxes |
59
- | [calendar-resources](integrations/calendar-resources.md) | Twake calendar resources |
60
- | [app-accounts](integrations/app-accounts.md) | Applicative accounts API |
55
+ | Plugin | Description |
56
+ | -------------------------------------------------- | ---------------------------------- |
57
+ | [james-mail](integrations/james-mail.md) | Apache James synchronization |
58
+ | [james-mailboxes](integrations/james-mailboxes.md) | Apache James team mailboxes |
59
+ | [calendar](integrations/calendar.md) | Twake Calendar resources and users |
60
+ | [app-accounts](integrations/app-accounts.md) | Applicative accounts API |
61
61
 
62
62
  ## Utility Plugins
63
63
 
@@ -14,11 +14,12 @@ LDAP-Rest provides multiple authentication plugins to secure API access. These p
14
14
 
15
15
  ## Authorization Plugins
16
16
 
17
- | Method | Plugin | Description |
18
- | ----------------------------------------------- | -------------------------- | --------------------------- |
19
- | [Authorization Per Branch](authz-per-branch.md) | `core/auth/authzPerBranch` | Branch-level access control |
20
- | [Authorization Per Route](authz-per-route.md) | `core/auth/authzPerRoute` | HTTP route-level ACL |
21
- | [Authorization LinID 1.x](authz-linid1.md) | `core/auth/authzLinid1` | LinID 1.x integration |
17
+ | Method | Plugin | Description |
18
+ | ----------------------------------------------- | -------------------------- | ---------------------------- |
19
+ | [Authorization Per Branch](authz-per-branch.md) | `core/auth/authzPerBranch` | Branch-level access control |
20
+ | [Authorization Per Route](authz-per-route.md) | `core/auth/authzPerRoute` | HTTP route-level ACL |
21
+ | [Authorization LinID 1.x](authz-linid1.md) | `core/auth/authzLinid1` | LinID 1.x integration |
22
+ | [Authorization Scope](authz-scope.md) | `core/auth/authzScope` | What the current user may do |
22
23
 
23
24
  ## Security Plugins
24
25
 
@@ -0,0 +1,103 @@
1
+ # Authorization Scope Plugin
2
+
3
+ Tells a client what the signed-in administrator may actually do, before it
4
+ offers them a button that will fail.
5
+
6
+ In a local-administration model the scope _is_ the interface: a manager
7
+ administers a few branches of the tree and nothing else, and an application
8
+ that cannot name those branches leaves them guessing. This endpoint answers
9
+ both halves — which branches, and which entities can be created — by asking
10
+ whichever authorization plugin is in force.
11
+
12
+ ## Configuration
13
+
14
+ ```bash
15
+ --plugin core/auth/authzScope
16
+ ```
17
+
18
+ It has no options. It answers through whichever loaded plugin carries the
19
+ `authz` role _and_ can say who may do what where — one implementing
20
+ `resolveUser`, `getAuthorizedBranches` and `getUserPermissions`. Both
21
+ [authz-linid1](authz-linid1.md) and [authz-per-branch](authz-per-branch.md)
22
+ do, and this endpoint works with either without knowing which.
23
+
24
+ The role alone is not enough. [authz-per-route](authz-per-route.md) gates URLs
25
+ and [authz-dynamic](authz-dynamic.md) reads a token: both carry the `authz`
26
+ role and implement none of those three methods, so this endpoint passes them
27
+ over — loading one beside a branch-level plugin changes the answer not at all.
28
+ A server whose only authorization plugin is one of those has nothing to
29
+ describe a scope with, and the answer is the unrestricted one shown below: a
30
+ client built on it shows every button, and a refusal, when it comes, comes from
31
+ the route or the token rather than from the scope.
32
+
33
+ ## Endpoint
34
+
35
+ ```
36
+ GET /api/v1/authz/scope
37
+ ```
38
+
39
+ ```json
40
+ {
41
+ "user": "uid=alice,ou=users,dc=example,dc=com",
42
+ "unrestricted": false,
43
+ "branches": [
44
+ {
45
+ "dn": "ou=Sales,ou=organization,dc=example,dc=com",
46
+ "name": "Sales",
47
+ "path": "Acme / Sales",
48
+ "read": true,
49
+ "write": true,
50
+ "delete": true
51
+ }
52
+ ],
53
+ "entities": [
54
+ { "name": "users", "base": "ou=users,dc=example,dc=com", "create": true },
55
+ { "name": "groups", "base": "ou=groups,dc=example,dc=com", "create": true },
56
+ {
57
+ "name": "organizations",
58
+ "base": "ou=organization,dc=example,dc=com",
59
+ "create": false
60
+ }
61
+ ]
62
+ }
63
+ ```
64
+
65
+ `name` and `path` come from the branch entry itself, so an interface can show
66
+ the scope in the directory's own words rather than as a raw DN.
67
+
68
+ `create` answers "may I add one of these?", on the rule the add hook enforces.
69
+ An entry that can carry the organization link (its schema declares the
70
+ configured link attribute) is scoped by the organization it is attached to, so
71
+ the answer is yes as soon as the caller may write in a branch they administer.
72
+ One that cannot — a position, a nomenclature row — is checked against the
73
+ branch it lands in, so the answer is whether the caller may write there.
74
+ Organizations are the other exception, since they live in the tree itself and
75
+ need write permission on the node they hang from — the top of the tree, which
76
+ is where a new organization goes when the client names no parent. A local administrator of one branch therefore reads
77
+ `create: false` for organizations, and creates sub-organizations under their own
78
+ node by naming it as `parentDn`.
79
+
80
+ With no authorization plugin loaded the server grants everything, and the
81
+ answer says so:
82
+
83
+ ```json
84
+ {
85
+ "user": "alice",
86
+ "unrestricted": true,
87
+ "branches": [],
88
+ "entities": [
89
+ { "name": "users", "base": "ou=users,dc=example,dc=com", "create": true }
90
+ ]
91
+ }
92
+ ```
93
+
94
+ ## Errors
95
+
96
+ | Status | When |
97
+ | ------ | ------------------------------------------------------------- |
98
+ | `401` | No authenticated user, or a user the directory cannot resolve |
99
+
100
+ ## See also
101
+
102
+ - [authz-linid1](authz-linid1.md) — the local-administrator model this serves
103
+ - [authz-per-branch](authz-per-branch.md) — static per-branch permissions
@@ -21,6 +21,23 @@ DM_LLNG_INI="/etc/lemonldap-ng/lemonldap-ng.ini"
21
21
  2. **LLNG Configuration**: Valid `lemonldap-ng.ini` file
22
22
  3. **Virtual Host Configuration**: LDAP-Rest must be configured as a protected application in LLNG
23
23
 
24
+ The handler runs inside LDAP-Rest and reads `lemonldap-ng.ini` when the server
25
+ starts. The file tells it where the LLNG configuration is, and which virtual
26
+ hosts it protects — a Node.js handler serves only those listed in
27
+ `nodeVhosts`:
28
+
29
+ ```ini
30
+ [configuration]
31
+ type = REST
32
+ baseConfigUrl = https://auth.example.com/index.fcgi/config
33
+
34
+ [node-handler]
35
+ nodeVhosts = api.example.com
36
+ ```
37
+
38
+ A file that cannot be read, a configuration that cannot be reached or a
39
+ missing `nodeVhosts` stops the server at startup, with the reason.
40
+
24
41
  ## How It Works
25
42
 
26
43
  1. Uses the LemonLDAP::NG Handler to validate requests
@@ -11,10 +11,10 @@ Plugins for connecting LDAP-Rest to external systems.
11
11
 
12
12
  ## Twake
13
13
 
14
- | Plugin | Description |
15
- | ------------------------------------------- | ---------------------------------------- |
16
- | [calendar-resources](calendar-resources.md) | Calendar resource synchronization |
17
- | [app-accounts](app-accounts.md) | Applicative accounts API (devices, apps) |
14
+ | Plugin | Description |
15
+ | ------------------------------- | ---------------------------------------- |
16
+ | [calendar](calendar.md) | Calendar resources and users sync |
17
+ | [app-accounts](app-accounts.md) | Applicative accounts API (devices, apps) |
18
18
 
19
19
  ## Prerequisites
20
20
 
@@ -37,6 +37,6 @@ James plugins require:
37
37
  core/twake/james
38
38
  └─ requires: core/ldap/onChange
39
39
 
40
- core/twake/calendarResources
40
+ core/twake/calendar
41
41
  └─ requires: core/ldap/onChange
42
42
  ```
@@ -1,6 +1,13 @@
1
- # Twake Calendar Resources Plugin
1
+ # Twake Calendar Plugin
2
2
 
3
- Plugin to automatically synchronize LDAP resources (meeting rooms, equipment, etc.) with Twake Calendar via its WebAdmin API.
3
+ Plugin to keep Twake Calendar in sync with LDAP via its WebAdmin API:
4
+
5
+ - resources (meeting rooms, equipment, etc.) stored in an LDAP branch
6
+ - registered users, whose email, first name and last name follow LDAP
7
+
8
+ > Formerly `core/twake/calendarResources`. That name still loads the plugin,
9
+ > registered as `calendarResources`, but logs a deprecation warning and will
10
+ > be removed in a future major release.
4
11
 
5
12
  ## Features
6
13
 
@@ -8,6 +15,7 @@ Plugin to automatically synchronize LDAP resources (meeting rooms, equipment, et
8
15
  - **Automatic Updates**: When a resource is modified in LDAP, changes are synced to Twake Calendar
9
16
  - **Automatic Deletion**: When a resource is removed from LDAP, it's deleted from Twake Calendar
10
17
  - **Flexible Configuration**: Configure which LDAP branch and objectClass to monitor
18
+ - **User Identity Propagation**: When a user's email, first name or last name changes in LDAP, the matching Calendar registered user is updated
11
19
 
12
20
  ## Configuration
13
21
 
@@ -15,10 +23,15 @@ Plugin to automatically synchronize LDAP resources (meeting rooms, equipment, et
15
23
 
16
24
  - `DM_CALENDAR_WEBADMIN_URL`: URL of the Twake Calendar WebAdmin API (default: `http://localhost:8080`)
17
25
  - `DM_CALENDAR_WEBADMIN_TOKEN`: Bearer token for WebAdmin API authentication
18
- - `DM_CALENDAR_RESOURCE_BASE`: LDAP branch to monitor for resources (e.g., `ou=resources,dc=example,dc=com`)
19
- - `DM_CALENDAR_RESOURCE_OBJECTCLASS`: Optional objectClass filter (only process entries with this objectClass)
26
+ - `DM_CALENDAR_RESOURCE_BASE`: LDAP branch to monitor for resources, as a full DN (e.g., `ou=resources,dc=example,dc=com`). It is compared to an entry's DN part by part, so a partial value such as `ou=resources` names no branch and matches nothing (the plugin warns at startup when the value is not a DN of the directory).
27
+ - `DM_CALENDAR_RESOURCE_OBJECTCLASS`: Optional objectClass filter, applied when a resource is **created** (see [Resource identity](#resource-identity))
20
28
  - `DM_CALENDAR_RESOURCE_CREATOR`: Default creator email for resources (default: `admin@example.com`)
21
29
  - `DM_CALENDAR_RESOURCE_DOMAIN`: Default domain for resources (extracted from DN if not specified)
30
+ - `DM_CALENDAR_FIRSTNAME_ATTRIBUTE`: LDAP attribute holding a user's first name (default: `givenName`)
31
+ - `DM_CALENDAR_LASTNAME_ATTRIBUTE`: LDAP attribute holding a user's last name (default: `sn`)
32
+
33
+ The resource variables are only needed for resource synchronization; user
34
+ identity propagation only needs the WebAdmin URL and token.
22
35
 
23
36
  ### Required Dependencies
24
37
 
@@ -66,6 +79,49 @@ The plugin uses the following WebAdmin API endpoints:
66
79
  - `POST /resources` - Create a new resource
67
80
  - `PATCH /resources/{id}` - Update an existing resource
68
81
  - `DELETE /resources/{id}` - Delete a resource
82
+ - `GET /registeredUsers?email={mail}` - Find the registered user to update
83
+ - `PATCH /registeredUsers?id={id}` - Update a registered user's email, first and last name
84
+ - `POST /users/{mail}?action=deleteData` - Delete a user's data (see `deleteUserData`)
85
+
86
+ The resource id goes into the path percent-encoded, so a `/` or a `#` in it
87
+ cannot build a path naming something else. The user's address is left as it is
88
+ written — `user@example.com`, `@` being legal in a path segment — which is what
89
+ `plugins/twake/james` does with the same WebAdmin; whether the two should
90
+ encode it is issue #175.
91
+
92
+ ### Registered Users
93
+
94
+ A change of the configured mail attribute updates the registered user found by
95
+ its **previous** address; a change of the first or last name attribute updates
96
+ the one found by its current address. In both cases the email, first name and
97
+ last name are re-read from LDAP and sent together. Adding or removing the mail
98
+ attribute, and users not registered in Calendar, are skipped.
99
+
100
+ Calendar lower-cases the address before looking it up, which is also how it
101
+ stores addresses, so a case difference between LDAP and Calendar does not
102
+ matter. The exception is a legacy record whose stored address kept upper case
103
+ (written without Calendar's normalisation, e.g. migrated data): it is not
104
+ found, and its user is logged as not registered. Calendar releases before
105
+ 1.0.0.1 ignore the `email` parameter and answer the full list of registered
106
+ users; the plugin then picks the user from it, which works but costs a full
107
+ listing per change.
108
+
109
+ ### Resource identity
110
+
111
+ A resource is known to Calendar by an `id`, which is the value of its LDAP
112
+ entry's own RDN with the escapes removed: `cn=Meeting Room 1,ou=resources,…`
113
+ gives `Meeting Room 1`, and `o=Room 12,ou=resources,…` gives `Room 12`. The
114
+ creation, the update and the deletion hooks all read it that way, so the three
115
+ name the same resource; an entry whose DN yields no value is not synchronised.
116
+
117
+ Only entries in `DM_CALENDAR_RESOURCE_BASE` are synchronised, on all three
118
+ hooks. `DM_CALENDAR_RESOURCE_OBJECTCLASS` filters creations only: a
119
+ modification carries the attributes that changed, so an untouched objectClass
120
+ is not there to compare, and a deleted entry cannot be read any more. The
121
+ branch is the guard that remains on those two paths.
122
+
123
+ Renaming an entry changes its RDN, hence its id: Calendar is not told, and the
124
+ resource keeps its former id there.
69
125
 
70
126
  ### Resource Data Format
71
127
 
@@ -103,7 +159,7 @@ DM_LDAP_FLAT_SCHEMA="/path/to/calendar-resources-schema.json"
103
159
  3. Add the plugin to `DM_PLUGINS`:
104
160
 
105
161
  ```bash
106
- DM_PLUGINS="core/ldap/onChange,core/ldap/flatGeneric,twake/calendarResources"
162
+ DM_PLUGINS="core/ldap/onChange,core/ldap/flatGeneric,twake/calendar"
107
163
  ```
108
164
 
109
165
  4. Start ldap-rest - resources will be automatically synced
@@ -121,7 +177,7 @@ Example log:
121
177
 
122
178
  ```json
123
179
  {
124
- "plugin": "calendarResources",
180
+ "plugin": "calendar",
125
181
  "event": "ldapcalendarResourceadddone",
126
182
  "result": "success",
127
183
  "http_status": 201,
@@ -138,6 +194,7 @@ Example log:
138
194
  2. Verify that `DM_CALENDAR_WEBADMIN_TOKEN` is valid
139
195
  3. Check logs for API errors
140
196
  4. Ensure the ldapFlat schema has `entity.name` set to `calendarResource`
197
+ 5. Ensure `DM_CALENDAR_RESOURCE_BASE` is the full DN of the branch the entries are in: a partial value matches no entry, and the plugin says so at startup
141
198
 
142
199
  ### Authentication errors
143
200
 
@@ -145,22 +202,21 @@ Ensure `DM_CALENDAR_WEBADMIN_TOKEN` is set and valid. The token is sent as a Bea
145
202
 
146
203
  ### Wrong resources being synced
147
204
 
148
- Use `DM_CALENDAR_RESOURCE_BASE` and `DM_CALENDAR_RESOURCE_OBJECTCLASS` to filter which LDAP entries are considered resources.
205
+ Use `DM_CALENDAR_RESOURCE_BASE` and `DM_CALENDAR_RESOURCE_OBJECTCLASS` to filter which LDAP entries are considered resources. Note that the objectClass filter only applies to creations, so an entry of the resource entity that must never reach Calendar has to sit outside the branch.
149
206
 
150
207
  ## Public Methods
151
208
 
152
- Public methods available on the CalendarResources plugin instance.
209
+ Public methods available on the Calendar plugin instance.
153
210
 
154
211
  ### Usage
155
212
 
156
213
  ```typescript
157
214
  import type { DM } from 'ldap-rest';
158
- import type CalendarResources from 'ldap-rest/plugin-twake-calendarresources';
215
+ import type Calendar from 'ldap-rest/plugin-twake-calendar';
159
216
 
160
217
  // Get the plugin instance from another plugin
161
- const calendar = this.server.getPlugin(
162
- 'calendarResources'
163
- ) as CalendarResources;
218
+ // (declare `dependencies = { calendar: 'core/twake/calendar' }` so it loads first)
219
+ const calendar = this.requirePlugin<Calendar>('calendar');
164
220
  ```
165
221
 
166
222
  ### deleteUserData(mail)
@@ -182,7 +238,8 @@ async deleteUserData(mail: string): Promise<{ taskId: string } | null>
182
238
  **Example:**
183
239
 
184
240
  ```typescript
185
- const result = await calendar.deleteUserData('user@example.com');
241
+ // requirePlugin() returns null when the plugin is not loaded
242
+ const result = await calendar?.deleteUserData('user@example.com');
186
243
  if (result) {
187
244
  console.log(`Deletion task started: ${result.taskId}`);
188
245
  }