ldap-rest 0.0.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/LICENSE +661 -0
- package/README.md +48 -0
- package/bin/cleanup-external-users.mjs +219 -0
- package/bin/index.mjs +54 -0
- package/bin/sync-james.mjs +190 -0
- package/dist/abstract/ldapFlat.js +1 -0
- package/dist/abstract/plugin.js +1 -0
- package/dist/abstract/twakePlugin.js +1 -0
- package/dist/bin/index.js +1 -0
- package/dist/config/args.js +1 -0
- package/dist/lib/auth/base.js +1 -0
- package/dist/lib/authz/base.js +1 -0
- package/dist/lib/expressFormatedResponses.js +1 -0
- package/dist/lib/ldapActions.js +1 -0
- package/dist/lib/parseConfig.js +1 -0
- package/dist/lib/utils.js +1 -0
- package/dist/logger/winston.js +1 -0
- package/dist/plugins/auth/authzLinid1.js +1 -0
- package/dist/plugins/auth/authzPerBranch.js +1 -0
- package/dist/plugins/auth/crowdsec.js +1 -0
- package/dist/plugins/auth/llng.js +1 -0
- package/dist/plugins/auth/openidconnect.js +1 -0
- package/dist/plugins/auth/rateLimit.js +1 -0
- package/dist/plugins/auth/token.js +1 -0
- package/dist/plugins/configApi.js +1 -0
- package/dist/plugins/demo/helloworld.js +1 -0
- package/dist/plugins/ldap/bulkImport.js +1 -0
- package/dist/plugins/ldap/departmentSync.js +1 -0
- package/dist/plugins/ldap/externalUsersInGroups.js +1 -0
- package/dist/plugins/ldap/flatGeneric.js +1 -0
- package/dist/plugins/ldap/groups.js +1 -0
- package/dist/plugins/ldap/onChange.js +1 -0
- package/dist/plugins/ldap/organization.js +1 -0
- package/dist/plugins/ldap/trash.js +1 -0
- package/dist/plugins/priority.json.js +1 -0
- package/dist/plugins/static.js +1 -0
- package/dist/plugins/twake/calendarResources.js +1 -0
- package/dist/plugins/twake/james.js +1 -0
- package/dist/plugins/weblogs.js +1 -0
- package/dist/src/abstract/ldapFlat.d.ts +94 -0
- package/dist/src/abstract/plugin.d.ts +43 -0
- package/dist/src/abstract/twakePlugin.d.ts +90 -0
- package/dist/src/bin/index.d.ts +43 -0
- package/dist/src/browser/ldap-group-editor/LdapGroupEditor.d.ts +31 -0
- package/dist/src/browser/ldap-group-editor/api/GroupApiClient.d.ts +23 -0
- package/dist/src/browser/ldap-group-editor/components/CreateGroupModal.d.ts +19 -0
- package/dist/src/browser/ldap-group-editor/components/GroupPropertyEditor.d.ts +30 -0
- package/dist/src/browser/ldap-group-editor/components/GroupTree.d.ts +17 -0
- package/dist/src/browser/ldap-group-editor/components/MoveGroupModal.d.ts +20 -0
- package/dist/src/browser/ldap-group-editor/index.d.ts +6 -0
- package/dist/src/browser/ldap-group-editor/types.d.ts +58 -0
- package/dist/src/browser/ldap-resource-editor/api/ResourceApiClient.d.ts +53 -0
- package/dist/src/browser/ldap-resource-editor/cache/CacheManager.d.ts +65 -0
- package/dist/src/browser/ldap-resource-editor/index.d.ts +6 -0
- package/dist/src/browser/ldap-resource-editor/types.d.ts +78 -0
- package/dist/src/browser/ldap-tree-viewer/LdapTreeViewer.d.ts +26 -0
- package/dist/src/browser/ldap-tree-viewer/api/LdapApiClient.d.ts +30 -0
- package/dist/src/browser/ldap-tree-viewer/components/TreeNode.d.ts +17 -0
- package/dist/src/browser/ldap-tree-viewer/components/TreeRoot.d.ts +14 -0
- package/dist/src/browser/ldap-tree-viewer/index.d.ts +7 -0
- package/dist/src/browser/ldap-tree-viewer/store/Store.d.ts +18 -0
- package/dist/src/browser/ldap-tree-viewer/store/actions.d.ts +28 -0
- package/dist/src/browser/ldap-tree-viewer/store/reducers.d.ts +7 -0
- package/dist/src/browser/ldap-tree-viewer/types.d.ts +34 -0
- package/dist/src/browser/ldap-unit-editor/LdapUnitEditor.d.ts +27 -0
- package/dist/src/browser/ldap-unit-editor/api/UnitApiClient.d.ts +24 -0
- package/dist/src/browser/ldap-unit-editor/components/MoveUnitModal.d.ts +28 -0
- package/dist/src/browser/ldap-unit-editor/components/UnitPropertyEditor.d.ts +28 -0
- package/dist/src/browser/ldap-unit-editor/components/UnitTree.d.ts +18 -0
- package/dist/src/browser/ldap-unit-editor/index.d.ts +6 -0
- package/dist/src/browser/ldap-unit-editor/types.d.ts +55 -0
- package/dist/src/browser/ldap-user-editor/LdapUserEditor.d.ts +32 -0
- package/dist/src/browser/ldap-user-editor/api/UserApiClient.d.ts +55 -0
- package/dist/src/browser/ldap-user-editor/cache/CacheManager.d.ts +65 -0
- package/dist/src/browser/ldap-user-editor/components/MoveUserModal.d.ts +28 -0
- package/dist/src/browser/ldap-user-editor/components/PointerField.d.ts +22 -0
- package/dist/src/browser/ldap-user-editor/components/UserEditor.d.ts +46 -0
- package/dist/src/browser/ldap-user-editor/components/UserList.d.ts +24 -0
- package/dist/src/browser/ldap-user-editor/components/UserTree.d.ts +24 -0
- package/dist/src/browser/ldap-user-editor/index.d.ts +7 -0
- package/dist/src/browser/ldap-user-editor/types.d.ts +67 -0
- package/dist/src/browser/shared/components/Modal.d.ts +38 -0
- package/dist/src/browser/shared/components/StatusMessage.d.ts +48 -0
- package/dist/src/browser/shared/index.d.ts +10 -0
- package/dist/src/browser/shared/types/index.d.ts +65 -0
- package/dist/src/browser/shared/utils/dom.d.ts +33 -0
- package/dist/src/browser/shared/utils/form.d.ts +28 -0
- package/dist/src/browser/shared/utils/schema.d.ts +29 -0
- package/dist/src/config/args.d.ts +128 -0
- package/dist/src/config/schema.d.ts +18 -0
- package/dist/src/hooks.d.ts +76 -0
- package/dist/src/lib/auth/base.d.ts +9 -0
- package/dist/src/lib/authz/base.d.ts +63 -0
- package/dist/src/lib/expressFormatedResponses.d.ts +26 -0
- package/dist/src/lib/ldapActions.d.ts +91 -0
- package/dist/src/lib/parseConfig.d.ts +23 -0
- package/dist/src/lib/utils.d.ts +73 -0
- package/dist/src/logger/winston.d.ts +3 -0
- package/dist/src/plugins/auth/authzLinid1.d.ts +46 -0
- package/dist/src/plugins/auth/authzPerBranch.d.ts +59 -0
- package/dist/src/plugins/auth/crowdsec.d.ts +29 -0
- package/dist/src/plugins/auth/llng.d.ts +8 -0
- package/dist/src/plugins/auth/openidconnect.d.ts +11 -0
- package/dist/src/plugins/auth/rateLimit.d.ts +24 -0
- package/dist/src/plugins/auth/token.d.ts +17 -0
- package/dist/src/plugins/configApi.d.ts +24 -0
- package/dist/src/plugins/demo/helloworld.d.ts +12 -0
- package/dist/src/plugins/ldap/bulkImport.d.ts +48 -0
- package/dist/src/plugins/ldap/departmentSync.d.ts +35 -0
- package/dist/src/plugins/ldap/externalUsersInGroups.d.ts +21 -0
- package/dist/src/plugins/ldap/flatGeneric.d.ts +31 -0
- package/dist/src/plugins/ldap/groups.d.ts +95 -0
- package/dist/src/plugins/ldap/onChange.d.ts +32 -0
- package/dist/src/plugins/ldap/organization.d.ts +65 -0
- package/dist/src/plugins/ldap/trash.d.ts +52 -0
- package/dist/src/plugins/static.d.ts +7 -0
- package/dist/src/plugins/twake/calendarResources.d.ts +31 -0
- package/dist/src/plugins/twake/james.d.ts +83 -0
- package/dist/src/plugins/weblogs.d.ts +8 -0
- package/docs/DEVELOPER_GUIDE.md +298 -0
- package/docs/OPENAPI.md +440 -0
- package/docs/PLUGIN_DEPENDENCIES.md +612 -0
- package/docs/README.md +466 -0
- package/docs/api/REFERENCE.md +983 -0
- package/docs/api/REST_API.md +1237 -0
- package/docs/authentication.md +469 -0
- package/docs/authzLinid1.md +521 -0
- package/docs/authzPerBranch.md +596 -0
- package/docs/browser/LIBRARIES.md +1196 -0
- package/docs/examples/EXAMPLES.md +1659 -0
- package/docs/examples/twake-mailbox-type-nomenclature.ldif +37 -0
- package/docs/examples/twake-mailbox-type-schema-amendment.ldif +30 -0
- package/docs/jamesTeamMailboxes.md +343 -0
- package/docs/ldapBulkImport.md +722 -0
- package/docs/ldapExternalUsersInGroups.md +325 -0
- package/docs/ldapFlatGeneric.md +444 -0
- package/docs/ldapGroups.md +589 -0
- package/docs/ldapOrganizations.md +659 -0
- package/docs/ldapTrash.md +260 -0
- package/docs/linagora.png +0 -0
- package/docs/onChange.md +318 -0
- package/docs/plugins/DEVELOPMENT.md +1219 -0
- package/docs/presenterm-fr.md +721 -0
- package/docs/presenterm.md +721 -0
- package/docs/schemas/SCHEMAS.md +1212 -0
- package/docs/static.md +410 -0
- package/docs/twakeCalendarResources.md +148 -0
- package/docs/twakeJames.md +661 -0
- package/docs/weblogs.md +399 -0
- package/examples/web/ldap-group-editor.html +113 -0
- package/examples/web/ldap-tree-viewer.html +368 -0
- package/examples/web/ldap-unit-editor.html +108 -0
- package/examples/web/ldap-user-editor.html +412 -0
- package/package.json +458 -0
- package/static/browser/common-demo.css +450 -0
- package/static/browser/ldap-group-editor/LdapGroupEditor.js +207 -0
- package/static/browser/ldap-group-editor/LdapGroupEditor.js.map +1 -0
- package/static/browser/ldap-group-editor/api/GroupApiClient.js +112 -0
- package/static/browser/ldap-group-editor/api/GroupApiClient.js.map +1 -0
- package/static/browser/ldap-group-editor/components/CreateGroupModal.js +212 -0
- package/static/browser/ldap-group-editor/components/CreateGroupModal.js.map +1 -0
- package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js +497 -0
- package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js.map +1 -0
- package/static/browser/ldap-group-editor/components/GroupTree.js +153 -0
- package/static/browser/ldap-group-editor/components/GroupTree.js.map +1 -0
- package/static/browser/ldap-group-editor/components/MoveGroupModal.js +235 -0
- package/static/browser/ldap-group-editor/components/MoveGroupModal.js.map +1 -0
- package/static/browser/ldap-group-editor/index.js +2 -0
- package/static/browser/ldap-group-editor/index.js.map +1 -0
- package/static/browser/ldap-group-editor/types.js +2 -0
- package/static/browser/ldap-group-editor/types.js.map +1 -0
- package/static/browser/ldap-group-editor.esm.js +1399 -0
- package/static/browser/ldap-group-editor.esm.js.map +1 -0
- package/static/browser/ldap-group-editor.js +1407 -0
- package/static/browser/ldap-group-editor.js.map +1 -0
- package/static/browser/ldap-group-editor.min.js +2 -0
- package/static/browser/ldap-group-editor.min.js.map +1 -0
- package/static/browser/ldap-resource-editor/api/ResourceApiClient.js +226 -0
- package/static/browser/ldap-resource-editor/api/ResourceApiClient.js.map +1 -0
- package/static/browser/ldap-resource-editor/cache/CacheManager.js +123 -0
- package/static/browser/ldap-resource-editor/cache/CacheManager.js.map +1 -0
- package/static/browser/ldap-resource-editor/index.js +2 -0
- package/static/browser/ldap-resource-editor/index.js.map +1 -0
- package/static/browser/ldap-resource-editor/types.js +2 -0
- package/static/browser/ldap-resource-editor/types.js.map +1 -0
- package/static/browser/ldap-tree-viewer/LdapTreeViewer.js +187 -0
- package/static/browser/ldap-tree-viewer/LdapTreeViewer.js.map +1 -0
- package/static/browser/ldap-tree-viewer/api/LdapApiClient.js +50 -0
- package/static/browser/ldap-tree-viewer/api/LdapApiClient.js.map +1 -0
- package/static/browser/ldap-tree-viewer/components/TreeNode.js +140 -0
- package/static/browser/ldap-tree-viewer/components/TreeNode.js.map +1 -0
- package/static/browser/ldap-tree-viewer/components/TreeRoot.js +98 -0
- package/static/browser/ldap-tree-viewer/components/TreeRoot.js.map +1 -0
- package/static/browser/ldap-tree-viewer/index.js +2 -0
- package/static/browser/ldap-tree-viewer/index.js.map +1 -0
- package/static/browser/ldap-tree-viewer/store/Store.js +29 -0
- package/static/browser/ldap-tree-viewer/store/Store.js.map +1 -0
- package/static/browser/ldap-tree-viewer/store/actions.js +53 -0
- package/static/browser/ldap-tree-viewer/store/actions.js.map +1 -0
- package/static/browser/ldap-tree-viewer/store/reducers.js +79 -0
- package/static/browser/ldap-tree-viewer/store/reducers.js.map +1 -0
- package/static/browser/ldap-tree-viewer/types.js +2 -0
- package/static/browser/ldap-tree-viewer/types.js.map +1 -0
- package/static/browser/ldap-tree-viewer.css +2 -0
- package/static/browser/ldap-tree-viewer.css.map +1 -0
- package/static/browser/ldap-tree-viewer.esm.js +606 -0
- package/static/browser/ldap-tree-viewer.esm.js.map +1 -0
- package/static/browser/ldap-tree-viewer.js +614 -0
- package/static/browser/ldap-tree-viewer.js.map +1 -0
- package/static/browser/ldap-tree-viewer.min.js +2 -0
- package/static/browser/ldap-tree-viewer.min.js.map +1 -0
- package/static/browser/ldap-unit-editor/LdapUnitEditor.js +119 -0
- package/static/browser/ldap-unit-editor/LdapUnitEditor.js.map +1 -0
- package/static/browser/ldap-unit-editor/api/UnitApiClient.js +95 -0
- package/static/browser/ldap-unit-editor/api/UnitApiClient.js.map +1 -0
- package/static/browser/ldap-unit-editor/components/MoveUnitModal.js +295 -0
- package/static/browser/ldap-unit-editor/components/MoveUnitModal.js.map +1 -0
- package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js +321 -0
- package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js.map +1 -0
- package/static/browser/ldap-unit-editor/components/UnitTree.js +197 -0
- package/static/browser/ldap-unit-editor/components/UnitTree.js.map +1 -0
- package/static/browser/ldap-unit-editor/index.js +2 -0
- package/static/browser/ldap-unit-editor/index.js.map +1 -0
- package/static/browser/ldap-unit-editor/types.js +2 -0
- package/static/browser/ldap-unit-editor/types.js.map +1 -0
- package/static/browser/ldap-unit-editor.esm.js +1013 -0
- package/static/browser/ldap-unit-editor.esm.js.map +1 -0
- package/static/browser/ldap-unit-editor.js +1021 -0
- package/static/browser/ldap-unit-editor.js.map +1 -0
- package/static/browser/ldap-unit-editor.min.js +2 -0
- package/static/browser/ldap-unit-editor.min.js.map +1 -0
- package/static/browser/ldap-user-editor/LdapUserEditor.js +184 -0
- package/static/browser/ldap-user-editor/LdapUserEditor.js.map +1 -0
- package/static/browser/ldap-user-editor/api/UserApiClient.js +209 -0
- package/static/browser/ldap-user-editor/api/UserApiClient.js.map +1 -0
- package/static/browser/ldap-user-editor/cache/CacheManager.js +123 -0
- package/static/browser/ldap-user-editor/cache/CacheManager.js.map +1 -0
- package/static/browser/ldap-user-editor/components/MoveUserModal.js +302 -0
- package/static/browser/ldap-user-editor/components/MoveUserModal.js.map +1 -0
- package/static/browser/ldap-user-editor/components/PointerField.js +152 -0
- package/static/browser/ldap-user-editor/components/PointerField.js.map +1 -0
- package/static/browser/ldap-user-editor/components/UserEditor.js +608 -0
- package/static/browser/ldap-user-editor/components/UserEditor.js.map +1 -0
- package/static/browser/ldap-user-editor/components/UserList.js +181 -0
- package/static/browser/ldap-user-editor/components/UserList.js.map +1 -0
- package/static/browser/ldap-user-editor/components/UserTree.js +164 -0
- package/static/browser/ldap-user-editor/components/UserTree.js.map +1 -0
- package/static/browser/ldap-user-editor/index.js +2 -0
- package/static/browser/ldap-user-editor/index.js.map +1 -0
- package/static/browser/ldap-user-editor/types.js +2 -0
- package/static/browser/ldap-user-editor/types.js.map +1 -0
- package/static/browser/ldap-user-editor.css +2 -0
- package/static/browser/ldap-user-editor.css.map +1 -0
- package/static/browser/ldap-user-editor.esm.js +1897 -0
- package/static/browser/ldap-user-editor.esm.js.map +1 -0
- package/static/browser/ldap-user-editor.js +1905 -0
- package/static/browser/ldap-user-editor.js.map +1 -0
- package/static/browser/ldap-user-editor.min.js +2 -0
- package/static/browser/ldap-user-editor.min.js.map +1 -0
- package/static/browser/shared/components/Modal.js +121 -0
- package/static/browser/shared/components/Modal.js.map +1 -0
- package/static/browser/shared/components/StatusMessage.js +119 -0
- package/static/browser/shared/components/StatusMessage.js.map +1 -0
- package/static/browser/shared/index.js +7 -0
- package/static/browser/shared/index.js.map +1 -0
- package/static/browser/shared/types/index.js +37 -0
- package/static/browser/shared/types/index.js.map +1 -0
- package/static/browser/shared/utils/dom.js +78 -0
- package/static/browser/shared/utils/dom.js.map +1 -0
- package/static/browser/shared/utils/form.js +234 -0
- package/static/browser/shared/utils/form.js.map +1 -0
- package/static/browser/shared/utils/schema.js +121 -0
- package/static/browser/shared/utils/schema.js.map +1 -0
- package/static/browser/shared.esm.js +694 -0
- package/static/browser/shared.esm.js.map +1 -0
- package/static/browser/shared.js +723 -0
- package/static/browser/shared.js.map +1 -0
- package/static/browser/shared.min.js +2 -0
- package/static/browser/shared.min.js.map +1 -0
- package/static/schemas/ad/computers.json +92 -0
- package/static/schemas/ad/groups.json +55 -0
- package/static/schemas/ad/organizations.json +66 -0
- package/static/schemas/ad/users.json +116 -0
- package/static/schemas/standard/devices.json +72 -0
- package/static/schemas/standard/groups.json +54 -0
- package/static/schemas/standard/organizations.json +49 -0
- package/static/schemas/standard/users.json +86 -0
- package/static/schemas/twake/groups.json +75 -0
- package/static/schemas/twake/nomenclature/twakeAccountStatus.json +31 -0
- package/static/schemas/twake/nomenclature/twakeDeliveryMode.json +31 -0
- package/static/schemas/twake/nomenclature/twakeListType.json +31 -0
- package/static/schemas/twake/nomenclature/twakeMailboxType.json +31 -0
- package/static/schemas/twake/nomenclature/twakeTitle.json +31 -0
- package/static/schemas/twake/organizations.json +52 -0
- package/static/schemas/twake/positions.json +32 -0
- package/static/schemas/twake/users.json +112 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# LDAP Trash Plugin
|
|
2
|
+
|
|
3
|
+
Plugin to intercept LDAP delete operations and move entries to a trash branch instead of permanently deleting them.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Soft Delete**: Moves entries to trash instead of permanent deletion
|
|
8
|
+
- **Configurable Branches**: Watch specific LDAP branches or all branches
|
|
9
|
+
- **Metadata Tracking**: Automatically adds deletion timestamp and original DN
|
|
10
|
+
- **Auto-create Trash**: Creates trash branch automatically if it doesn't exist
|
|
11
|
+
- **Atomic Move**: Uses LDAP modifyDN for safe, atomic operations
|
|
12
|
+
- **Overwrite Protection**: Automatically removes old trash entries when moving new ones
|
|
13
|
+
|
|
14
|
+
## Configuration
|
|
15
|
+
|
|
16
|
+
### Environment Variables
|
|
17
|
+
|
|
18
|
+
- `DM_TRASH_BASE`: LDAP base DN for trash (default: `ou=trash,dc=example,dc=com`)
|
|
19
|
+
- `DM_TRASH_WATCHED_BASES`: Comma-separated list of branches to watch (default: all branches except trash)
|
|
20
|
+
- `DM_TRASH_ADD_METADATA`: Add metadata to trashed entries (default: `true`)
|
|
21
|
+
- `DM_TRASH_AUTO_CREATE`: Auto-create trash branch if missing (default: `true`)
|
|
22
|
+
|
|
23
|
+
### Command Line
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
--plugin core/ldap/trash \
|
|
27
|
+
--trash-base "ou=trash,dc=example,dc=com" \
|
|
28
|
+
--trash-watched-bases "ou=users,dc=example,dc=com,ou=groups,dc=example,dc=com" \
|
|
29
|
+
--trash-add-metadata true \
|
|
30
|
+
--trash-auto-create true
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## How It Works
|
|
34
|
+
|
|
35
|
+
When a delete operation occurs:
|
|
36
|
+
|
|
37
|
+
1. **Check if DN is watched**: Plugin checks if the DN is in a configured watched branch
|
|
38
|
+
2. **Ensure trash exists**: Creates trash branch if needed (when `auto-create` is enabled)
|
|
39
|
+
3. **Remove old trash entry**: If an entry with the same RDN exists in trash, it's deleted first
|
|
40
|
+
4. **Atomic move**: Uses LDAP `modifyDN` to move the entry to trash
|
|
41
|
+
5. **Add metadata**: Adds deletion timestamp and original DN as `description` attribute
|
|
42
|
+
6. **Cancel native delete**: The original delete operation is cancelled
|
|
43
|
+
|
|
44
|
+
### DN Transformation
|
|
45
|
+
|
|
46
|
+
Original DN is transformed to trash DN by replacing the parent branch:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Before: uid=john,ou=users,dc=example,dc=com
|
|
50
|
+
After: uid=john,ou=trash,dc=example,dc=com
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Usage Examples
|
|
54
|
+
|
|
55
|
+
### Watch All Branches (Default)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
ldap-rest \
|
|
59
|
+
--plugin core/ldap/trash \
|
|
60
|
+
--trash-base "ou=trash,dc=example,dc=com"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This watches all LDAP branches except the trash branch itself.
|
|
64
|
+
|
|
65
|
+
### Watch Specific Branches Only
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
ldap-rest \
|
|
69
|
+
--plugin core/ldap/trash \
|
|
70
|
+
--trash-base "ou=trash,dc=example,dc=com" \
|
|
71
|
+
--trash-watched-bases "ou=users,dc=example,dc=com,ou=groups,dc=example,dc=com"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This only intercepts deletes from `ou=users` and `ou=groups`. Deletes from other branches proceed normally.
|
|
75
|
+
|
|
76
|
+
### Disable Metadata
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
ldap-rest \
|
|
80
|
+
--plugin core/ldap/trash \
|
|
81
|
+
--trash-base "ou=trash,dc=example,dc=com" \
|
|
82
|
+
--trash-add-metadata false
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Entries are moved to trash without adding deletion metadata.
|
|
86
|
+
|
|
87
|
+
### Manual Trash Branch
|
|
88
|
+
|
|
89
|
+
If you want to create the trash branch manually:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Create trash branch manually
|
|
93
|
+
ldapadd -x -D "cn=admin,dc=example,dc=com" -W <<EOF
|
|
94
|
+
dn: ou=trash,dc=example,dc=com
|
|
95
|
+
objectClass: top
|
|
96
|
+
objectClass: organizationalUnit
|
|
97
|
+
ou: trash
|
|
98
|
+
description: LDAP Trash - Deleted entries are moved here
|
|
99
|
+
EOF
|
|
100
|
+
|
|
101
|
+
# Configure plugin to not auto-create
|
|
102
|
+
ldap-rest \
|
|
103
|
+
--plugin core/ldap/trash \
|
|
104
|
+
--trash-base "ou=trash,dc=example,dc=com" \
|
|
105
|
+
--trash-auto-create false
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Metadata Format
|
|
109
|
+
|
|
110
|
+
When `trash-add-metadata` is enabled, the plugin adds a `description` attribute to trashed entries:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
description: Deleted on 2025-10-15T10:30:45.123Z from uid=john,ou=users,dc=example,dc=com
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
This helps track:
|
|
117
|
+
|
|
118
|
+
- When the entry was deleted
|
|
119
|
+
- Where it originally existed
|
|
120
|
+
|
|
121
|
+
## Recovering from Trash
|
|
122
|
+
|
|
123
|
+
To restore an entry from trash, manually move it back using LDAP tools:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# List entries in trash
|
|
127
|
+
ldapsearch -x -b "ou=trash,dc=example,dc=com" "(objectClass=*)"
|
|
128
|
+
|
|
129
|
+
# Restore user (move back)
|
|
130
|
+
ldapmodrdn -x -D "cn=admin,dc=example,dc=com" -W \
|
|
131
|
+
"uid=john,ou=trash,dc=example,dc=com" \
|
|
132
|
+
"uid=john" \
|
|
133
|
+
-s "ou=users,dc=example,dc=com"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Or use a script:
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
// Restore from trash
|
|
140
|
+
await dm.ldap.move(
|
|
141
|
+
'uid=john,ou=trash,dc=example,dc=com',
|
|
142
|
+
'uid=john,ou=users,dc=example,dc=com'
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// Clean up metadata (optional)
|
|
146
|
+
await dm.ldap.modify('uid=john,ou=users,dc=example,dc=com', {
|
|
147
|
+
delete: { description: [] },
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Emptying Trash
|
|
152
|
+
|
|
153
|
+
To permanently delete entries from trash:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# Delete specific entry from trash
|
|
157
|
+
ldapdelete -x -D "cn=admin,dc=example,dc=com" -W \
|
|
158
|
+
"uid=john,ou=trash,dc=example,dc=com"
|
|
159
|
+
|
|
160
|
+
# Empty entire trash (careful!)
|
|
161
|
+
ldapsearch -x -b "ou=trash,dc=example,dc=com" -LLL dn | \
|
|
162
|
+
grep '^dn:' | cut -d' ' -f2 | \
|
|
163
|
+
while read dn; do
|
|
164
|
+
if [ "$dn" != "ou=trash,dc=example,dc=com" ]; then
|
|
165
|
+
ldapdelete -x -D "cn=admin,dc=example,dc=com" -W "$dn"
|
|
166
|
+
fi
|
|
167
|
+
done
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Note: Deleting from trash itself is NOT intercepted by the plugin (prevents infinite loops).
|
|
171
|
+
|
|
172
|
+
## LDAP Permissions
|
|
173
|
+
|
|
174
|
+
The plugin requires `modifyDN` permission to move entries. Ensure your LDAP bind DN has sufficient permissions:
|
|
175
|
+
|
|
176
|
+
```ldif
|
|
177
|
+
# Example ACL for OpenLDAP
|
|
178
|
+
olcAccess: to dn.subtree="dc=example,dc=com"
|
|
179
|
+
by dn="cn=admin,dc=example,dc=com" write
|
|
180
|
+
by * read
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
If you get permission errors, check your LDAP ACLs and ensure the bind DN can:
|
|
184
|
+
|
|
185
|
+
- Read entries from watched branches
|
|
186
|
+
- Move entries to trash branch (modifyDN)
|
|
187
|
+
- Modify entries in trash (to add metadata)
|
|
188
|
+
|
|
189
|
+
## Hooks
|
|
190
|
+
|
|
191
|
+
This plugin uses the `ldapdeleterequest` hook to intercept delete operations before they occur.
|
|
192
|
+
|
|
193
|
+
## Limitations
|
|
194
|
+
|
|
195
|
+
- Only works with flat entries (RDN is preserved, parent DN is changed)
|
|
196
|
+
- Entries with the same RDN will overwrite previous trash entries
|
|
197
|
+
- Tree structures (entries with children) must be deleted leaf-first
|
|
198
|
+
- Does not track history (only keeps the latest deleted version)
|
|
199
|
+
|
|
200
|
+
## Troubleshooting
|
|
201
|
+
|
|
202
|
+
### Trash not being created
|
|
203
|
+
|
|
204
|
+
1. Check `auto-create` is enabled (default)
|
|
205
|
+
2. Verify bind DN has permission to create `ou=trash`
|
|
206
|
+
3. Check logs for error messages
|
|
207
|
+
|
|
208
|
+
### Entries not moving to trash
|
|
209
|
+
|
|
210
|
+
1. Check if DN matches `watched-bases` configuration
|
|
211
|
+
2. Verify bind DN has `modifyDN` permission
|
|
212
|
+
3. Check that trash branch exists
|
|
213
|
+
4. Review logs for specific error messages
|
|
214
|
+
|
|
215
|
+
### Permission errors
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
Error: Trash plugin: Insufficient LDAP permissions to move uid=john,ou=users,dc=example,dc=com to trash
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Solution: Grant `modifyDN` permission to the bind DN in LDAP ACLs.
|
|
222
|
+
|
|
223
|
+
### Overwrite warnings
|
|
224
|
+
|
|
225
|
+
If you see messages about removing old trash entries, this is normal behavior. The plugin ensures only one version of each entry exists in trash.
|
|
226
|
+
|
|
227
|
+
## Best Practices
|
|
228
|
+
|
|
229
|
+
1. **Regular cleanup**: Schedule periodic trash cleanup to prevent unlimited growth
|
|
230
|
+
2. **Backup first**: Always backup LDAP before emptying trash
|
|
231
|
+
3. **Monitor trash size**: Track the number of entries in trash
|
|
232
|
+
4. **Document recovery**: Document the recovery process for your team
|
|
233
|
+
5. **Test permissions**: Verify LDAP permissions before deploying to production
|
|
234
|
+
6. **Watch selectively**: Only watch branches that need trash protection
|
|
235
|
+
|
|
236
|
+
## Example Configuration
|
|
237
|
+
|
|
238
|
+
Full production setup:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
ldap-rest \
|
|
242
|
+
--plugin core/auth/token \
|
|
243
|
+
--plugin core/ldap/trash \
|
|
244
|
+
--plugin core/ldap/flatGeneric \
|
|
245
|
+
--plugin core/ldap/groups \
|
|
246
|
+
--ldap-url "ldap://localhost:389" \
|
|
247
|
+
--ldap-dn "cn=admin,dc=example,dc=com" \
|
|
248
|
+
--ldap-pwd "admin" \
|
|
249
|
+
--ldap-base "dc=example,dc=com" \
|
|
250
|
+
--trash-base "ou=trash,dc=example,dc=com" \
|
|
251
|
+
--trash-watched-bases "ou=users,dc=example,dc=com,ou=groups,dc=example,dc=com" \
|
|
252
|
+
--trash-add-metadata true \
|
|
253
|
+
--auth-token "secret-token"
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## License
|
|
257
|
+
|
|
258
|
+
[](https://linagora.com)
|
|
259
|
+
|
|
260
|
+
License: [AGPL-3.0](../LICENSE), copyright 2025-present LINAGORA.
|
|
Binary file
|
package/docs/onChange.md
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# LDAP onChange Plugin
|
|
2
|
+
|
|
3
|
+
Monitor LDAP attribute changes and trigger hooks for specific attributes.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `onChange` plugin watches for LDAP modifications and generates specialized hooks when configured attributes change. This enables reactive integrations with external systems (mail servers, quota managers, etc.).
|
|
8
|
+
|
|
9
|
+
## Configuration
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
--plugin core/ldap/onChange \
|
|
13
|
+
--mail-attribute mail \
|
|
14
|
+
--quota-attribute mailQuota
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Environment Variables:**
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
DM_MAIL_ATTRIBUTE="mail"
|
|
21
|
+
DM_QUOTA_ATTRIBUTE="mailQuota"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Generated Hooks
|
|
25
|
+
|
|
26
|
+
The plugin generates hooks based on configured attributes:
|
|
27
|
+
|
|
28
|
+
| Config Parameter | Generated Hook | Parameters |
|
|
29
|
+
| ------------------- | ------------------- | -------------------------------- |
|
|
30
|
+
| `--mail-attribute` | `onLdapMailChange` | `(dn, oldMail, newMail)` |
|
|
31
|
+
| `--quota-attribute` | `onLdapQuotaChange` | `(dn, mail, oldQuota, newQuota)` |
|
|
32
|
+
|
|
33
|
+
Generic hook for any change:
|
|
34
|
+
|
|
35
|
+
- `onLdapChange` - `(dn, changes)` where `changes` is `Record<attr, [oldValue, newValue]>`
|
|
36
|
+
|
|
37
|
+
## How It Works
|
|
38
|
+
|
|
39
|
+
1. **Hooks into LDAP modify operations** via `ldapUsersmodify` and `ldapGroupsmodify` hooks
|
|
40
|
+
2. **Detects attribute changes** by comparing old and new values
|
|
41
|
+
3. **Triggers specialized hooks** for configured attributes
|
|
42
|
+
4. **Provides old and new values** to downstream hooks
|
|
43
|
+
|
|
44
|
+
## Use Cases
|
|
45
|
+
|
|
46
|
+
### Mail Server Synchronization
|
|
47
|
+
|
|
48
|
+
When user email changes, update external mail server:
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
// In another plugin
|
|
52
|
+
hooks: {
|
|
53
|
+
onLdapMailChange: async (dn, oldMail, newMail) => {
|
|
54
|
+
await mailServer.renameAccount(oldMail, newMail);
|
|
55
|
+
console.log(`Renamed mail account: ${oldMail} → ${newMail}`);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Quota Management
|
|
61
|
+
|
|
62
|
+
When quota changes, update mail server quota:
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
hooks: {
|
|
66
|
+
onLdapQuotaChange: async (dn, mail, oldQuota, newQuota) => {
|
|
67
|
+
await mailServer.setQuota(mail, newQuota);
|
|
68
|
+
console.log(`Updated quota for ${mail}: ${oldQuota} → ${newQuota}`);
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Generic Change Tracking
|
|
74
|
+
|
|
75
|
+
Track all attribute changes:
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
hooks: {
|
|
79
|
+
onLdapChange: async (dn, changes) => {
|
|
80
|
+
for (const [attr, [oldVal, newVal]] of Object.entries(changes)) {
|
|
81
|
+
console.log(`${dn}: ${attr} changed from ${oldVal} to ${newVal}`);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Examples
|
|
88
|
+
|
|
89
|
+
### Example 1: Mail Attribute Monitoring
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
--plugin core/ldap/onChange \
|
|
93
|
+
--plugin core/ldap/flatGeneric \
|
|
94
|
+
--ldap-flat-schema ./schemas/users.json \
|
|
95
|
+
--mail-attribute mail
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
When a user's mail attribute changes via API:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
PUT /api/v1/ldap/users/jdoe
|
|
102
|
+
{
|
|
103
|
+
"replace": {
|
|
104
|
+
"mail": "john.doe@newdomain.com"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The `onLdapMailChange` hook fires with:
|
|
110
|
+
|
|
111
|
+
- `dn`: `"uid=jdoe,ou=users,dc=example,dc=com"`
|
|
112
|
+
- `oldMail`: `"jdoe@olddomain.com"`
|
|
113
|
+
- `newMail`: `"john.doe@newdomain.com"`
|
|
114
|
+
|
|
115
|
+
### Example 2: Multiple Attributes
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
--plugin core/ldap/onChange \
|
|
119
|
+
--mail-attribute mail \
|
|
120
|
+
--quota-attribute mailQuota
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
When both attributes change:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
PUT /api/v1/ldap/users/jdoe
|
|
127
|
+
{
|
|
128
|
+
"replace": {
|
|
129
|
+
"mail": "jdoe@company.com",
|
|
130
|
+
"mailQuota": "5000000000"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Both hooks fire:
|
|
136
|
+
|
|
137
|
+
1. `onLdapMailChange(dn, "old@mail.com", "jdoe@company.com")`
|
|
138
|
+
2. `onLdapQuotaChange(dn, "jdoe@company.com", "1000000000", "5000000000")`
|
|
139
|
+
|
|
140
|
+
### Example 3: Generic Change Listener
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
--plugin core/ldap/onChange
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
All modifications trigger `onLdapChange`:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
PUT /api/v1/ldap/users/jdoe
|
|
150
|
+
{
|
|
151
|
+
"replace": {
|
|
152
|
+
"cn": "John A. Doe",
|
|
153
|
+
"telephoneNumber": "+1234567890"
|
|
154
|
+
},
|
|
155
|
+
"delete": ["description"]
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Hook receives:
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
{
|
|
163
|
+
"cn": ["John Doe", "John A. Doe"],
|
|
164
|
+
"telephoneNumber": [null, "+1234567890"],
|
|
165
|
+
"description": ["Old description", null]
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Integration Examples
|
|
170
|
+
|
|
171
|
+
### With Twake James Mail Server
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
--plugin core/ldap/onChange \
|
|
175
|
+
--plugin twake/james \
|
|
176
|
+
--plugin core/ldap/flatGeneric \
|
|
177
|
+
--ldap-flat-schema ./schemas/twake/users.json \
|
|
178
|
+
--mail-attribute mail \
|
|
179
|
+
--quota-attribute mailQuota \
|
|
180
|
+
--james-webadmin-url http://james:8000
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The James plugin listens to `onLdapMailChange` and `onLdapQuotaChange` hooks and automatically updates the James mail server.
|
|
184
|
+
|
|
185
|
+
### Custom Integration Plugin
|
|
186
|
+
|
|
187
|
+
Create a custom plugin that reacts to changes:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import DmPlugin from '../abstract/plugin';
|
|
191
|
+
import { Hooks } from '../hooks';
|
|
192
|
+
|
|
193
|
+
export default class CustomSync extends DmPlugin {
|
|
194
|
+
name = 'customSync';
|
|
195
|
+
|
|
196
|
+
dependencies = {
|
|
197
|
+
onChange: 'core/ldap/onChange',
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
hooks: Hooks = {
|
|
201
|
+
onLdapMailChange: async (dn, oldMail, newMail) => {
|
|
202
|
+
this.logger.info(`Mail changed: ${oldMail} → ${newMail}`);
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
await fetch('https://api.example.com/update-email', {
|
|
206
|
+
method: 'POST',
|
|
207
|
+
headers: { 'Content-Type': 'application/json' },
|
|
208
|
+
body: JSON.stringify({ dn, oldMail, newMail }),
|
|
209
|
+
});
|
|
210
|
+
} catch (err) {
|
|
211
|
+
this.logger.error(`Failed to sync mail change: ${err}`);
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Change Detection
|
|
219
|
+
|
|
220
|
+
### Replace Operations
|
|
221
|
+
|
|
222
|
+
```javascript
|
|
223
|
+
// Before: mail = "old@example.com"
|
|
224
|
+
{
|
|
225
|
+
replace: {
|
|
226
|
+
mail: 'new@example.com';
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// Triggers: onLdapMailChange("uid=user,ou=users,dc=example,dc=com", "old@example.com", "new@example.com")
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Add Operations
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
// Before: mail = undefined
|
|
236
|
+
{
|
|
237
|
+
add: {
|
|
238
|
+
mail: 'new@example.com';
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// Triggers: onLdapMailChange("uid=user,ou=users,dc=example,dc=com", null, "new@example.com")
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Delete Operations
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
// Before: mail = "old@example.com"
|
|
248
|
+
{ delete: ["mail"] }
|
|
249
|
+
// Triggers: onLdapMailChange("uid=user,ou=users,dc=example,dc=com", "old@example.com", null)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Performance Considerations
|
|
253
|
+
|
|
254
|
+
### Hook Execution
|
|
255
|
+
|
|
256
|
+
- Hooks run **synchronously** within the modify operation
|
|
257
|
+
- Slow hooks delay the LDAP response
|
|
258
|
+
- Consider async operations with fire-and-forget for non-critical updates
|
|
259
|
+
|
|
260
|
+
### Optimization Tips
|
|
261
|
+
|
|
262
|
+
1. **Keep hooks fast**: Defer heavy work to background jobs
|
|
263
|
+
2. **Use error handling**: Don't let hook failures block LDAP operations
|
|
264
|
+
3. **Batch updates**: If possible, batch multiple hook calls
|
|
265
|
+
4. **Cache external state**: Avoid redundant external API calls
|
|
266
|
+
|
|
267
|
+
## Error Handling
|
|
268
|
+
|
|
269
|
+
Hooks can throw errors to abort the LDAP modification:
|
|
270
|
+
|
|
271
|
+
```javascript
|
|
272
|
+
hooks: {
|
|
273
|
+
onLdapMailChange: async (dn, oldMail, newMail) => {
|
|
274
|
+
// Validate mail domain
|
|
275
|
+
if (!newMail.endsWith('@company.com')) {
|
|
276
|
+
throw new Error('Mail must be @company.com domain');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Update mail server
|
|
280
|
+
try {
|
|
281
|
+
await mailServer.renameAccount(oldMail, newMail);
|
|
282
|
+
} catch (err) {
|
|
283
|
+
throw new Error(`Mail server update failed: ${err.message}`);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
If the hook throws, the LDAP modification is rolled back (if supported by LDAP server).
|
|
290
|
+
|
|
291
|
+
## Debugging
|
|
292
|
+
|
|
293
|
+
Enable debug logging to see change detection:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
--log-level debug
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Output:
|
|
300
|
+
|
|
301
|
+
```
|
|
302
|
+
[debug] Detected mail change for uid=jdoe,ou=users,dc=example,dc=com: old@domain.com → new@domain.com
|
|
303
|
+
[debug] Triggering onLdapMailChange hook
|
|
304
|
+
[info] onLdapMailChange: Mail account renamed successfully
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Limitations
|
|
308
|
+
|
|
309
|
+
1. **Only monitors modify operations**: Does not detect changes from direct LDAP clients
|
|
310
|
+
2. **Requires old value lookup**: Fetches entry before modification for comparison
|
|
311
|
+
3. **Single-valued attributes**: Best suited for single-valued attributes like mail
|
|
312
|
+
4. **Synchronous execution**: Hooks block the LDAP response
|
|
313
|
+
|
|
314
|
+
## See Also
|
|
315
|
+
|
|
316
|
+
- [Twake James Plugin](twakeJames.md) - Mail server integration using onChange
|
|
317
|
+
- [ldapFlatGeneric.md](ldapFlatGeneric.md) - Generic LDAP entity management
|
|
318
|
+
- [Hooks Documentation](../src/hooks.ts) - All available hooks
|