ldap-rest 1.2.1 → 1.3.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/bin/sync-james.mjs +13 -2
- package/dist/abstract/ldapFlat.js +1 -1
- package/dist/abstract/plugin.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/config/options.js +1 -0
- package/dist/config/schema.js +1 -1
- package/dist/lib/auth/base.js +1 -1
- package/dist/lib/authz/base.js +1 -1
- package/dist/lib/entitySchema/validator.js +1 -1
- package/dist/lib/errors.js +1 -1
- package/dist/lib/expressFormatedResponses.js +1 -1
- package/dist/lib/health.js +1 -1
- package/dist/lib/hookBus.js +1 -0
- package/dist/lib/instanceProviders.js +1 -1
- package/dist/lib/ldapActions.js +1 -1
- package/dist/lib/ldapSchema.js +1 -1
- package/dist/lib/parseConfig.js +1 -1
- package/dist/lib/route.js +1 -0
- package/dist/lib/schemaFile.js +1 -1
- package/dist/lib/storage/store.js +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/lib/version.js +1 -0
- package/dist/logger/winston.js +1 -1
- package/dist/plugins/auth/auth.options.js +1 -0
- 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/authzPerRoute.js +1 -1
- package/dist/plugins/auth/authzScope.js +1 -1
- package/dist/plugins/auth/crowdsec.js +1 -1
- package/dist/plugins/auth/fake.js +1 -1
- package/dist/plugins/auth/hmac.js +1 -1
- package/dist/plugins/auth/llng.js +1 -1
- package/dist/plugins/auth/openidconnect.js +1 -1
- package/dist/plugins/auth/rateLimit.js +1 -1
- package/dist/plugins/auth/token.js +1 -1
- package/dist/plugins/auth/totp.js +1 -1
- package/dist/plugins/auth/trustedProxy.js +1 -1
- package/dist/plugins/bcl.js +1 -1
- package/dist/plugins/bcl.options.js +1 -0
- package/dist/plugins/configApi.js +1 -1
- package/dist/plugins/demo/helloworld.js +1 -1
- package/dist/plugins/ldap/accountLifecycle.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/flatGeneric.js +1 -1
- package/dist/plugins/ldap/groups.js +1 -1
- package/dist/plugins/ldap/ldap.options.js +1 -0
- 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/metrics.js +1 -1
- package/dist/plugins/metrics.options.js +1 -0
- package/dist/plugins/rabbitmq.js +1 -1
- package/dist/plugins/rabbitmq.options.js +1 -0
- package/dist/plugins/scim/baseResolver.js +1 -1
- package/dist/plugins/scim/bulk.js +1 -1
- package/dist/plugins/scim/discovery.js +1 -1
- package/dist/plugins/scim/errors.js +1 -1
- package/dist/plugins/scim/filter.js +1 -1
- package/dist/plugins/scim/groups.js +1 -1
- package/dist/plugins/scim/mapping.js +1 -1
- package/dist/plugins/scim/patch.js +1 -1
- package/dist/plugins/scim/scim.js +1 -1
- package/dist/plugins/scim/scim.options.js +1 -0
- package/dist/plugins/scim/users.js +1 -1
- package/dist/plugins/static.js +1 -1
- package/dist/plugins/static.options.js +1 -0
- package/dist/plugins/storage/file.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/storage.js +1 -1
- package/dist/plugins/storage.options.js +1 -0
- package/dist/plugins/twake/appAccounts.options.js +1 -0
- package/dist/plugins/twake/appAccountsApi.js +1 -1
- package/dist/plugins/twake/appAccountsConsistency.js +1 -1
- package/dist/plugins/twake/calendar.js +1 -1
- package/dist/plugins/twake/calendar.options.js +1 -0
- package/dist/plugins/twake/calendarResources.js +1 -1
- package/dist/plugins/twake/cloudery.options.js +1 -0
- package/dist/plugins/twake/clouderyProvision.js +1 -1
- package/dist/plugins/twake/cozy.options.js +1 -0
- package/dist/plugins/twake/cozyProvision.js +1 -1
- package/dist/plugins/twake/drive.js +1 -1
- package/dist/plugins/twake/drive.options.js +1 -0
- 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/james.options.js +1 -0
- package/dist/plugins/twake/lifecycleAttributes.js +1 -1
- package/dist/plugins/twake/lifecycleEvents.js +1 -1
- package/dist/plugins/twake/migrationProxy.js +1 -1
- package/dist/plugins/twake/migrationProxy.options.js +1 -0
- package/dist/plugins/twake/spaces.js +1 -1
- package/dist/plugins/twake/tombstone.js +1 -1
- package/dist/plugins/twake/twakeGroups.options.js +1 -0
- package/dist/plugins/twake/twakeInstances.options.js +1 -0
- package/dist/plugins/twake/twakeLifecycle.options.js +1 -0
- package/dist/plugins/twake/twakeSpaces.options.js +1 -0
- package/dist/plugins/twake/twakeTombstone.options.js +1 -0
- package/dist/plugins/weblogs.js +1 -1
- package/dist/src/abstract/ldapFlat.d.ts +28 -10
- package/dist/src/abstract/plugin.d.ts +10 -5
- package/dist/src/abstract/twakePlugin.d.ts +14 -4
- package/dist/src/bin/index.d.ts +17 -4
- package/dist/src/config/args.d.ts +2 -4
- package/dist/src/config/options.d.ts +89 -0
- package/dist/src/config/schema.d.ts +50 -1
- package/dist/src/hooks.d.ts +60 -22
- package/dist/src/lib/authz/base.d.ts +4 -51
- package/dist/src/lib/authz/composition.d.ts +2 -2
- package/dist/src/lib/entitySchema/validator.d.ts +63 -6
- package/dist/src/lib/errors.d.ts +56 -1
- package/dist/src/lib/expressFormatedResponses.d.ts +49 -7
- package/dist/src/lib/hookBus.d.ts +226 -0
- package/dist/src/lib/instanceProviders.d.ts +0 -8
- package/dist/src/lib/ldapActions.d.ts +7 -0
- package/dist/src/lib/parseConfig.d.ts +5 -0
- package/dist/src/lib/route.d.ts +99 -0
- package/dist/src/lib/utils.d.ts +31 -36
- package/dist/src/lib/version.d.ts +12 -0
- package/dist/src/plugins/auth/auth.options.d.ts +88 -0
- package/dist/src/plugins/auth/authzDynamic.d.ts +10 -1
- package/dist/src/plugins/auth/authzLinid1.d.ts +6 -0
- package/dist/src/plugins/auth/authzPerBranch.d.ts +9 -0
- package/dist/src/plugins/auth/authzPerRoute.d.ts +3 -0
- package/dist/src/plugins/auth/authzScope.d.ts +23 -14
- package/dist/src/plugins/auth/crowdsec.d.ts +5 -0
- package/dist/src/plugins/auth/fake.d.ts +3 -0
- package/dist/src/plugins/auth/hmac.d.ts +4 -0
- package/dist/src/plugins/auth/llng.d.ts +4 -0
- package/dist/src/plugins/auth/openidconnect.d.ts +7 -0
- package/dist/src/plugins/auth/rateLimit.d.ts +4 -0
- package/dist/src/plugins/auth/token.d.ts +3 -0
- package/dist/src/plugins/auth/totp.d.ts +5 -0
- package/dist/src/plugins/auth/trustedProxy.d.ts +4 -0
- package/dist/src/plugins/bcl.d.ts +3 -0
- package/dist/src/plugins/bcl.options.d.ts +8 -0
- package/dist/src/plugins/configApi.d.ts +24 -5
- package/dist/src/plugins/demo/helloworld.d.ts +8 -19
- package/dist/src/plugins/ldap/accountLifecycle.d.ts +17 -0
- package/dist/src/plugins/ldap/bulkImport.d.ts +6 -76
- package/dist/src/plugins/ldap/departmentSync.d.ts +1 -0
- package/dist/src/plugins/ldap/enterpriseRules.d.ts +8 -1
- package/dist/src/plugins/ldap/externalUsersInGroups.d.ts +4 -0
- package/dist/src/plugins/ldap/flatGeneric.d.ts +4 -0
- package/dist/src/plugins/ldap/groups.d.ts +27 -11
- package/dist/src/plugins/ldap/ldap.options.d.ts +75 -0
- package/dist/src/plugins/ldap/onChange.d.ts +10 -2
- package/dist/src/plugins/ldap/organizations.d.ts +16 -0
- package/dist/src/plugins/ldap/passwordPolicy.d.ts +32 -1
- package/dist/src/plugins/ldap/raw.d.ts +50 -19
- package/dist/src/plugins/ldap/trash.d.ts +6 -0
- package/dist/src/plugins/metrics.d.ts +3 -0
- package/dist/src/plugins/metrics.options.d.ts +8 -0
- package/dist/src/plugins/rabbitmq.d.ts +3 -0
- package/dist/src/plugins/rabbitmq.options.d.ts +8 -0
- package/dist/src/plugins/scim/baseResolver.d.ts +2 -1
- package/dist/src/plugins/scim/bulk.d.ts +6 -7
- package/dist/src/plugins/scim/discovery.d.ts +3 -0
- package/dist/src/plugins/scim/errors.d.ts +2 -1
- package/dist/src/plugins/scim/groups.d.ts +6 -4
- package/dist/src/plugins/scim/scim.d.ts +37 -3
- package/dist/src/plugins/scim/scim.options.d.ts +35 -0
- package/dist/src/plugins/scim/users.d.ts +6 -7
- package/dist/src/plugins/static.d.ts +9 -0
- package/dist/src/plugins/static.options.d.ts +13 -0
- package/dist/src/plugins/storage.d.ts +12 -1
- package/dist/src/plugins/storage.options.d.ts +16 -0
- package/dist/src/plugins/twake/appAccounts.options.d.ts +13 -0
- package/dist/src/plugins/twake/appAccountsApi.d.ts +18 -0
- package/dist/src/plugins/twake/appAccountsConsistency.d.ts +8 -0
- package/dist/src/plugins/twake/calendar.d.ts +25 -2
- package/dist/src/plugins/twake/calendar.options.d.ts +19 -0
- package/dist/src/plugins/twake/cloudery.options.d.ts +26 -0
- package/dist/src/plugins/twake/clouderyProvision.d.ts +18 -0
- package/dist/src/plugins/twake/cozy.options.d.ts +23 -0
- package/dist/src/plugins/twake/cozyProvision.d.ts +14 -0
- package/dist/src/plugins/twake/drive.d.ts +7 -0
- package/dist/src/plugins/twake/drive.options.d.ts +15 -0
- package/dist/src/plugins/twake/groups.d.ts +29 -1
- package/dist/src/plugins/twake/instances.d.ts +43 -10
- package/dist/src/plugins/twake/james.d.ts +17 -0
- package/dist/src/plugins/twake/james.options.d.ts +21 -0
- package/dist/src/plugins/twake/lifecycleEvents.d.ts +12 -0
- package/dist/src/plugins/twake/migrationProxy.d.ts +11 -11
- package/dist/src/plugins/twake/migrationProxy.options.d.ts +16 -0
- package/dist/src/plugins/twake/spaces.d.ts +30 -0
- package/dist/src/plugins/twake/tombstone.d.ts +16 -14
- package/dist/src/plugins/twake/twakeGroups.options.d.ts +20 -0
- package/dist/src/plugins/twake/twakeInstances.options.d.ts +43 -0
- package/dist/src/plugins/twake/twakeLifecycle.options.d.ts +21 -0
- package/dist/src/plugins/twake/twakeSpaces.options.d.ts +19 -0
- package/dist/src/plugins/twake/twakeTombstone.options.d.ts +17 -0
- package/docs/README.md +1 -0
- package/docs/client-development/api/rest-api.md +9 -3
- package/docs/client-development/schemas/README.md +96 -32
- package/docs/plugin-development/README.md +45 -94
- package/docs/plugin-development/dependencies.md +11 -6
- package/docs/plugin-development/hooks.md +142 -23
- package/docs/plugin-development/openapi.md +14 -1
- package/docs/plugin-development/options.md +109 -0
- package/docs/plugin-development/routes.md +152 -0
- package/docs/plugin-development/testing.md +3 -3
- package/docs/usage/configuration.md +18 -11
- package/docs/usage/operations.md +19 -4
- package/docs/usage/plugins/ldap/enterprise-rules.md +10 -1
- package/docs/usage/plugins/ldap/flat-generic.md +4 -0
- package/docs/usage/plugins/ldap/raw.md +17 -1
- package/docs/usage/upgrading.md +194 -0
- package/package.json +358 -277
- package/static/browser/ldap-browser/LdapBrowser.js +8 -6
- package/static/browser/ldap-browser/LdapBrowser.js.map +1 -1
- package/static/browser/ldap-browser/api/RawApiClient.js +2 -0
- package/static/browser/ldap-browser/api/RawApiClient.js.map +1 -1
- package/static/browser/ldap-browser/components/EntryTree.js +9 -5
- package/static/browser/ldap-browser/components/EntryTree.js.map +1 -1
- package/static/browser/ldap-browser/components/EntryView.js +2 -0
- package/static/browser/ldap-browser/components/EntryView.js.map +1 -1
- package/static/browser/ldap-browser/schema.js +3 -2
- package/static/browser/ldap-browser/schema.js.map +1 -1
- package/static/browser/ldap-browser.esm.js +27 -18
- package/static/browser/ldap-browser.esm.js.map +1 -1
- package/static/browser/ldap-browser.js +27 -18
- package/static/browser/ldap-browser.js.map +1 -1
- package/static/browser/ldap-browser.min.js +1 -1
- package/static/browser/ldap-browser.min.js.map +1 -1
- package/static/browser/ldap-group-editor/LdapGroupEditor.js +8 -6
- package/static/browser/ldap-group-editor/LdapGroupEditor.js.map +1 -1
- package/static/browser/ldap-group-editor/api/GroupApiClient.js +1 -0
- package/static/browser/ldap-group-editor/api/GroupApiClient.js.map +1 -1
- package/static/browser/ldap-group-editor/components/CreateGroupModal.js +5 -1
- package/static/browser/ldap-group-editor/components/CreateGroupModal.js.map +1 -1
- package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js +6 -3
- package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js.map +1 -1
- package/static/browser/ldap-group-editor/components/GroupTree.js +6 -3
- package/static/browser/ldap-group-editor/components/GroupTree.js.map +1 -1
- package/static/browser/ldap-group-editor/components/MoveGroupModal.js +6 -3
- package/static/browser/ldap-group-editor/components/MoveGroupModal.js.map +1 -1
- package/static/browser/ldap-group-editor.esm.js +32 -16
- package/static/browser/ldap-group-editor.esm.js.map +1 -1
- package/static/browser/ldap-group-editor.js +32 -16
- package/static/browser/ldap-group-editor.js.map +1 -1
- package/static/browser/ldap-group-editor.min.js +1 -1
- package/static/browser/ldap-group-editor.min.js.map +1 -1
- package/static/browser/ldap-resource-editor/api/ResourceApiClient.js +3 -0
- package/static/browser/ldap-resource-editor/api/ResourceApiClient.js.map +1 -1
- package/static/browser/ldap-resource-editor/cache/CacheManager.js +3 -1
- package/static/browser/ldap-resource-editor/cache/CacheManager.js.map +1 -1
- package/static/browser/ldap-tree-viewer/LdapTreeViewer.js +6 -1
- package/static/browser/ldap-tree-viewer/LdapTreeViewer.js.map +1 -1
- package/static/browser/ldap-tree-viewer/api/LdapApiClient.js +2 -0
- package/static/browser/ldap-tree-viewer/api/LdapApiClient.js.map +1 -1
- package/static/browser/ldap-tree-viewer/components/TreeNode.js +4 -0
- package/static/browser/ldap-tree-viewer/components/TreeNode.js.map +1 -1
- package/static/browser/ldap-tree-viewer/components/TreeRoot.js +3 -0
- package/static/browser/ldap-tree-viewer/components/TreeRoot.js.map +1 -1
- package/static/browser/ldap-tree-viewer/store/Store.js +3 -1
- package/static/browser/ldap-tree-viewer/store/Store.js.map +1 -1
- package/static/browser/ldap-tree-viewer.esm.js +18 -2
- package/static/browser/ldap-tree-viewer.esm.js.map +1 -1
- package/static/browser/ldap-tree-viewer.js +18 -2
- package/static/browser/ldap-tree-viewer.js.map +1 -1
- package/static/browser/ldap-tree-viewer.min.js +1 -1
- package/static/browser/ldap-tree-viewer.min.js.map +1 -1
- package/static/browser/ldap-unit-editor/LdapUnitEditor.js +7 -5
- package/static/browser/ldap-unit-editor/LdapUnitEditor.js.map +1 -1
- package/static/browser/ldap-unit-editor/api/UnitApiClient.js +1 -0
- package/static/browser/ldap-unit-editor/api/UnitApiClient.js.map +1 -1
- package/static/browser/ldap-unit-editor/components/MoveUnitModal.js +7 -4
- package/static/browser/ldap-unit-editor/components/MoveUnitModal.js.map +1 -1
- package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js +7 -4
- package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js.map +1 -1
- package/static/browser/ldap-unit-editor/components/UnitTree.js +6 -3
- package/static/browser/ldap-unit-editor/components/UnitTree.js.map +1 -1
- package/static/browser/ldap-unit-editor.esm.js +28 -16
- package/static/browser/ldap-unit-editor.esm.js.map +1 -1
- package/static/browser/ldap-unit-editor.js +28 -16
- package/static/browser/ldap-unit-editor.js.map +1 -1
- package/static/browser/ldap-unit-editor.min.js +1 -1
- package/static/browser/ldap-unit-editor.min.js.map +1 -1
- package/static/browser/ldap-user-editor/LdapUserEditor.js +9 -7
- package/static/browser/ldap-user-editor/LdapUserEditor.js.map +1 -1
- package/static/browser/ldap-user-editor/api/UserApiClient.js +2 -0
- package/static/browser/ldap-user-editor/api/UserApiClient.js.map +1 -1
- package/static/browser/ldap-user-editor/cache/CacheManager.js +3 -1
- package/static/browser/ldap-user-editor/cache/CacheManager.js.map +1 -1
- package/static/browser/ldap-user-editor/components/MoveUserModal.js +7 -4
- package/static/browser/ldap-user-editor/components/MoveUserModal.js.map +1 -1
- package/static/browser/ldap-user-editor/components/PointerField.js +6 -1
- package/static/browser/ldap-user-editor/components/PointerField.js.map +1 -1
- package/static/browser/ldap-user-editor/components/UserEditor.js +10 -5
- package/static/browser/ldap-user-editor/components/UserEditor.js.map +1 -1
- package/static/browser/ldap-user-editor/components/UserList.js +8 -4
- package/static/browser/ldap-user-editor/components/UserList.js.map +1 -1
- package/static/browser/ldap-user-editor/components/UserTree.js +7 -3
- package/static/browser/ldap-user-editor/components/UserTree.js.map +1 -1
- package/static/browser/ldap-user-editor.esm.js +55 -30
- package/static/browser/ldap-user-editor.esm.js.map +1 -1
- package/static/browser/ldap-user-editor.js +55 -30
- package/static/browser/ldap-user-editor.js.map +1 -1
- package/static/browser/ldap-user-editor.min.js +1 -1
- package/static/browser/ldap-user-editor.min.js.map +1 -1
- package/static/browser/shared/components/DisposableComponent.js +3 -5
- package/static/browser/shared/components/DisposableComponent.js.map +1 -1
- package/static/browser/shared/components/Modal.js +2 -1
- package/static/browser/shared/components/Modal.js.map +1 -1
- package/static/browser/shared/components/StatusMessage.js +3 -2
- package/static/browser/shared/components/StatusMessage.js.map +1 -1
- package/static/browser/shared/utils/form.js.map +1 -1
- package/static/browser/shared/utils/hmac.js +1 -0
- package/static/browser/shared/utils/hmac.js.map +1 -1
- package/static/browser/shared/utils/totp.js +1 -0
- package/static/browser/shared/utils/totp.js.map +1 -1
- package/static/browser/shared.esm.js +9 -8
- package/static/browser/shared.esm.js.map +1 -1
- package/static/browser/shared.js +9 -8
- package/static/browser/shared.js.map +1 -1
- package/static/browser/shared.min.js +1 -1
- package/static/browser/shared.min.js.map +1 -1
- package/static/schemas/ad/groups.json +1 -1
- package/static/schemas/ad/users.json +3 -3
- package/static/schemas/example/organizations.json +2 -2
- package/static/schemas/example/users.json +2 -2
- package/static/schemas/obm/users.json +2 -2
- package/static/schemas/standard/groups.json +1 -1
- package/static/schemas/standard/users.json +2 -2
- package/static/schemas/twake/groups.json +4 -4
- package/static/schemas/twake/teamMailboxes.json +3 -3
- package/static/schemas/twake/users.json +7 -7
|
@@ -8,6 +8,7 @@ This guide will help you create powerful, production-ready plugins for LDAP-Rest
|
|
|
8
8
|
- [Plugin Structure](#plugin-structure)
|
|
9
9
|
- [Creating Your Plugin](#creating-your-plugin)
|
|
10
10
|
- [Custom Configuration Options](#custom-configuration-options)
|
|
11
|
+
- [Plugin options](./options.md)
|
|
11
12
|
- [Loading Your Plugin](#loading-your-plugin)
|
|
12
13
|
- [Plugin Features](#plugin-features)
|
|
13
14
|
- [Configuration Best Practices](#configuration-best-practices)
|
|
@@ -59,8 +60,11 @@ export default class MyPlugin extends DmPlugin {
|
|
|
59
60
|
|
|
60
61
|
- **this.server**: DM server instance
|
|
61
62
|
- **this.config**: Complete configuration object
|
|
63
|
+
- **this.options(declarations)**: Values of the options the plugin declares,
|
|
64
|
+
typed and checked, see [plugin options](options.md)
|
|
62
65
|
- **this.logger**: Winston logger instance
|
|
63
|
-
- **this.
|
|
66
|
+
- **this.hookBus**: The server's hooks: chain, notify and wait on them, see
|
|
67
|
+
[hooks](hooks.md#the-bus)
|
|
64
68
|
- **this.fetch**: `fetch` timed into the
|
|
65
69
|
[outbound metrics](../usage/operations.md#metrics) under the plugin's
|
|
66
70
|
name; the lint refuses a bare `fetch` or `node-fetch` under `src`, and a
|
|
@@ -272,99 +276,15 @@ npx ldap-rest
|
|
|
272
276
|
|
|
273
277
|
## Custom Configuration Options
|
|
274
278
|
|
|
275
|
-
|
|
279
|
+
A core plugin declares its options next to its code, in a
|
|
280
|
+
`<plugin>.options.ts` file, and reads them through `this.options()` with their
|
|
281
|
+
types, defaults and checks. See [plugin options](options.md).
|
|
276
282
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
4. **Access in plugin**: `this.config.option_name`
|
|
283
|
-
|
|
284
|
-
### Single Value Options
|
|
285
|
-
|
|
286
|
-
```bash
|
|
287
|
-
# Command line
|
|
288
|
-
npx ldap-rest \
|
|
289
|
-
--webhook-url https://example.com/webhook \
|
|
290
|
-
--webhook-timeout 5000 \
|
|
291
|
-
--webhook-retry true
|
|
292
|
-
|
|
293
|
-
# Environment variables
|
|
294
|
-
export DM_WEBHOOK_URL=https://example.com/webhook
|
|
295
|
-
export DM_WEBHOOK_TIMEOUT=5000
|
|
296
|
-
export DM_WEBHOOK_RETRY=true
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
In your plugin:
|
|
300
|
-
|
|
301
|
-
```typescript
|
|
302
|
-
constructor(server: DM) {
|
|
303
|
-
super(server);
|
|
304
|
-
|
|
305
|
-
// Access custom options (converted to snake_case)
|
|
306
|
-
const url = this.config.webhook_url; // string
|
|
307
|
-
const timeout = this.config.webhook_timeout; // string (convert if needed)
|
|
308
|
-
const retry = this.config.webhook_retry; // string 'true' (convert if needed)
|
|
309
|
-
|
|
310
|
-
// Type conversion
|
|
311
|
-
const timeoutNum = parseInt(timeout as string || '3000');
|
|
312
|
-
const retryBool = (retry as string) === 'true';
|
|
313
|
-
}
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
### Multiple Value Options
|
|
317
|
-
|
|
318
|
-
Repeat the same option multiple times to create an array:
|
|
319
|
-
|
|
320
|
-
```bash
|
|
321
|
-
# Command line
|
|
322
|
-
npx ldap-rest \
|
|
323
|
-
--allowed-domain example.com \
|
|
324
|
-
--allowed-domain test.com \
|
|
325
|
-
--allowed-domain dev.com
|
|
326
|
-
|
|
327
|
-
# Environment variable (comma or semicolon separated)
|
|
328
|
-
export DM_ALLOWED_DOMAINS=example.com,test.com,dev.com
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
In your plugin:
|
|
332
|
-
|
|
333
|
-
```typescript
|
|
334
|
-
constructor(server: DM) {
|
|
335
|
-
super(server);
|
|
336
|
-
|
|
337
|
-
// Access as array (note: singular becomes plural in config key)
|
|
338
|
-
const domains = this.config.allowed_domain as string[];
|
|
339
|
-
// or sometimes stored as: this.config.allowed_domains
|
|
340
|
-
|
|
341
|
-
this.logger.info(`Allowed domains: ${domains.join(', ')}`);
|
|
342
|
-
}
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
### Configuration Priority
|
|
346
|
-
|
|
347
|
-
Values are resolved in this order (later overrides earlier):
|
|
348
|
-
|
|
349
|
-
1. **Default value** in your plugin
|
|
350
|
-
2. **Environment variable** (`DM_OPTION_NAME`)
|
|
351
|
-
3. **Command-line argument** (`--option-name`)
|
|
352
|
-
|
|
353
|
-
```typescript
|
|
354
|
-
constructor(server: DM) {
|
|
355
|
-
super(server);
|
|
356
|
-
|
|
357
|
-
// Provide defaults for optional configuration
|
|
358
|
-
const webhookUrl = this.config.webhook_url || 'http://localhost:8080/webhook';
|
|
359
|
-
const webhookTimeout = parseInt(this.config.webhook_timeout as string || '3000');
|
|
360
|
-
const maxRetries = parseInt(this.config.max_retries as string || '3');
|
|
361
|
-
|
|
362
|
-
// Validate required configuration
|
|
363
|
-
if (!this.config.webhook_secret) {
|
|
364
|
-
throw new Error('--webhook-secret is required for authentication');
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
```
|
|
283
|
+
A plugin outside the repository cannot add to `src/config/args.ts` yet. It keeps
|
|
284
|
+
reading `this.config.<name>`: LDAP-Rest accepts and stores unknown
|
|
285
|
+
`--option-name value` pairs under `option_name`, with `--option-name` and
|
|
286
|
+
`DM_OPTION_NAME` overriding in that order. See
|
|
287
|
+
[configuration](../usage/configuration.md#environment-variables).
|
|
368
288
|
|
|
369
289
|
---
|
|
370
290
|
|
|
@@ -684,7 +604,15 @@ hooks: Hooks = {
|
|
|
684
604
|
|
|
685
605
|
### Exposing API Endpoints
|
|
686
606
|
|
|
687
|
-
|
|
607
|
+
Declare routes with [`defineRoute`](routes.md): it types and validates the
|
|
608
|
+
request (`params`, `query`, `body`), answers a failing request with a 400
|
|
609
|
+
naming the paths at fault, and records the route for the OpenAPI document.
|
|
610
|
+
The `asyncHandler` form below remains for routes that have not been migrated
|
|
611
|
+
yet.
|
|
612
|
+
|
|
613
|
+
Create REST API endpoints for your plugin. Always use the `asyncHandler`
|
|
614
|
+
wrapper for async routes, so that errors are properly handled and do not
|
|
615
|
+
crash the server.
|
|
688
616
|
|
|
689
617
|
```typescript
|
|
690
618
|
import DmPlugin, { asyncHandler } from '../../abstract/plugin';
|
|
@@ -791,6 +719,29 @@ app.get(
|
|
|
791
719
|
);
|
|
792
720
|
```
|
|
793
721
|
|
|
722
|
+
**Wrapping an error keeps its answer:**
|
|
723
|
+
|
|
724
|
+
Wrap with `cause` rather than in the message alone. The status and the class
|
|
725
|
+
of what was thrown are read through the chain, so a refusal stays a 403 and
|
|
726
|
+
a timeout a 504 wherever it surfaces:
|
|
727
|
+
|
|
728
|
+
```typescript
|
|
729
|
+
// ✅ GOOD: the refusal keeps its 403 when it reaches the client
|
|
730
|
+
throw new Error(`Failed to add group ${dn}: ${String(err)}`, { cause: err });
|
|
731
|
+
|
|
732
|
+
// ❌ BAD: a 403 becomes a 500, and its message is all that is left
|
|
733
|
+
throw new Error(`Failed to add group ${dn}: ${String(err)}`);
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
**Refusing a caller:**
|
|
737
|
+
|
|
738
|
+
An authorization plugin throws `AuthzRefusedError`, whose message is for the
|
|
739
|
+
operator: it names the model and the branch it judged. The client is told
|
|
740
|
+
`AUTHZ_REFUSED` with `code: "forbidden"` instead, whatever a plugin wrapped
|
|
741
|
+
it in on the way. A refusal that is not an authorization plugin's is any
|
|
742
|
+
`ForbiddenError`, answered with its own message; `isAuthzRefusal(err)` tells
|
|
743
|
+
the two apart.
|
|
744
|
+
|
|
794
745
|
**Custom error handling:**
|
|
795
746
|
|
|
796
747
|
```typescript
|
|
@@ -27,7 +27,7 @@ overrides builds a second instance, and it needs a name of its own.
|
|
|
27
27
|
### Hook Dependencies
|
|
28
28
|
|
|
29
29
|
**Hook Providers**: Plugins that implement hooks (via `hooks: Hooks = { ... }`)
|
|
30
|
-
**Hook Consumers**: Plugins that
|
|
30
|
+
**Hook Consumers**: Plugins that raise hooks (via `this.hookBus`)
|
|
31
31
|
|
|
32
32
|
## Complete Dependency Graph
|
|
33
33
|
|
|
@@ -601,10 +601,10 @@ refused before a plugin judges its content, or replaces it.
|
|
|
601
601
|
2. Returns modified data to next plugin
|
|
602
602
|
3. Last plugin's return value is used
|
|
603
603
|
|
|
604
|
-
For
|
|
604
|
+
For notified hooks, see [hooks](hooks.md#the-bus):
|
|
605
605
|
|
|
606
|
-
- All hooks
|
|
607
|
-
-
|
|
606
|
+
- All hooks run one after the other, in load order
|
|
607
|
+
- A failing one is logged and the next still runs
|
|
608
608
|
|
|
609
609
|
**Example**: `ldapdeleterequest` chain
|
|
610
610
|
|
|
@@ -642,11 +642,16 @@ export default class MyPlugin extends DmPlugin {
|
|
|
642
642
|
|
|
643
643
|
### Providing Hooks
|
|
644
644
|
|
|
645
|
+
A hook is declared in `src/hooks.ts` before anything raises it, and raised
|
|
646
|
+
through the bus of the kind it is, see [hooks](hooks.md#the-bus):
|
|
647
|
+
|
|
645
648
|
```typescript
|
|
646
649
|
export default class MyPlugin extends DmPlugin {
|
|
647
650
|
async someMethod() {
|
|
648
|
-
//
|
|
649
|
-
await this.
|
|
651
|
+
// Thread a value through the subscribers of a chained hook
|
|
652
|
+
value = await this.hookBus.chain('ldapsearchopts', value);
|
|
653
|
+
// Tell the subscribers of a notified one, without waiting for them
|
|
654
|
+
this.hookBus.notify('ldapadddone', dn, entry, changeContext(req));
|
|
650
655
|
}
|
|
651
656
|
}
|
|
652
657
|
```
|
|
@@ -1,34 +1,143 @@
|
|
|
1
1
|
# Core hooks
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Hooks are how plugins talk to each other. Each one is declared in
|
|
4
|
+
[hooks.ts](../../src/hooks.ts), which is the reference for the arguments it
|
|
5
|
+
gets and what it must answer; a plugin subscribing to a hook it does not
|
|
6
|
+
name there does not compile.
|
|
5
7
|
|
|
6
|
-
##
|
|
8
|
+
## The bus
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
`this.hookBus` (or `server.hookBus`) is how a plugin raises a hook, and how a
|
|
11
|
+
test waits for one. It has one method per kind of hook, told apart by what a
|
|
12
|
+
subscriber answers:
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
| Kind | Method | Subscriber |
|
|
15
|
+
| -------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
16
|
+
| chained | `chain(name, value)` | answers the value, changed or not |
|
|
17
|
+
| notified | `notify(name, ...args)` | answers nothing, not awaited |
|
|
18
|
+
| notified | `notifyAndWait(name, ...args)` | answered nothing, awaited |
|
|
19
|
+
| notified | `notifyStrict(name, ...args)` | as `notifyAndWait`, but a failure goes to the caller |
|
|
20
|
+
| any | `has(name)`, `subscribers(name)` | — |
|
|
21
|
+
| any | `settled()` | resolves once every notification so far has run |
|
|
22
|
+
| any | `set`, `subscribe`, `insert`, `unsubscribe`, `toLast` | add a subscriber, or move one: for a test, or for a plugin changing what its base declares |
|
|
11
23
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- **
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
A chained hook is handed its arguments as one value, a tuple when it has
|
|
25
|
+
several. A notified hook is handed them one by one: its subscriber is
|
|
26
|
+
`(dn, entry, context) => …`, never `([dn, entry], context) => …` — that form
|
|
27
|
+
still compiles, the parameters being bivariant, but then reads the DN's first
|
|
28
|
+
character as the DN.
|
|
29
|
+
|
|
30
|
+
- **Chained.** The value goes from one subscriber to the next, each getting
|
|
31
|
+
what the one before answered; the last answer is the result. A subscriber
|
|
32
|
+
that throws stops the chain and the error travels up: a refusal is a
|
|
33
|
+
refusal. Several values are passed as one tuple.
|
|
34
|
+
- **Notified.** Subscribers run one after the other, outside the request
|
|
35
|
+
that caused the write, and are told what happened and was judged: they
|
|
36
|
+
react as the server — synchronizing, publishing, keeping invariants —
|
|
37
|
+
whoever asked. `notify` does not wait for them and never fails: the write
|
|
38
|
+
they react to is done, and a failing subscriber is logged, with its plugin
|
|
39
|
+
and hook, and counted in the [hook failure
|
|
40
|
+
metrics](../usage/operations.md#metrics), then the next one still runs.
|
|
41
|
+
- **`notifyStrict`** is for the rare subscriber whose failure is part of the
|
|
42
|
+
answer: a logout token reaches the provider only once what it closes is
|
|
43
|
+
recorded, so the library answers 400 and the provider retries. Every
|
|
44
|
+
subscriber runs even when one fails, and the first failure is raised once
|
|
45
|
+
they all have.
|
|
46
|
+
|
|
47
|
+
A test that would otherwise sleep waits instead:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
// One hook's subscribers
|
|
51
|
+
await server.hookBus.notifyAndWait('ldapadddone', dn, entry, {});
|
|
52
|
+
// or whatever the write under test started
|
|
53
|
+
await server.hookBus.settled();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Hooks
|
|
57
|
+
|
|
58
|
+
<!-- hooks: generated from src/hooks.ts, do not edit by hand -->
|
|
59
|
+
|
|
60
|
+
- **hello**: raised by the [demo plugin](../../src/plugins/demo/helloworld.ts) for `/hello`, answered by the demo plugins
|
|
61
|
+
- **ldapsearchopts**: chained before any search, with its options
|
|
62
|
+
- **ldapsearchrequest**: chained before any search, with `[base, options, request]`; an authorization plugin refuses by throwing
|
|
63
|
+
- **ldapsearchresult**: chained after any search, with its result, before it is cached
|
|
64
|
+
- **ldapsearchfilter**: chained on the result of a search after the cache, once per page, with `[result, request, options]`, to drop what the caller may not see
|
|
65
|
+
- **ldapaddrequest**: chained before any add, with `[dn, entry, request]`
|
|
66
|
+
- **ldapadddone**: notified once an add has written the entry, with the DN, the entry and the context, one by one; not awaited
|
|
67
|
+
- **ldapaddafter**: notified once an add has written the entry, before it answers, with the DN, the entry and the context, one by one, see [below](#ldapaddafter)
|
|
68
|
+
- **ldapmodifyrequest**: chained before any modify, with `[dn, changes, op, request]`
|
|
69
|
+
- **ldapmodifydone**: notified once a modify is written, with the DN, the changes, the operation number and the context, one by one
|
|
70
|
+
- **ldapmodifyend**: notified once a modify is over, whatever became of it, and its done subscribers have returned
|
|
71
|
+
- **ldapdeleterequest**: chained before any delete, with `[dn, request]`
|
|
72
|
+
- **ldapdeletedone**: notified once a delete is written, with the DN and the context, one by one
|
|
73
|
+
- **ldapdeleteend**: notified once a delete is over, whatever became of it, and its done subscribers have returned
|
|
74
|
+
- **ldaprenamerequest**: chained before any rename or move, with `[dn, newDn, request]`
|
|
75
|
+
- **ldaprenamedone**: notified once a rename or a move is written, with the DN, its new DN and the context, one by one
|
|
76
|
+
- **ldaprenameend**: notified once a rename is over, whatever became of it, and its done subscribers have returned
|
|
77
|
+
- **oidcsessionvalid**: chained with the claims of a session and the verdict so far, see [the OpenID Connect page](../usage/plugins/auth/openid-connect.md)
|
|
78
|
+
- **oidclogouttoken**: notified with the claims of a session a logout token or `/logout` ends
|
|
79
|
+
- **oidclogin**: notified with the claims of a session just established
|
|
80
|
+
- **ldapgroupvalidatenew**: chained before a group is created, with `[dn, entry]`
|
|
81
|
+
- **ldapgroupadd**: chained before a group is added, with `[dn, entry]`
|
|
82
|
+
- **ldapgroupadddone**: notified once a group is added, with the DN and the entry, one by one
|
|
83
|
+
- **ldapgroupvalidatechanges**: chained before a group is modified, with `[dn, changes]`
|
|
84
|
+
- **ldapgroupmodify**: chained before a group is modified, with `[dn, changes, op]`
|
|
85
|
+
- **ldapgroupmodifydone**: notified once a group is modified, with the DN, the changes and the operation number, one by one
|
|
86
|
+
- **ldapgrouprename**: chained before a group is renamed, with `[dn, newDn]`
|
|
87
|
+
- **ldapgrouprenamedone**: notified once a group is renamed, with the DN and its new DN, one by one
|
|
88
|
+
- **ldapgroupdelete**: chained before a group is deleted, with its DN
|
|
89
|
+
- **ldapgroupdeletedone**: notified once a group is deleted, with its DN
|
|
90
|
+
- **ldapgroupaddmember**: chained before members are added, with `[cn, members]`
|
|
91
|
+
- **ldapgroupdeletemember**: chained before a member is removed, with `[cn, member]`
|
|
92
|
+
- **ldapgroupvalidatemembers**: chained before a member is added, to judge the members, with `[dn, members]`
|
|
93
|
+
- **\_ldapgrouplist**: chained around the list of groups, internal to the groups plugin
|
|
94
|
+
- **onLdapEntryChange**: notified with the entry before and after any write, and who made it
|
|
95
|
+
- **onLdapChange**: notified with the attributes a write changed
|
|
96
|
+
- **onLdapMailChange**: notified with the mail before and after a change, or with no new mail for a delete
|
|
97
|
+
- **onLdapAliasChange**: notified with the mail and the aliases before and after a change
|
|
98
|
+
- **onLdapQuotaChange**: notified with the mail and the quota before and after a change
|
|
99
|
+
- **onLdapForwardChange**: notified with the mail and the forwards before and after a change
|
|
100
|
+
- **onLdapDisplayNameChange**: notified with the name before and after a change
|
|
101
|
+
- **onLdapDriveQuotaChange**: notified with the drive quota before and after a change
|
|
102
|
+
- **externaluserentry**: chained before an external member is created, with `[dn, entry]`
|
|
103
|
+
- **externaluseradded**: notified once an external member is created, with the DN and the entry, one by one
|
|
104
|
+
- **beforeAuth**: chained on a request before it is authenticated, with `[request, response]`
|
|
105
|
+
- **afterAuth**: chained on a request once it is authenticated, with `[request, response]`
|
|
106
|
+
- **getOrganisationTop**: chained before the top organization is answered, with `[request, entry]`
|
|
107
|
+
- **scimusercreate**: chained before a SCIM user is created, with `[user, request]`
|
|
108
|
+
- **scimusercreatedone**: notified once a SCIM user is created
|
|
109
|
+
- **scimuserupdate**: chained before a SCIM user is replaced, with `[id, user, request]`
|
|
110
|
+
- **scimuserupdatedone**: notified once a SCIM user is replaced, with id, user (one by one)
|
|
111
|
+
- **scimuserdelete**: chained before a SCIM user is deleted, with `[id, request]`
|
|
112
|
+
- **scimuserdeletedone**: notified once a SCIM user is deleted, with its id
|
|
113
|
+
- **scimgroupcreate**: chained before a SCIM group is created, with `[group, request, base]`
|
|
114
|
+
- **scimgroupcreatedone**: notified once a SCIM group is created
|
|
115
|
+
- **scimgroupupdate**: chained before a SCIM group is replaced, with `[id, group, request]`
|
|
116
|
+
- **scimgroupupdatedone**: notified once a SCIM group is replaced, with id, group (one by one)
|
|
117
|
+
- **scimgroupdelete**: chained before a SCIM group is deleted, with `[id, request]`
|
|
118
|
+
- **scimgroupdeletedone**: notified once a SCIM group is deleted, with its id
|
|
119
|
+
- **scimbulkdone**: notified once a SCIM bulk request is answered
|
|
120
|
+
- **twakedeletionreplay**: notified with the DN, the entry and the context, one by one when a delete asks of an entry that is already a tombstone
|
|
121
|
+
<!-- /hooks -->
|
|
122
|
+
|
|
123
|
+
## Flat entities
|
|
124
|
+
|
|
125
|
+
A plugin built on `LdapFlat` (`--ldap-flat-schema`) raises one set of hooks
|
|
126
|
+
per entity: `ldap${entity.name}` followed by the suffix of the shared hook of
|
|
127
|
+
the same kind — `add` and `adddone`, `modify` and `modifydone`, `rename` and
|
|
128
|
+
`renamedone`, `delete` and `deletedone`, with the same arguments — plus
|
|
129
|
+
`{prefix}move` before an entry is moved, followed by `onLdapChange` as a
|
|
130
|
+
modify is. A plugin watching one declares it beside its other hooks.
|
|
131
|
+
|
|
132
|
+
## LDAP write hooks
|
|
24
133
|
|
|
25
134
|
### ldapaddafter
|
|
26
135
|
|
|
27
136
|
For what must follow an add within the same request, such as provisioning
|
|
28
137
|
the account the entry describes.
|
|
29
138
|
|
|
30
|
-
- Subscribers get
|
|
31
|
-
does. They run one after the other, and `add()` answers once the last has
|
|
139
|
+
- Subscribers get the DN, the entry and the change context, one by one, as
|
|
140
|
+
`ldapadddone` does. They run one after the other, and `add()` answers once the last has
|
|
32
141
|
returned.
|
|
33
142
|
- An error is logged and goes no further: the add has succeeded, and the
|
|
34
143
|
next subscriber still runs. A subscriber that can fail owns its repair
|
|
@@ -38,7 +147,8 @@ the account the entry describes.
|
|
|
38
147
|
empty. Some of these follow an API call all the same, so select entries by
|
|
39
148
|
their DN or attributes rather than by an empty context.
|
|
40
149
|
- The entry is the one submitted, after `ldapaddrequest`, not what the
|
|
41
|
-
directory holds: no operational attributes, and
|
|
150
|
+
directory holds: no operational attributes, and a `Buffer` value — a
|
|
151
|
+
password sent that way — read as a string.
|
|
42
152
|
- `ldapadddone` and `onLdapEntryChange` fire whatever this hook does, and in
|
|
43
153
|
no set order with it: an add event may or may not include what a
|
|
44
154
|
subscriber here wrote.
|
|
@@ -65,8 +175,17 @@ never reaches its "done" hook; its "end" hook fires all the same.
|
|
|
65
175
|
its `ldaprenamedone` runs outside any operation, even when a request hook
|
|
66
176
|
made it.
|
|
67
177
|
|
|
68
|
-
##
|
|
178
|
+
## onLdapChange
|
|
69
179
|
|
|
70
180
|
- **onLdapEntryChange**: called after any write, with the entry before and
|
|
71
|
-
after it, and who made it
|
|
72
|
-
- **onLdapChange**, **onLdapMailChange**…: derived from it
|
|
181
|
+
after it, and who made it.
|
|
182
|
+
- **onLdapChange**, **onLdapMailChange**…: derived from it, and the
|
|
183
|
+
attribute ones fire only while the entry has the attribute configured for
|
|
184
|
+
them. A delete fires `onLdapEntryChange`, `onLdapChange` and
|
|
185
|
+
`onLdapMailChange` only.
|
|
186
|
+
|
|
187
|
+
## Ordering
|
|
188
|
+
|
|
189
|
+
Hooks run in the order their plugins loaded, but for the request hooks of
|
|
190
|
+
authorization plugins, which run before every other plugin's, see
|
|
191
|
+
[dependencies](dependencies.md#hook-execution-order).
|
|
@@ -3,7 +3,15 @@
|
|
|
3
3
|
The published API reference at <https://linagora.github.io/ldap-rest/api/> is
|
|
4
4
|
generated from `openapi.json`, which itself is built statically by
|
|
5
5
|
[`scripts/generate-openapi.ts`](../../scripts/generate-openapi.ts) — it walks
|
|
6
|
-
every plugin's `api()` method and reads the JSDoc comments above each route
|
|
6
|
+
every plugin's `api()` method and reads the JSDoc comments above each route,
|
|
7
|
+
and reads the declaration of every [`defineRoute`](routes.md) call.
|
|
8
|
+
|
|
9
|
+
With `defineRoute` the declaration is the document: the generator takes its
|
|
10
|
+
`summary`, `description`, `tags`, `params`, `query`, `body` and `responses`,
|
|
11
|
+
and imports the exported schema constants it names. An `@openapi` block above
|
|
12
|
+
the call still wins over what the declaration says — a `requestBody` it
|
|
13
|
+
describes replaces the declared `body` schema whole, rather than merging into
|
|
14
|
+
it — and a route registered with `app.<verb>(` still needs one.
|
|
7
15
|
|
|
8
16
|
You don't need to touch the generator to enrich your plugin's documentation.
|
|
9
17
|
Two JSDoc directives drive the entire pipeline:
|
|
@@ -139,6 +147,11 @@ npm run generate:openapi # writes openapi.json
|
|
|
139
147
|
npm run build:pages # rebuilds the static site under _site/
|
|
140
148
|
```
|
|
141
149
|
|
|
150
|
+
`openapi.json` is committed: it is what the site publishes, so a change to a
|
|
151
|
+
route or to a schema it declares must come with a regenerated document. CI
|
|
152
|
+
runs `npm run check:openapi`, which builds the spec and fails when the file
|
|
153
|
+
differs, naming the first line at fault.
|
|
154
|
+
|
|
142
155
|
Open `_site/api/index.html` to preview the Redoc rendering. The published
|
|
143
156
|
site at <https://linagora.github.io/ldap-rest/api/> is rebuilt automatically
|
|
144
157
|
on every `v*` tag push.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Plugin options
|
|
2
|
+
|
|
3
|
+
A core plugin declares its configuration options next to its code, in a
|
|
4
|
+
`<plugin>.options.ts` file. A family that shares imports and lifecycle keeps
|
|
5
|
+
one set per plugin in a single `<family>.options.ts` file, named after the
|
|
6
|
+
family. `src/config/args.ts` concatenates those declarations,
|
|
7
|
+
so the parser, the command line and the environment variables see the same
|
|
8
|
+
entries as before. The plugin then reads typed values instead of casting
|
|
9
|
+
`this.config`.
|
|
10
|
+
|
|
11
|
+
## Declaring options
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// src/plugins/example/example.options.ts
|
|
15
|
+
import { defineOptions, opt, type OptionsOf } from '../../config/options';
|
|
16
|
+
|
|
17
|
+
export const exampleOptions = defineOptions({
|
|
18
|
+
example_url: opt.string({
|
|
19
|
+
env: 'DM_EXAMPLE_URL',
|
|
20
|
+
required: true,
|
|
21
|
+
}),
|
|
22
|
+
example_timeout: opt.number({
|
|
23
|
+
env: 'DM_EXAMPLE_TIMEOUT',
|
|
24
|
+
default: 5000,
|
|
25
|
+
min: 1,
|
|
26
|
+
}),
|
|
27
|
+
example_retry: opt.boolean({
|
|
28
|
+
env: 'DM_EXAMPLE_RETRY',
|
|
29
|
+
default: false,
|
|
30
|
+
}),
|
|
31
|
+
example_domains: opt.array({
|
|
32
|
+
env: 'DM_EXAMPLE_DOMAINS',
|
|
33
|
+
default: [],
|
|
34
|
+
plural: 'example-domains',
|
|
35
|
+
kind: 'identifiers',
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export type ExampleOptions = OptionsOf<typeof exampleOptions>;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The key is the `Config` name: `example_timeout` is `--example-timeout`,
|
|
43
|
+
`DM_EXAMPLE_TIMEOUT` when the descriptor names that variable.
|
|
44
|
+
|
|
45
|
+
Each declaration takes:
|
|
46
|
+
|
|
47
|
+
| Field | Meaning |
|
|
48
|
+
| ---------- | ------------------------------------------------------------------- |
|
|
49
|
+
| `env` | Environment variable read when the CLI does not set the option |
|
|
50
|
+
| `default` | Value kept when neither the CLI nor the environment sets the option |
|
|
51
|
+
| `required` | Refuse an empty value instead of reading it as unset |
|
|
52
|
+
| `plural` | Array only: CLI name of the plural form, without the leading `--` |
|
|
53
|
+
| `kind` | Array only: how a value splits, see `ArrayKind` in `parseConfig.ts` |
|
|
54
|
+
| `min/max` | Number only: bounds checked when the option is read |
|
|
55
|
+
| `choices` | String only: the values the option accepts |
|
|
56
|
+
|
|
57
|
+
## Reading options
|
|
58
|
+
|
|
59
|
+
The plugin names the declaration as a static field, and asks for the values in
|
|
60
|
+
its constructor:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
export default class Example extends DmPlugin {
|
|
64
|
+
static optionDefinitions = exampleOptions;
|
|
65
|
+
name = 'example';
|
|
66
|
+
|
|
67
|
+
private readonly settings: ExampleOptions;
|
|
68
|
+
|
|
69
|
+
constructor(server: DM) {
|
|
70
|
+
super(server);
|
|
71
|
+
this.settings = this.options(exampleOptions);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`options(exampleOptions)` applies the defaults, copies array defaults and
|
|
77
|
+
checks `required`, `min`, `max` and `choices`. The base constructor asks once,
|
|
78
|
+
so a bad value stops the plugin from loading rather than failing at the first
|
|
79
|
+
request. Each later call reads the configuration again rather than keeping
|
|
80
|
+
the first values. `--plugin example:name:{...}` overrides still work: the view the
|
|
81
|
+
plugin receives carries the merged configuration, and `options()` reads that
|
|
82
|
+
view. Give it the same declaration object the class names in
|
|
83
|
+
`optionDefinitions`: any other one is refused, so the generic type parameter
|
|
84
|
+
cannot describe the wrong view.
|
|
85
|
+
|
|
86
|
+
## Wiring it in
|
|
87
|
+
|
|
88
|
+
Import the declaration in `src/config/args.ts` and spread it where the plugin's
|
|
89
|
+
options used to be:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { exampleOptions } from '../plugins/example/example.options';
|
|
93
|
+
import { toConfigEntries } from './options';
|
|
94
|
+
|
|
95
|
+
const configArgs: ConfigTemplate = [
|
|
96
|
+
// ...
|
|
97
|
+
...toConfigEntries(exampleOptions),
|
|
98
|
+
];
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The `Config` interface keeps the hand-written fields until every plugin has
|
|
102
|
+
moved. A migrated plugin no longer reads them: it uses `options()`.
|
|
103
|
+
|
|
104
|
+
Plugins outside the repository cannot add to `args.ts` yet, but they can
|
|
105
|
+
declare options the same way and name them in `optionDefinitions`: the startup
|
|
106
|
+
report subtracts those keys, and `options()` reads a declared `env` variable
|
|
107
|
+
when the parser did not carry the key. An option without a declaration still
|
|
108
|
+
reaches `this.config` from an unknown `--key value` pair, and the startup
|
|
109
|
+
report names it.
|