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
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Declaring routes
|
|
2
|
+
|
|
3
|
+
`defineRoute` declares an HTTP route once: its inputs, their schemas, what
|
|
4
|
+
the OpenAPI document says of it, and the handler. The schemas are checked
|
|
5
|
+
before the handler runs, the openapi generator describes the route from the
|
|
6
|
+
declaration, and the route probe finds it in the registry.
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
import { Type } from '@sinclair/typebox';
|
|
10
|
+
import type { Express } from 'express';
|
|
11
|
+
|
|
12
|
+
import DmPlugin, { defineRoute, type Role } from '../../abstract/plugin';
|
|
13
|
+
|
|
14
|
+
/** Exported: the OpenAPI generator reads it without running the plugin. */
|
|
15
|
+
export const GroupParams = Type.Object({
|
|
16
|
+
cn: Type.String({ minLength: 1 }),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const GroupQuery = Type.Object({
|
|
20
|
+
dryRun: Type.Optional(Type.Boolean()),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const GroupMembers = Type.Object(
|
|
24
|
+
{ member: Type.Union([Type.String(), Type.Array(Type.String())]) },
|
|
25
|
+
{ additionalProperties: false }
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export const MembersAdded = Type.Object({ added: Type.Number() });
|
|
29
|
+
|
|
30
|
+
export default class MyPlugin extends DmPlugin {
|
|
31
|
+
name = 'myPlugin';
|
|
32
|
+
roles: Role[] = ['api'] as const;
|
|
33
|
+
|
|
34
|
+
api(app: Express): void {
|
|
35
|
+
defineRoute(app, {
|
|
36
|
+
method: 'post',
|
|
37
|
+
path: `${this.config.api_prefix}/v1/ldap/groups/:cn/members`,
|
|
38
|
+
params: GroupParams,
|
|
39
|
+
query: GroupQuery,
|
|
40
|
+
body: GroupMembers,
|
|
41
|
+
responses: {
|
|
42
|
+
200: { schema: MembersAdded, description: 'Members added' },
|
|
43
|
+
},
|
|
44
|
+
summary: 'Add members to a group',
|
|
45
|
+
tags: ['Groups'],
|
|
46
|
+
handler: async ({ params, query, body, res }) => {
|
|
47
|
+
const added = await this.addMembers(params.cn, body.member, {
|
|
48
|
+
dryRun: query.dryRun ?? false,
|
|
49
|
+
});
|
|
50
|
+
res.json({ added });
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The handler has the same duties as an `asyncHandler` callback: it answers
|
|
58
|
+
through `res`, and a rejection is passed to the error middleware.
|
|
59
|
+
|
|
60
|
+
## Schemas
|
|
61
|
+
|
|
62
|
+
Each schema is an **exported constant**, possibly imported from another
|
|
63
|
+
module. `npm run generate:openapi` does not run the plugin: it finds the
|
|
64
|
+
`defineRoute` call in the source, imports the module that exports each
|
|
65
|
+
schema, and reads its value — a TypeBox schema is JSON Schema already. A
|
|
66
|
+
schema written inline in the declaration is checked at runtime but is not
|
|
67
|
+
in the document, and the generator warns about it.
|
|
68
|
+
|
|
69
|
+
Keep in mind that the schemas are part of the published API: give the
|
|
70
|
+
properties a `description`, or rely on what TypeBox emits.
|
|
71
|
+
|
|
72
|
+
## Inputs
|
|
73
|
+
|
|
74
|
+
| Field | Source | Checked as |
|
|
75
|
+
| -------- | ------------ | ---------- |
|
|
76
|
+
| `params` | `req.params` | converted |
|
|
77
|
+
| `query` | `req.query` | converted |
|
|
78
|
+
| `body` | `req.body` | as parsed |
|
|
79
|
+
|
|
80
|
+
A route that needs a route middleware — a multipart parser, say — passes it
|
|
81
|
+
as `middleware`. Express runs it before the handler, and before the `Accept`
|
|
82
|
+
and schema checks: a multipart body is read before the request may be
|
|
83
|
+
refused.
|
|
84
|
+
|
|
85
|
+
Path and query values always arrive as strings, so they are converted before
|
|
86
|
+
being checked: a `Type.Number()` query accepts `?limit=10` and hands the
|
|
87
|
+
handler `10`. The body is whatever the registered parser left in `req.body`;
|
|
88
|
+
a request carrying none is checked as an empty object, so an all-optional
|
|
89
|
+
schema accepts it and a required field is refused by name.
|
|
90
|
+
|
|
91
|
+
A part the route declares no schema for is handed to the handler unchecked,
|
|
92
|
+
as before. The declaration feeds the document the same way: `params` becomes
|
|
93
|
+
the path parameters, `query` the query parameters, `body` the JSON request
|
|
94
|
+
body, and `responses` the answers; the standard 400/401/404/500 entries stay.
|
|
95
|
+
|
|
96
|
+
An answer is a schema, or `{ schema, description }` when the document needs
|
|
97
|
+
a sentence of its own:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
responses: {
|
|
101
|
+
200: { schema: MembersAdded, description: 'Members added' },
|
|
102
|
+
409: { schema: AlreadyMember, description: 'Already a member' },
|
|
103
|
+
500: ErrorSchema,
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Only the parameters the path actually carries (`:cn` → `{cn}`) become path
|
|
108
|
+
parameters: a `params` key the path does not name is reported by the
|
|
109
|
+
generator and left out, since OpenAPI refuses it.
|
|
110
|
+
|
|
111
|
+
## Refusals
|
|
112
|
+
|
|
113
|
+
A request that fails a schema is answered with `400` and a body naming every
|
|
114
|
+
failing path:
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"error": "Invalid request: body.member: Expected string; query.limit: Expected number"
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
A caller whose `Accept` excludes JSON is answered `400 Bad request` before
|
|
123
|
+
the handler runs. A route serving another media type sets `json: false` and
|
|
124
|
+
answers it itself; SCIM does, for `application/scim+json`. Its declared
|
|
125
|
+
schemas are still checked first, and a failure there is answered in the REST
|
|
126
|
+
shape (`Invalid request: …`): such a route validates the rest of its input
|
|
127
|
+
itself. The handler also receives Express's `next`, for a route that falls
|
|
128
|
+
through or has already answered.
|
|
129
|
+
|
|
130
|
+
`Type.Object` accepts unknown keys unless the schema says
|
|
131
|
+
`{ additionalProperties: false }`. A route migrated from hand-written checks
|
|
132
|
+
starts without that option, so it refuses no client it accepted before;
|
|
133
|
+
tighten it one route at a time.
|
|
134
|
+
|
|
135
|
+
## The registry
|
|
136
|
+
|
|
137
|
+
Every declaration is recorded on the app, schemas included, without its
|
|
138
|
+
handler. `registeredRoutes(app)` returns them, and `unregisteredRoutes(app)`
|
|
139
|
+
lists what the Express stack carries that `defineRoute` did not declare.
|
|
140
|
+
|
|
141
|
+
## The migration guard
|
|
142
|
+
|
|
143
|
+
`npm run lint` refuses `app.<verb>(` under `src/plugins` and `src/abstract`,
|
|
144
|
+
through `eslint-rules/no-raw-route.mjs`. `eslint-rules/route-allowlist.json`
|
|
145
|
+
names the files a raw route is tolerated in; it is empty, so every route in
|
|
146
|
+
the tree is declared. `test/scripts/routeAllowlist.test.ts` keeps the list
|
|
147
|
+
exact: a listed file must still declare a raw route, and an unlisted file
|
|
148
|
+
must not.
|
|
149
|
+
|
|
150
|
+
`test/security/routeValidation.test.ts` reads the registry and sends each
|
|
151
|
+
declared query and body a value it must refuse, so a new declaration is
|
|
152
|
+
probed without touching the test.
|
|
@@ -92,12 +92,12 @@ jobs:
|
|
|
92
92
|
runs-on: ubuntu-latest
|
|
93
93
|
|
|
94
94
|
steps:
|
|
95
|
-
- uses: actions/checkout@
|
|
95
|
+
- uses: actions/checkout@v7
|
|
96
96
|
|
|
97
97
|
- name: Setup Node.js
|
|
98
|
-
uses: actions/setup-node@
|
|
98
|
+
uses: actions/setup-node@v7
|
|
99
99
|
with:
|
|
100
|
-
node-version: '
|
|
100
|
+
node-version: '22'
|
|
101
101
|
|
|
102
102
|
- name: Install dependencies
|
|
103
103
|
run: npm ci
|
|
@@ -149,6 +149,11 @@ of the environment variable. An empty value on the command line, such as an
|
|
|
149
149
|
unset variable expanding to nothing, stops the server: leave the option out
|
|
150
150
|
to keep the default.
|
|
151
151
|
|
|
152
|
+
An option or `DM_*` variable that no declared option reads is named in a
|
|
153
|
+
warning at startup. `--strict-options` refuses it instead. Kubernetes injects
|
|
154
|
+
`DM_*` variables for services whose name starts with `dm`; leave the flag off
|
|
155
|
+
where those names are in use.
|
|
156
|
+
|
|
152
157
|
## General Options
|
|
153
158
|
|
|
154
159
|
| CLI | Plural | Env | Default | Description |
|
|
@@ -160,6 +165,7 @@ to keep the default.
|
|
|
160
165
|
| `--logger` | | `DM_LOGGER` | `console` | `console` or `file`, see [Logger](#logger---logger) |
|
|
161
166
|
| `--log-file` | | `DM_LOG_FILE` | `error.log` | File `--logger file` writes to |
|
|
162
167
|
| `--debug` | | `DM_DEBUG` | `false` | Name the failure in the 5xx answers of the error middleware and `serverError`; not for production |
|
|
168
|
+
| `--strict-options` | | `DM_STRICT_OPTIONS` | `false` | Refuse an option or `DM_*` variable no declared option reads |
|
|
163
169
|
| `--api-prefix` | | `DM_API_PREFIX` | `/api` | API URL prefix |
|
|
164
170
|
| `--mail-domain` | `--mail-domains` | `DM_MAIL_DOMAIN` | `[]` | Mail domains |
|
|
165
171
|
| `--schemas-path` | | `DM_SCHEMAS_PATH` | `static/schemas` | Path to JSON schemas |
|
|
@@ -229,15 +235,15 @@ at `0` otherwise.
|
|
|
229
235
|
|
|
230
236
|
#### `core/ldap/groups`
|
|
231
237
|
|
|
232
|
-
| CLI
|
|
233
|
-
|
|
|
234
|
-
| `--ldap-group-base`
|
|
235
|
-
| `--ldap-groups-main-attribute`
|
|
236
|
-
| `--group-class`
|
|
237
|
-
| `--
|
|
238
|
-
| `--group-default-attributes`
|
|
239
|
-
| `--group-dummy-user`
|
|
240
|
-
| `--group-schema`
|
|
238
|
+
| CLI | Plural | Env | Default | Description |
|
|
239
|
+
| ----------------------------------- | ----------------- | ------------------------------- | ---------------------------------- | --------------------------- |
|
|
240
|
+
| `--ldap-group-base` | | `DM_LDAP_GROUP_BASE` | | Groups base DN |
|
|
241
|
+
| `--ldap-groups-main-attribute` | | `DM_LDAP_GROUPS_MAIN_ATTRIBUTE` | `cn` | Group identifier attribute |
|
|
242
|
+
| `--group-class` | `--group-classes` | `DM_GROUP_CLASSES` | `top,groupOfNames` | Group objectClasses |
|
|
243
|
+
| `--groups-allow-unexistent-members` | | `DM_ALLOW_UNEXISTENT_MEMBERS` | `false` | Allow non-existent members |
|
|
244
|
+
| `--group-default-attributes` | | `DM_GROUP_DEFAULT_ATTRIBUTES` | `{}` | Default attributes (JSON) |
|
|
245
|
+
| `--group-dummy-user` | | `DM_GROUP_DUMMY_USER` | `cn=fakeuser` | Dummy user for empty groups |
|
|
246
|
+
| `--group-schema` | | `DM_GROUP_SCHEMA` | `static/schemas/twake/groups.json` | Group JSON schema path |
|
|
241
247
|
|
|
242
248
|
#### `core/ldap/externalUsersInGroups`
|
|
243
249
|
|
|
@@ -429,6 +435,7 @@ whichever of the two names it selects. A value other than `req.user` or
|
|
|
429
435
|
| ---------------------------------- | ----------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
430
436
|
| `--authz-per-branch-config` | `DM_AUTHZ_PER_BRANCH_CONFIG` | `{default:{read:true,write:false,delete:false}}` | Authorization config (JSON) |
|
|
431
437
|
| `--authz-per-branch-cache-ttl` | `DM_AUTHZ_PER_BRANCH_CACHE_TTL` | `60` | Cache TTL (seconds) |
|
|
438
|
+
| `--ldap-group-member-attribute` | `DM_LDAP_GROUP_MEMBER_ATTRIBUTE` | `member` | Attribute a group lists its members in |
|
|
432
439
|
| `--authz-filter-attached-entries` | `DM_AUTHZ_FILTER_ATTACHED_ENTRIES` | `false` | Judge an attached entry by the organization it hangs off, and filter listings accordingly. Honoured by `authzPerBranch`; always on under `authzLinid1`; does nothing under `authzDynamic`. |
|
|
433
440
|
| `--authz-transit-branch` | `DM_AUTHZ_TRANSIT_BRANCH` | | Organization entries are handed over through when they are judged by attachment: every administrator sees and may claim what is there, and puts an entry there with write on the organization it leaves. An entry attached to no organization is in transit too. See [authzLinid1](plugins/auth/authz-linid1.md#transit-handing-an-entry-over). |
|
|
434
441
|
| `--authz-transit-origin-attribute` | `DM_AUTHZ_TRANSIT_ORIGIN_ATTRIBUTE` | | Attribute an entry put in transit keeps the organization it leaves in, removed when it is claimed. Empty records nothing; it must exist in the directory's schema. See [authzLinid1](plugins/auth/authz-linid1.md#where-an-entry-comes-from). |
|
|
@@ -523,7 +530,7 @@ Formerly `core/twake/calendarResources`, still accepted as a deprecated alias.
|
|
|
523
530
|
| `--calendar-concurrency` | `DM_CALENDAR_CONCURRENCY` | `10` | API concurrency |
|
|
524
531
|
| `--calendar-resource-base` | `DM_CALENDAR_RESOURCE_BASE` | | Resource base DN |
|
|
525
532
|
| `--calendar-resource-objectclass` | `DM_CALENDAR_RESOURCE_OBJECTCLASS` | | Resource objectClass |
|
|
526
|
-
| `--calendar-resource-creator` | `DM_CALENDAR_RESOURCE_CREATOR` |
|
|
533
|
+
| `--calendar-resource-creator` | `DM_CALENDAR_RESOURCE_CREATOR` | `admin@example.com` | Resource creator |
|
|
527
534
|
| `--calendar-resource-domain` | `DM_CALENDAR_RESOURCE_DOMAIN` | | Resource domain |
|
|
528
535
|
| `--calendar-firstname-attribute` | `DM_CALENDAR_FIRSTNAME_ATTRIBUTE` | `givenName` | Registered user first name |
|
|
529
536
|
| `--calendar-lastname-attribute` | `DM_CALENDAR_LASTNAME_ATTRIBUTE` | `sn` | Registered user last name |
|
|
@@ -722,7 +729,7 @@ see [Storage](plugins/utilities/storage.md).
|
|
|
722
729
|
|
|
723
730
|
#### `core/configApi`
|
|
724
731
|
|
|
725
|
-
Exposes API configuration at `/api/v1/config` for client applications. Returns available features, schemas, and
|
|
732
|
+
Exposes API configuration at `/api/v1/config` for client applications. Returns available features, schemas, endpoints, and the server's `version` — read from the `package.json` that ships with it, so a client can tell an administrator the server is older than it supports. No configuration options.
|
|
726
733
|
|
|
727
734
|
## Configuration File
|
|
728
735
|
|
package/docs/usage/operations.md
CHANGED
|
@@ -41,7 +41,17 @@ and `/readyz` names the loaded plugins that report: on an ingress exposing
|
|
|
41
41
|
the server to the internet, keep the two paths to the cluster.
|
|
42
42
|
|
|
43
43
|
The Helm chart probes both routes over HTTP (`livenessProbe` and
|
|
44
|
-
`readinessProbe` in `values.yaml`).
|
|
44
|
+
`readinessProbe` in `values.yaml`). The published image also declares a
|
|
45
|
+
`HEALTHCHECK` calling `GET /healthz` from inside the container, on `DM_PORT`:
|
|
46
|
+
`docker ps` reports the container unhealthy when it stops answering, and a
|
|
47
|
+
`--restart` policy restarts it as it would after a crash, while an
|
|
48
|
+
orchestrator with probes of its own ignores the declaration. A port given
|
|
49
|
+
any other way (`--port` in a configuration file, a plugin) leaves the check
|
|
50
|
+
on 8081.
|
|
51
|
+
|
|
52
|
+
The image runs as its unprivileged `node` user: with `docker run`, a
|
|
53
|
+
`DM_PORT` below 1024 stops the server at startup, where the default 8081
|
|
54
|
+
changes nothing.
|
|
45
55
|
|
|
46
56
|
### What the shipped plugins report
|
|
47
57
|
|
|
@@ -110,6 +120,7 @@ the rate limiting and the request id, to keep to the cluster or the host.
|
|
|
110
120
|
| `ldap_rest_ldap_pool_wait_seconds` | histogram | | Time to get a pooled connection, opening one included |
|
|
111
121
|
| `ldap_rest_ldap_pool_connections` | gauge | `state` | Pooled connections, `busy` or `idle` |
|
|
112
122
|
| `ldap_rest_ldap_pool_waiting` | gauge | | Operations waiting for a connection, the pool being full |
|
|
123
|
+
| `ldap_rest_ldap_pool_opening` | gauge | | Connections being opened, within `--ldap-pool-size` |
|
|
113
124
|
| `ldap_rest_ldap_pool_max` | gauge | | `--ldap-pool-size` |
|
|
114
125
|
| `ldap_rest_ldap_search_cache_total` | counter | `result` | Cacheable searches, `hit` or `miss`; nothing when the cache is off |
|
|
115
126
|
| `ldap_rest_hook_failures_total` | counter | `hook`, `plugin` | Notification hooks (`*done`, `onLdap*`) that threw, which the server logs and goes on |
|
|
@@ -117,6 +128,10 @@ the rate limiting and the request id, to keep to the cluster or the host.
|
|
|
117
128
|
| `ldap_rest_unhandled_rejections_total` | counter | | Promise rejections nothing handled; counted by the binary, a host skipping `exitOnCrash()` leaves it at zero |
|
|
118
129
|
| `process_*`, `nodejs_*` | | | The process: CPU, memory, event loop lag, handles, GC |
|
|
119
130
|
|
|
131
|
+
A burst of requests that has to open connections no longer opens one per
|
|
132
|
+
request: a connection being opened takes its place in the pool, and the
|
|
133
|
+
requests over `--ldap-pool-size` wait for a free one.
|
|
134
|
+
|
|
120
135
|
A search the caller pages is timed page by page, what the caller does
|
|
121
136
|
between two pages left out; the readiness ping is not timed. The route
|
|
122
137
|
label is the template a route was declared with, never the URL: a label
|
|
@@ -128,9 +143,9 @@ James's does, is a call like another. A plugin calling a service through
|
|
|
128
143
|
`this.fetch` has its calls counted under its name; the OpenID Connect
|
|
129
144
|
client's calls to its provider are made by its library, and not timed.
|
|
130
145
|
|
|
131
|
-
The instruments come from `prom-client` 15,
|
|
132
|
-
|
|
133
|
-
|
|
146
|
+
The instruments come from `prom-client` 15, which `npm install` reports as
|
|
147
|
+
deprecated: its successor `@prometheus-io/client` is the migration to make,
|
|
148
|
+
and the notice is expected until it is done.
|
|
134
149
|
|
|
135
150
|
The port listens on every interface. What to alert on: `/readyz` for a
|
|
136
151
|
replica out of rotation, `result="error"` in
|
|
@@ -119,7 +119,10 @@ whenever the link changes. Mark the path `generated` so a client cannot set it.
|
|
|
119
119
|
|
|
120
120
|
`5GB`, `500 MB`, `2KB` and `2048` all become a number of bytes, with decimal
|
|
121
121
|
multipliers (1 GB = 10⁹ bytes) as mail servers count them. The same conversion
|
|
122
|
-
applies to creations and to updates.
|
|
122
|
+
applies to creations and to updates. A size above 2⁵³ − 1 bytes is refused
|
|
123
|
+
(400), since its number of bytes would be rounded. A schema `number` or
|
|
124
|
+
`integer` refuses a whole number beyond 2⁵³ on its own, whatever the type;
|
|
125
|
+
see [what a value must look like](../../../client-development/schemas/README.md#what-a-value-must-look-like).
|
|
123
126
|
|
|
124
127
|
### Server-owned defaults
|
|
125
128
|
|
|
@@ -144,6 +147,12 @@ An `accountExpiry` attribute must not carry a date already past. The comparison
|
|
|
144
147
|
is against the start of the current day, so scheduling something for today is
|
|
145
148
|
allowed.
|
|
146
149
|
|
|
150
|
+
The date is an LDAP generalized time, read as written: the year keeps its
|
|
151
|
+
four digits (`0029123123` is a date of year 29, not 1929), and a leap second
|
|
152
|
+
(`20991231235960Z`) is the instant that follows the day. The zone is `Z` or a
|
|
153
|
+
difference of hours and minutes from UTC (`+00`, `+02`, `-0530`, `+14:00`); a
|
|
154
|
+
value past 23 hours or 59 minutes is answered 400 as invalid.
|
|
155
|
+
|
|
147
156
|
### Deletion and references
|
|
148
157
|
|
|
149
158
|
```json
|
|
@@ -451,6 +451,10 @@ Content-Type: application/json
|
|
|
451
451
|
}
|
|
452
452
|
```
|
|
453
453
|
|
|
454
|
+
The identifier may also be the whole DN of the entry, in the entity's
|
|
455
|
+
branch: `"uid": "uid=Smith\\,John,ou=users,dc=example,dc=com"` creates the
|
|
456
|
+
entry `Smith,John`, whose DN spells the name with its escapes.
|
|
457
|
+
|
|
454
458
|
### Modify Entry
|
|
455
459
|
|
|
456
460
|
```bash
|
|
@@ -181,6 +181,22 @@ Site-specific attributes are added with `--ldap-raw-hidden-attribute`, which sta
|
|
|
181
181
|
--ldap-raw-hidden-attribute twakeApiKey --ldap-raw-hidden-attribute employeeNumber
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
+
A hidden attribute is resolved to its directory type, so the type's OID and
|
|
185
|
+
every alias it declares are hidden too — asking for `2.5.4.35` or
|
|
186
|
+
`homePhone` does not serve `userPassword` or a configured
|
|
187
|
+
`homeTelephoneNumber`. A search filter that tests a hidden type, at any
|
|
188
|
+
depth and under any of its names, is refused with a 400: the search runs with
|
|
189
|
+
the service account's rights, so leaving it through would answer the same
|
|
190
|
+
question one match at a time. An assertion on a supertype of a hidden type is
|
|
191
|
+
refused as well, since the directory evaluates an attribute's subtypes. An
|
|
192
|
+
extensible match without an attribute (`(:octetStringMatch:=…)`) runs over
|
|
193
|
+
every attribute and is refused too while attributes are hidden.
|
|
194
|
+
|
|
195
|
+
When the directory schema cannot be read, an entry read or a search is
|
|
196
|
+
refused with a 503 rather than answered with only the configured names
|
|
197
|
+
compared, which could serve an alias or an OID. The root DSE still answers:
|
|
198
|
+
it carries no entry values and is how a client discovers the schema.
|
|
199
|
+
|
|
184
200
|
Note that hiding an attribute here keeps it out of the API, not out of the directory: an account able to bind to LDAP directly still reads it. This is a guard against casual exposure through the browsing UI, not an access control — use [authz-per-branch](../auth/authz-per-branch.md) or directory ACLs for that.
|
|
185
201
|
|
|
186
202
|
## Security
|
|
@@ -189,7 +205,7 @@ Access is restricted in three independent layers:
|
|
|
189
205
|
|
|
190
206
|
1. **Exposed bases** — any DN outside `--ldap-raw-base` is refused with a 403, whatever the caller's rights.
|
|
191
207
|
2. **Authorization hooks** — every entry read, children listing and search goes through `server.ldap`, so the authorization plugins ([authz-per-branch](../auth/authz-per-branch.md), authz-dynamic, …) apply exactly as on the high-level APIs.
|
|
192
|
-
3. **Hidden attributes** — credential attributes, plus anything in `--ldap-raw-hidden-attribute`, are removed from every response, including searches. See [Hidden attributes](#hidden-attributes).
|
|
208
|
+
3. **Hidden attributes** — credential attributes, plus anything in `--ldap-raw-hidden-attribute`, are removed from every response, including searches, under every name their type answers to; a filter that tests one is refused with a 400. See [Hidden attributes](#hidden-attributes).
|
|
193
209
|
|
|
194
210
|
Two exceptions, both deliberate: the **root DSE** and the **schema** are read with the service account, not with the caller's rights. They are directory metadata and carry no user data; reading them with a restricted account would leave the UI unable to name and type attributes. Do not expose this plugin publicly if your directory publishes sensitive information in its root DSE.
|
|
195
211
|
|
package/docs/usage/upgrading.md
CHANGED
|
@@ -4,6 +4,200 @@ What to check before deploying, newest first. Only releases that need a
|
|
|
4
4
|
decision or a configuration change appear here; see the
|
|
5
5
|
[CHANGELOG](../../CHANGELOG.md) for everything else.
|
|
6
6
|
|
|
7
|
+
## From 1.2.1
|
|
8
|
+
|
|
9
|
+
### A plugin written against the old hooks API
|
|
10
|
+
|
|
11
|
+
**Who is affected:** a deployment loading a plugin built outside this
|
|
12
|
+
repository that calls `launchHooks` / `launchHooksChained`, or subscribes
|
|
13
|
+
to a `*done` hook with its arguments packed in one tuple.
|
|
14
|
+
|
|
15
|
+
Hooks are raised through `this.hookBus`, and every hook is declared in
|
|
16
|
+
`src/hooks.ts`: a plugin raising or subscribing to a hook the server does
|
|
17
|
+
not declare no longer compiles. A notified hook — `ldapadddone`,
|
|
18
|
+
`ldapaddafter`, `ldapmodifydone`, `ldaprenamedone`, `ldapgroupadddone`,
|
|
19
|
+
`ldapgroupmodifydone`, `ldapgrouprenamedone`, `ldapexternaluseradded` — now
|
|
20
|
+
gets its arguments one by one: `(dn, entry, context) => …`, not
|
|
21
|
+
`([dn, entry], context) => …`. The packed form still compiles — parameters
|
|
22
|
+
are bivariant — and then reads the DN's first character, so a plugin left
|
|
23
|
+
as it was stops reacting without saying so. `ldapgroupdeletemember` threads
|
|
24
|
+
`[cn, member]`, a single member DN, as it always handed one.
|
|
25
|
+
|
|
26
|
+
A test waiting for a hook to land waits on `await server.hookBus.settled()`
|
|
27
|
+
rather than sleeping. See [hooks](../plugin-development/hooks.md#the-bus).
|
|
28
|
+
|
|
29
|
+
### A plugin with its own `options` or `optionDefinitions` member
|
|
30
|
+
|
|
31
|
+
**Who is affected:** a plugin built outside this repository that declares a
|
|
32
|
+
field, getter or method named `options`, or a static field named
|
|
33
|
+
`optionDefinitions`.
|
|
34
|
+
|
|
35
|
+
`DmPlugin` now has `options()`, which returns the values a plugin declared
|
|
36
|
+
through `optionDefinitions`. Rename the plugin's own `options` member; a
|
|
37
|
+
plugin that does not declare options keeps reading `this.config` as before.
|
|
38
|
+
|
|
39
|
+
### A plugin extending `TwakePlugin`
|
|
40
|
+
|
|
41
|
+
**Who is affected:** a plugin built outside this repository whose class extends
|
|
42
|
+
`TwakePlugin`.
|
|
43
|
+
|
|
44
|
+
The base constructor takes the WebAdmin URL, token and concurrency instead of
|
|
45
|
+
the names of the options holding them:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { jamesOptionsOf } from 'ldap-rest/plugin-twake-james.options';
|
|
49
|
+
|
|
50
|
+
const options = jamesOptionsOf(server.config);
|
|
51
|
+
super(server, {
|
|
52
|
+
url: options.james_webadmin_url,
|
|
53
|
+
token: options.james_webadmin_token,
|
|
54
|
+
concurrency: options.james_concurrency,
|
|
55
|
+
urlOption: 'james_webadmin_url',
|
|
56
|
+
tokenOption: 'james_webadmin_token',
|
|
57
|
+
concurrencyOption: 'james_concurrency',
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The three `*Option` fields only name the option in the startup error and the
|
|
62
|
+
missing-token warning. `concurrency` must be a positive integer; the base
|
|
63
|
+
refuses anything else, naming `concurrencyOption`.
|
|
64
|
+
|
|
65
|
+
### A plugin building SCIM's exported helpers
|
|
66
|
+
|
|
67
|
+
**Who is affected:** a plugin built outside this repository that constructs
|
|
68
|
+
`ScimUsers`, `ScimGroups`, `ScimBulk`, `ScimDiscovery` or `BaseResolver`, or
|
|
69
|
+
calls `scimBodyParser`.
|
|
70
|
+
|
|
71
|
+
These take the SCIM options instead of a plain `Config`:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { scimOptionsOf } from 'ldap-rest/plugin-scim-scim.options';
|
|
75
|
+
|
|
76
|
+
const options = scimOptionsOf(config);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`BaseResolver` keeps its `(config, options)` form; the others take the checked
|
|
80
|
+
values beside what they already require.
|
|
81
|
+
|
|
82
|
+
### An invalid SCIM or Twake value
|
|
83
|
+
|
|
84
|
+
**Who is affected:** a deployment setting a SCIM limit to zero or a negative
|
|
85
|
+
number, giving a per-instance override of the wrong JSON type, or setting the
|
|
86
|
+
James, Calendar, Drive or migration proxy concurrency to the wrong type.
|
|
87
|
+
|
|
88
|
+
These options now refuse a value below 1 when the plugin loads: a zero used
|
|
89
|
+
to be silently replaced by the default, and a negative value used as it was.
|
|
90
|
+
Remove the option to keep the default, or give it a positive number.
|
|
91
|
+
|
|
92
|
+
A per-instance override (`--plugin core/scim:name:{...}`) must also use the
|
|
93
|
+
option's JSON type: `{"scim_max_results": "50"}` and `{"scim_user_base":
|
|
94
|
+
null}` are refused, where the quoted number used to be coerced and the null
|
|
95
|
+
treated as unset. The applicative accounts options refuse the same:
|
|
96
|
+
`{"max_app_accounts": "5"}` used to be kept as the string, and
|
|
97
|
+
`{"applicative_account_attribute": "cn"}` was spread into `['c', 'n']`.
|
|
98
|
+
|
|
99
|
+
The James, Calendar, Drive and migration proxy concurrency used to replace a
|
|
100
|
+
value that was not a number with `10`, and now refuses it. A zero or a
|
|
101
|
+
negative value was already refused by `p-limit`, and the message now names
|
|
102
|
+
the option. The other Twake options also refuse a value of the wrong JSON
|
|
103
|
+
type in an override.
|
|
104
|
+
|
|
105
|
+
A zero `--twake-group-max-page-limit`, `--twake-instance-timeout`,
|
|
106
|
+
`--cloudery-workflow-poll-interval-ms` or
|
|
107
|
+
`--cloudery-workflow-max-attempts` used to be replaced by the default and is
|
|
108
|
+
now refused.
|
|
109
|
+
|
|
110
|
+
The authentication and authorization options are checked the same way. A zero
|
|
111
|
+
`--rate-limit-window-ms`, `--rate-limit-max`, `--crowdsec-cache-ttl`,
|
|
112
|
+
`--authz-per-branch-cache-ttl` or `--authz-dynamic-cache-ttl` used to be
|
|
113
|
+
replaced by the default and is now refused. A zero `--auth-totp-step` or
|
|
114
|
+
`--auth-hmac-window` was accepted and broke the plugin — every counter was
|
|
115
|
+
`Infinity`, every signature not made in the same millisecond was refused — and
|
|
116
|
+
is now refused at startup. A zero `--ldap-organization-max-subnodes`,
|
|
117
|
+
`--ldap-raw-max-results`, `--ppolicy-warn-days`, `--ppolicy-min-length` or
|
|
118
|
+
`--bcl-retention` used to be replaced by the default and is now refused.
|
|
119
|
+
|
|
120
|
+
`ConfigParser` keeps an empty string for a string option. A plugin reads the
|
|
121
|
+
declared default instead when that default is not empty, as its `|| default`
|
|
122
|
+
used to: `DM_COZY_ADMIN_USER=` gives `admin`, `DM_CLOUDERY_OFFER=` gives
|
|
123
|
+
`b2b_twake_default`. An option whose empty value means something says so in
|
|
124
|
+
its declaration (`--cozy-apps`, `--cozy-user-deleted-routing-key`,
|
|
125
|
+
`--twake-instance-cozy-apps`, `--twake-instance-cozy-context`,
|
|
126
|
+
`--twake-instance-cloudery-offer`) and keeps it; an option whose default is
|
|
127
|
+
empty keeps the empty string.
|
|
128
|
+
|
|
129
|
+
### The group member check option
|
|
130
|
+
|
|
131
|
+
**Who is affected:** a deployment passing `--group-allow-unexistent-members`.
|
|
132
|
+
|
|
133
|
+
That spelling was stored under a key the groups plugin never read, so it never
|
|
134
|
+
disabled member validation. The option is now `--groups-allow-unexistent-members`
|
|
135
|
+
and `DM_ALLOW_UNEXISTENT_MEMBERS`, which the plugin reads; the old spelling is
|
|
136
|
+
gone.
|
|
137
|
+
|
|
138
|
+
### A config key a plugin did not declare
|
|
139
|
+
|
|
140
|
+
**Who is affected:** a plugin built outside this repository reading an option
|
|
141
|
+
through `this.config.<name>` without declaring it.
|
|
142
|
+
|
|
143
|
+
`Config` no longer has an index signature, so such a read does not compile.
|
|
144
|
+
Declare the option with `defineOptions` and read it through `this.options()`
|
|
145
|
+
(see [plugin options](../plugin-development/options.md)); the declaration also
|
|
146
|
+
makes the option known to the startup report and its `env` variable read.
|
|
147
|
+
|
|
148
|
+
### Node 22 or later is required
|
|
149
|
+
|
|
150
|
+
**Who is affected:** a deployment running on Node 20 (end of life
|
|
151
|
+
2026-04-30). A deployment of the published image is not: it is built on Node 24.
|
|
152
|
+
|
|
153
|
+
`engines` declares `>=22`, and the CI runs the suite on 22, 24 and 26. Node
|
|
154
|
+
20 is neither tested nor supported.
|
|
155
|
+
|
|
156
|
+
### Values past 4096 characters, and an address with a trailing or empty label
|
|
157
|
+
|
|
158
|
+
**Who is affected:** a deployment whose schemas accept a value longer than
|
|
159
|
+
4096 characters, or an address such as `a@b.c.` or `a@b..c` in an attribute
|
|
160
|
+
whose pattern came with the shipped schemas.
|
|
161
|
+
|
|
162
|
+
A schema `test` pattern is no longer run on a value longer than 4096
|
|
163
|
+
characters; the value is answered 400, naming the length, before the pattern
|
|
164
|
+
sees it, so that a pattern cannot be made to walk several kilobytes. This
|
|
165
|
+
holds for every attribute in every schema which carries a pattern, a
|
|
166
|
+
deployment's own included, and none of the shipped schemas declares a longer
|
|
167
|
+
limit. An attribute holding longer values declares its own `maxLength`, a
|
|
168
|
+
whole number of characters, or the server refuses to load the schema:
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"sshPublicKey": {
|
|
173
|
+
"type": "array",
|
|
174
|
+
"items": { "type": "string", "maxLength": 16384, "test": "^[^\\r\\n]*$" }
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The address patterns shipped with the schemas no longer accept a trailing dot
|
|
180
|
+
or an empty label: `a@b.c.`, `a@b..c` and `a@.b.c` are refused. Entries already holding
|
|
181
|
+
such a value stay as they are; the value is refused the next time that
|
|
182
|
+
attribute is written. A deployment that copied one of these patterns keeps
|
|
183
|
+
its own copy, unbounded only if it declares no `maxLength`; the patterns to
|
|
184
|
+
use are in the
|
|
185
|
+
[schema reference](../../docs/client-development/schemas/README.md#test-optional).
|
|
186
|
+
|
|
187
|
+
### A `null` under a name the schema does not declare
|
|
188
|
+
|
|
189
|
+
**Who is affected:** a client sending `null` to an entity whose schema does
|
|
190
|
+
not declare the attribute, or to an entity without a schema — the
|
|
191
|
+
`dhcpHosts` and `dnsRecords` schemas shipped with `"strict": false` among
|
|
192
|
+
them.
|
|
193
|
+
|
|
194
|
+
Such a write answered 500, with nothing written. It now succeeds: the key is
|
|
195
|
+
left out of a creation or an `add`, and clears the attribute in a `replace`.
|
|
196
|
+
No action is required. A strict schema still answers 400 for such a name, and
|
|
197
|
+
the value is written as if the key were left out, so a client relying on the
|
|
198
|
+
500 to notice a misspelt attribute no longer hears about it — see
|
|
199
|
+
[what a value must look like](../client-development/schemas/README.md#what-a-value-must-look-like).
|
|
200
|
+
|
|
7
201
|
## From 1.2.0 to 1.2.1
|
|
8
202
|
|
|
9
203
|
### `{org}` once in a Twake branch pattern
|