ldap-rest 1.2.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/ldap/callerView.js +1 -0
- package/dist/lib/ldap/pool.js +1 -0
- package/dist/lib/ldap/schema.js +1 -0
- package/dist/lib/ldap/searchCache.js +1 -0
- 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/services/index.js +1 -0
- 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 +41 -10
- package/dist/src/abstract/plugin.d.ts +31 -5
- package/dist/src/abstract/twakePlugin.d.ts +14 -4
- package/dist/src/bin/index.d.ts +35 -4
- package/dist/src/config/args.d.ts +2 -4
- package/dist/src/config/options.d.ts +104 -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 +57 -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/ldap/callerView.d.ts +45 -0
- package/dist/src/lib/ldap/pool.d.ts +108 -0
- package/dist/src/lib/ldap/schema.d.ts +157 -0
- package/dist/src/lib/ldap/searchCache.d.ts +112 -0
- package/dist/src/lib/ldapActions.d.ts +26 -256
- package/dist/src/lib/parseConfig.d.ts +31 -2
- package/dist/src/lib/route.d.ts +99 -0
- package/dist/src/lib/services/index.d.ts +56 -0
- package/dist/src/lib/utils.d.ts +100 -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 -15
- 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 +35 -3
- 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/mapping.d.ts +16 -3
- 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/types.d.ts +2 -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 +74 -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/lifecycleAttributes.d.ts +13 -2
- 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 +25 -16
- 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 +17 -2
- package/docs/client-development/api/rest-api.md +13 -3
- package/docs/client-development/schemas/README.md +96 -32
- package/docs/development/contracts.md +109 -0
- package/docs/plugin-development/README.md +53 -95
- package/docs/plugin-development/dependencies.md +24 -8
- package/docs/plugin-development/hooks.md +143 -23
- package/docs/plugin-development/openapi.md +14 -1
- package/docs/plugin-development/options.md +136 -0
- package/docs/plugin-development/routes.md +152 -0
- package/docs/plugin-development/services.md +95 -0
- package/docs/plugin-development/testing.md +3 -3
- package/docs/usage/configuration.md +18 -11
- package/docs/usage/operations.md +28 -4
- package/docs/usage/plugins/integrations/instances.md +11 -3
- package/docs/usage/plugins/integrations/lifecycle-events.md +4 -1
- package/docs/usage/plugins/integrations/scim.md +20 -18
- package/docs/usage/plugins/integrations/tombstone.md +10 -6
- package/docs/usage/plugins/ldap/enterprise-rules.md +10 -1
- package/docs/usage/plugins/ldap/flat-generic.md +11 -0
- package/docs/usage/plugins/ldap/groups.md +4 -1
- package/docs/usage/plugins/ldap/password-policy.md +7 -0
- package/docs/usage/plugins/ldap/raw.md +17 -1
- package/docs/usage/upgrading.md +253 -0
- package/package.json +372 -286
- 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,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The hooks of the server, typed by `Hooks`, with one calling convention per
|
|
3
|
+
* kind.
|
|
4
|
+
*
|
|
5
|
+
* The three kinds are told apart by what their subscriber answers:
|
|
6
|
+
* chained hooks get a value and pass it — possibly changed — along the
|
|
7
|
+
* chain; notified hooks answer nothing, are not waited for, and their
|
|
8
|
+
* failures are logged; several hooks answer a value each.
|
|
9
|
+
*
|
|
10
|
+
* @see docs/plugin-development/hooks.md
|
|
11
|
+
*/
|
|
12
|
+
import type { Hooks } from '../hooks';
|
|
13
|
+
export type HookFn = Function;
|
|
14
|
+
/** A promise, or the value itself */
|
|
15
|
+
type Settled<T> = T extends Promise<infer P> ? P : T;
|
|
16
|
+
/** Whether `F` answers nothing: `void`, or a promise of it */
|
|
17
|
+
export type AnswersVoid<F> = [
|
|
18
|
+
Settled<F extends (...args: never[]) => infer R ? R : never>
|
|
19
|
+
] extends [void | undefined] ? true : false;
|
|
20
|
+
/** The values of `Hooks` a chain threads: the ones whose answer is a value */
|
|
21
|
+
export type ChainedName = {
|
|
22
|
+
[K in keyof Hooks]-?: AnswersVoid<NonNullable<Hooks[K]>> extends true ? never : K;
|
|
23
|
+
}[keyof Hooks];
|
|
24
|
+
/** The `Hooks` a notification fires: the ones whose subscriber answers nothing */
|
|
25
|
+
export type NotifiedName = Exclude<keyof Hooks, ChainedName>;
|
|
26
|
+
/** A chained hook is declared with the value it threads */
|
|
27
|
+
export type ChainedNameOf<K extends keyof Hooks> = K extends ChainedName ? K : never;
|
|
28
|
+
/** The arguments a hook expects */
|
|
29
|
+
export type Args<K extends keyof Hooks> = NonNullable<Hooks[K]> extends (...args: infer A) => unknown ? A : never;
|
|
30
|
+
/** What a chained hook threads */
|
|
31
|
+
export type ChainedValue<K extends keyof Hooks> = NonNullable<Hooks[K]> extends (arg: infer A) => unknown ? A : never;
|
|
32
|
+
/**
|
|
33
|
+
* Record the plugin a hook function belongs to.
|
|
34
|
+
*
|
|
35
|
+
* @param fn the registered function
|
|
36
|
+
* @param plugin the plugin's instance name
|
|
37
|
+
* @param hook the hook it is registered under
|
|
38
|
+
*/
|
|
39
|
+
export declare const recordHookOwner: (fn: HookFn, plugin: string, hook: string) => void;
|
|
40
|
+
/**
|
|
41
|
+
* The plugin a hook function belongs to, and the hook it is registered
|
|
42
|
+
* under, when `registerPlugin` registered it.
|
|
43
|
+
*
|
|
44
|
+
* @param fn a hook function
|
|
45
|
+
* @returns its plugin's instance name and hook name, or undefined
|
|
46
|
+
*/
|
|
47
|
+
export declare const hookOwner: (fn: HookFn) => {
|
|
48
|
+
plugin: string;
|
|
49
|
+
hook: string;
|
|
50
|
+
} | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Run a hook under its plugin's name and its own, for the lines it logs.
|
|
53
|
+
*
|
|
54
|
+
* @param hook the registered function
|
|
55
|
+
* @param fn its run
|
|
56
|
+
* @returns what `fn` returns
|
|
57
|
+
*/
|
|
58
|
+
export declare const runAsHookOwner: <T>(hook: HookFn, fn: () => T) => T;
|
|
59
|
+
/**
|
|
60
|
+
* A hook's list, as `server.hooks` spells it: `undefined` when nothing is
|
|
61
|
+
* subscribed, a function for one subscriber, a list for several, and
|
|
62
|
+
* anything else when a test writes a list through the hook and walks it.
|
|
63
|
+
*/
|
|
64
|
+
export type HookValue<K extends keyof Hooks = keyof Hooks> = Hooks[K] | Function[] | undefined;
|
|
65
|
+
/** The functions a subscriber value holds, one or many */
|
|
66
|
+
export declare const asHooks: <K extends keyof Hooks>(value: HookValue<K>) => HookFn[];
|
|
67
|
+
/**
|
|
68
|
+
* How a flat entity raises its own hooks, see `abstract/ldapFlat.ts`: the
|
|
69
|
+
* prefix it names them under, added to the suffix of each. They are not
|
|
70
|
+
* `Hooks`' own, see {@link FlatHookPrefix}.
|
|
71
|
+
*/
|
|
72
|
+
export interface FlatHookBus {
|
|
73
|
+
/** Thread a value through `{prefix}{suffix}`, as {@link HookBus.chain} */
|
|
74
|
+
chain<T>(suffix: string, value: T): Promise<T>;
|
|
75
|
+
/** Notify `{prefix}{suffix}`, as {@link HookBus.notify} */
|
|
76
|
+
notify(suffix: string, ...args: unknown[]): void;
|
|
77
|
+
}
|
|
78
|
+
export default class HookBus implements FlatHookBus {
|
|
79
|
+
/**
|
|
80
|
+
* What `server.hooks` holds, shared by reference. A plugin reaching it
|
|
81
|
+
* directly is not told by its type whether the hook exists, nor how it
|
|
82
|
+
* is called; this bus is.
|
|
83
|
+
*/
|
|
84
|
+
private readonly hooks;
|
|
85
|
+
private readonly tracked;
|
|
86
|
+
/**
|
|
87
|
+
* @param hooks the server's hook lists, shared by reference
|
|
88
|
+
*/
|
|
89
|
+
constructor(hooks: {
|
|
90
|
+
[K in keyof Hooks]?: HookValue<K>;
|
|
91
|
+
});
|
|
92
|
+
/** Whether anything is subscribed to a hook */
|
|
93
|
+
has(name: keyof Hooks): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* The subscribers of a hook, in the order they run.
|
|
96
|
+
*
|
|
97
|
+
* @param name the hook
|
|
98
|
+
* @returns its subscribers, empty when nothing subscribed
|
|
99
|
+
*/
|
|
100
|
+
subscribers(name: keyof Hooks): readonly HookFn[];
|
|
101
|
+
/**
|
|
102
|
+
* Subscribe a function to a hook, or forget its subscribers when given
|
|
103
|
+
* none, as a plugin declaring `hooks` would.
|
|
104
|
+
*
|
|
105
|
+
* @param name the hook
|
|
106
|
+
* @param fn what to run, none to unsubscribe every subscriber
|
|
107
|
+
*/
|
|
108
|
+
set<K extends keyof Hooks>(name: K, ...fn: NonNullable<Hooks[K]>[]): void;
|
|
109
|
+
/**
|
|
110
|
+
* Add a subscriber to a hook, after the ones already there.
|
|
111
|
+
*
|
|
112
|
+
* @param name the hook
|
|
113
|
+
* @param fn what to run
|
|
114
|
+
*/
|
|
115
|
+
subscribe<K extends keyof Hooks>(name: K, fn: NonNullable<Hooks[K]>): void;
|
|
116
|
+
/**
|
|
117
|
+
* Put a subscriber at a given place in a hook's list, as
|
|
118
|
+
* `registerPlugin` does for an authorization hook.
|
|
119
|
+
*
|
|
120
|
+
* @param name the hook
|
|
121
|
+
* @param fn what to run
|
|
122
|
+
* @param at its place, the end when past it
|
|
123
|
+
*/
|
|
124
|
+
insert<K extends keyof Hooks>(name: K, fn: NonNullable<Hooks[K]>, at: number): void;
|
|
125
|
+
/**
|
|
126
|
+
* Remove a subscriber from a hook, wherever it sits.
|
|
127
|
+
*
|
|
128
|
+
* @param name the hook
|
|
129
|
+
* @param fn the subscriber
|
|
130
|
+
*/
|
|
131
|
+
unsubscribe<K extends keyof Hooks>(name: K, fn: NonNullable<Hooks[K]>): void;
|
|
132
|
+
/**
|
|
133
|
+
* Move a subscriber to the end of its hook's list, keeping it registered,
|
|
134
|
+
* for a plugin that must run after every other one.
|
|
135
|
+
*
|
|
136
|
+
* @param name the hook
|
|
137
|
+
* @param hook the subscriber
|
|
138
|
+
* @returns whether it was subscribed to the hook
|
|
139
|
+
*/
|
|
140
|
+
toLast(name: keyof Hooks, hook: HookFn): boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Thread a value through every subscriber of a chained hook, each getting
|
|
143
|
+
* what the one before answered. The first failure stops the chain and
|
|
144
|
+
* travels up: a refusal is a refusal, and the caller must not go on as if
|
|
145
|
+
* the chain had said yes.
|
|
146
|
+
*
|
|
147
|
+
* Hooks run outside the request being served: what a hook reads or writes
|
|
148
|
+
* to validate, complete or keep track of an operation is the server's
|
|
149
|
+
* (#385). The request is in the arguments of the hooks that judge, which
|
|
150
|
+
* judge by it; a hook acting as the caller names it.
|
|
151
|
+
*
|
|
152
|
+
* @param name the hook
|
|
153
|
+
* @param value the value to thread
|
|
154
|
+
* @returns the last value in the chain
|
|
155
|
+
*/
|
|
156
|
+
chain<K extends keyof Hooks>(name: ChainedNameOf<K>, value: ChainedValue<K>): Promise<ChainedValue<K>>;
|
|
157
|
+
/**
|
|
158
|
+
* Notify every subscriber of a hook, one after the other, outside the
|
|
159
|
+
* request that caused it: they are told what happened, and was judged, and
|
|
160
|
+
* react as the server — synchronizing, publishing, keeping invariants —
|
|
161
|
+
* whoever asked.
|
|
162
|
+
*
|
|
163
|
+
* Not awaited, and not failed: the write the subscribers react to is done,
|
|
164
|
+
* and a subscriber's failure is logged and goes no further. `settled`
|
|
165
|
+
* waits for them.
|
|
166
|
+
*
|
|
167
|
+
* @param name the hook
|
|
168
|
+
* @param args what its subscribers are told
|
|
169
|
+
*/
|
|
170
|
+
notify<K extends NotifiedName>(name: K, ...args: Args<K>): void;
|
|
171
|
+
/**
|
|
172
|
+
* Run every subscriber of a hook and let their failures go to the caller,
|
|
173
|
+
* for the rare hook whose subscribers are part of the answer: a logout
|
|
174
|
+
* token reaches the provider only once what it closes is recorded, so the
|
|
175
|
+
* failure has to reach the library, which answers 400 and lets the
|
|
176
|
+
* provider retry.
|
|
177
|
+
*
|
|
178
|
+
* Every subscriber runs even when one fails, one after the other as
|
|
179
|
+
* `notify` runs them, and the first failure is raised once they all have:
|
|
180
|
+
* stopping at the first would cost the others their turn — a session the
|
|
181
|
+
* provider considers closed would keep working against the store that
|
|
182
|
+
* would have killed it.
|
|
183
|
+
*
|
|
184
|
+
* @param name the hook
|
|
185
|
+
* @param args what its subscribers are told
|
|
186
|
+
*/
|
|
187
|
+
notifyStrict<K extends NotifiedName>(name: K, ...args: Args<K>): Promise<void>;
|
|
188
|
+
/**
|
|
189
|
+
* Notify, then wait for the subscribers: a caller that has to know the
|
|
190
|
+
* consequences have run, a test instead of a sleep.
|
|
191
|
+
*
|
|
192
|
+
* @param name the hook
|
|
193
|
+
* @param args what its subscribers are told
|
|
194
|
+
* @returns once they have returned
|
|
195
|
+
*/
|
|
196
|
+
notifyAndWait<K extends NotifiedName>(name: K, ...args: Args<K>): Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* Run every hook the server holds and answer what each gives, in order.
|
|
199
|
+
* For the hooks nothing decided the answer of: `hello` is a demo of one.
|
|
200
|
+
*
|
|
201
|
+
* @param name the hook
|
|
202
|
+
* @param args what its subscribers are given
|
|
203
|
+
* @returns one answer per subscriber
|
|
204
|
+
*/
|
|
205
|
+
call<K extends keyof Hooks>(name: K, ...args: Args<K>): Promise<unknown[]>;
|
|
206
|
+
/**
|
|
207
|
+
* A view raising `{prefix}{suffix}` hooks, for an entity whose hooks are
|
|
208
|
+
* named dynamically.
|
|
209
|
+
*
|
|
210
|
+
* @param prefix the entity's prefix
|
|
211
|
+
* @returns the bus, raising its hooks
|
|
212
|
+
*/
|
|
213
|
+
prefixed(prefix: string): FlatHookBus;
|
|
214
|
+
/**
|
|
215
|
+
* Resolves when every notification started so far has run, the ones
|
|
216
|
+
* started while waiting included.
|
|
217
|
+
*/
|
|
218
|
+
settled(): Promise<void>;
|
|
219
|
+
/**
|
|
220
|
+
* Write a hook's subscribers the way `server.hooks` spells them: no entry
|
|
221
|
+
* when nothing is subscribed, the function itself for one subscriber.
|
|
222
|
+
*/
|
|
223
|
+
private store;
|
|
224
|
+
private fire;
|
|
225
|
+
}
|
|
226
|
+
export {};
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module lib/instanceProviders
|
|
3
|
-
*
|
|
4
|
-
* The two places a workplace instance can be created: the Cloudery, which
|
|
5
|
-
* answers at once and builds the instance afterwards, and the cozy-stack
|
|
6
|
-
* admin API, which builds it before answering.
|
|
7
|
-
*/
|
|
8
|
-
import { type RequestInit, type Response } from 'node-fetch';
|
|
9
1
|
/**
|
|
10
2
|
* Reduce a name to what the Cloudery takes in a slug; it refused `_` or `@`
|
|
11
3
|
* with "Slug is invalid". `-` is kept, except at either end where no host
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The directory as one caller sees it: whether a route may tell them an
|
|
3
|
+
* entry exists, and what the `ldapsearchfilter` hooks leave of a result.
|
|
4
|
+
*/
|
|
5
|
+
import type { Request } from 'express';
|
|
6
|
+
import type { SearchOptions, SearchResult } from 'ldapts';
|
|
7
|
+
export interface CallerViewOptions {
|
|
8
|
+
/** Run a search for the caller who asks. */
|
|
9
|
+
search: (options: SearchOptions, base: string, req?: Request) => Promise<SearchResult | AsyncGenerator<SearchResult>>;
|
|
10
|
+
/** Whether any plugin filters search results. */
|
|
11
|
+
hasFilter: () => boolean;
|
|
12
|
+
/** Hand one chunk to `ldapsearchfilter`. */
|
|
13
|
+
filter: (chunk: SearchResult, req?: Request, opts?: SearchOptions) => Promise<SearchResult>;
|
|
14
|
+
}
|
|
15
|
+
export default class CallerView {
|
|
16
|
+
private readonly search;
|
|
17
|
+
private readonly hasFilter;
|
|
18
|
+
private readonly filterHook;
|
|
19
|
+
constructor(options: CallerViewOptions);
|
|
20
|
+
/**
|
|
21
|
+
* Whether the caller of a request can read an entry.
|
|
22
|
+
*
|
|
23
|
+
* For a route that finds an entry as the server — by an attribute, across
|
|
24
|
+
* branches — and then answers about it: an entry the caller cannot read
|
|
25
|
+
* must get the answer an entry that is not there gets, or the route tells
|
|
26
|
+
* them what the directory holds where they cannot read. Refused or
|
|
27
|
+
* filtered out by an authorization plugin, the entry reads as absent.
|
|
28
|
+
* Without a request, as for any operation, nobody judges.
|
|
29
|
+
*
|
|
30
|
+
* @param dn the entry
|
|
31
|
+
* @param req the request whose caller asks
|
|
32
|
+
* @returns whether a read of the entry, as that caller, finds it
|
|
33
|
+
*/
|
|
34
|
+
readableBy(dn: string, req?: Request): Promise<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* Hand a search result to `ldapsearchfilter`, which may drop entries the
|
|
37
|
+
* caller is not allowed to see.
|
|
38
|
+
*
|
|
39
|
+
* Applied on every return path of `search`, and deliberately after the
|
|
40
|
+
* cache: `ldapsearchresult` fires before caching and knows no request, so
|
|
41
|
+
* what it removed for one caller would be served to the next. A paginated
|
|
42
|
+
* search is filtered chunk by chunk, as it is consumed.
|
|
43
|
+
*/
|
|
44
|
+
filter(value: SearchResult | AsyncGenerator<SearchResult>, req?: Request, opts?: SearchOptions): SearchResult | AsyncGenerator<SearchResult> | Promise<SearchResult>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The directory connections this server keeps: failover between the URLs,
|
|
3
|
+
* a bounded pool with a TTL, and the readiness probe on a connection of its
|
|
4
|
+
* own.
|
|
5
|
+
*/
|
|
6
|
+
import { Client } from 'ldapts';
|
|
7
|
+
import type { ClientOptions } from 'ldapts';
|
|
8
|
+
export interface PoolLogger {
|
|
9
|
+
debug(message: string): void;
|
|
10
|
+
info(message: string): void;
|
|
11
|
+
warn(message: string): void;
|
|
12
|
+
error(message: string): void;
|
|
13
|
+
}
|
|
14
|
+
export interface PooledConnection {
|
|
15
|
+
client: Client;
|
|
16
|
+
createdAt: number;
|
|
17
|
+
inUse: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** Opens a client; binding is the pool's, so a fake can stand in. */
|
|
20
|
+
export type ClientFactory = (options: ClientOptions) => Client | Promise<Client>;
|
|
21
|
+
export interface LdapPoolOptions {
|
|
22
|
+
/** The URLs to try. */
|
|
23
|
+
urls: string[];
|
|
24
|
+
dn: string;
|
|
25
|
+
pwd: string;
|
|
26
|
+
/** Options shared by every URL; the pool sets `url` and `tlsOptions`. */
|
|
27
|
+
options: ClientOptions;
|
|
28
|
+
poolSize: number;
|
|
29
|
+
connectionTtl: number;
|
|
30
|
+
logger: PoolLogger;
|
|
31
|
+
clientFactory?: ClientFactory;
|
|
32
|
+
}
|
|
33
|
+
export default class LdapPool {
|
|
34
|
+
private readonly urls;
|
|
35
|
+
private readonly dn;
|
|
36
|
+
private readonly pwd;
|
|
37
|
+
private readonly options;
|
|
38
|
+
private readonly poolSize;
|
|
39
|
+
private readonly connectionTtl;
|
|
40
|
+
private readonly logger;
|
|
41
|
+
private readonly clientFactory;
|
|
42
|
+
private connectionPool;
|
|
43
|
+
private availableConnections;
|
|
44
|
+
private waitingResolvers;
|
|
45
|
+
/** Connections being opened, not yet in `connectionPool` */
|
|
46
|
+
private opening;
|
|
47
|
+
private isCleaningUp;
|
|
48
|
+
private currentUrlIndex;
|
|
49
|
+
/** Set by `close`: no connection is handed out any more */
|
|
50
|
+
private closed;
|
|
51
|
+
constructor(options: LdapPoolOptions);
|
|
52
|
+
/**
|
|
53
|
+
* Create a new LDAP connection with failover support
|
|
54
|
+
*/
|
|
55
|
+
private createConnection;
|
|
56
|
+
/**
|
|
57
|
+
* Clean up expired connections from the pool
|
|
58
|
+
* Uses a flag to prevent concurrent cleanup operations
|
|
59
|
+
*/
|
|
60
|
+
private cleanupExpiredConnections;
|
|
61
|
+
/**
|
|
62
|
+
* Ask the directory for its root DSE on a connection of its own, bound
|
|
63
|
+
* as the server and closed after: the readiness probe. Neither the pool
|
|
64
|
+
* nor the search cache nor the hooks answer for a directory that does
|
|
65
|
+
* not, and a directory that stops answering holds nothing of the pool.
|
|
66
|
+
* The URLs are tried as `createConnection` tries them.
|
|
67
|
+
*
|
|
68
|
+
* @param timeoutMs how long the connection, the bind and the search may
|
|
69
|
+
* each take; the client closes the socket when it passes
|
|
70
|
+
* @throws when no URL answers in time
|
|
71
|
+
*/
|
|
72
|
+
ping(timeoutMs: number): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Close every pooled connection, the ones in use included: an operation
|
|
75
|
+
* still running on one is rejected by the client, and a waiter is refused
|
|
76
|
+
* rather than kept.
|
|
77
|
+
*/
|
|
78
|
+
close(): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Acquire a connection from the pool or create a new one
|
|
81
|
+
* Optimized for O(1) lookup using separate available connections queue
|
|
82
|
+
*/
|
|
83
|
+
acquire(): Promise<PooledConnection>;
|
|
84
|
+
/**
|
|
85
|
+
* Run `operation` on a pooled connection, released whatever becomes of it.
|
|
86
|
+
*/
|
|
87
|
+
withConnection<T>(operation: (client: Client) => Promise<T>): Promise<T>;
|
|
88
|
+
/** Throw when `close` ran: the pool hands out no connection any more. */
|
|
89
|
+
assertOpen(): void;
|
|
90
|
+
/** The pool as it is, for the metrics */
|
|
91
|
+
stats(): {
|
|
92
|
+
busy: number;
|
|
93
|
+
idle: number;
|
|
94
|
+
waiting: number;
|
|
95
|
+
opening: number;
|
|
96
|
+
max: number;
|
|
97
|
+
};
|
|
98
|
+
private takeConnection;
|
|
99
|
+
/** Refuse every queued request with `error` */
|
|
100
|
+
private refuseWaiters;
|
|
101
|
+
/** Let one queued request take a place that freed with no connection */
|
|
102
|
+
private wakeWaiter;
|
|
103
|
+
/**
|
|
104
|
+
* Release a connection back to the pool
|
|
105
|
+
* If there are waiting requests, hand off the connection directly
|
|
106
|
+
*/
|
|
107
|
+
release(pooled: PooledConnection): void;
|
|
108
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The directory schema: where it lives, its index, how this server spells
|
|
3
|
+
* attribute names, the object classes an entity declares and the repair a
|
|
4
|
+
* write needs.
|
|
5
|
+
*/
|
|
6
|
+
import type { SearchOptions, SearchResult } from 'ldapts';
|
|
7
|
+
import type { AttributesList, ModifyRequest } from '../ldapActions';
|
|
8
|
+
import { SchemaIndex } from '../ldapSchema';
|
|
9
|
+
export interface SchemaLogger {
|
|
10
|
+
info(message: string): void;
|
|
11
|
+
warn(message: string): void;
|
|
12
|
+
}
|
|
13
|
+
export type DirectorySearch = (options: SearchOptions, base: string) => Promise<SearchResult | AsyncGenerator<SearchResult>>;
|
|
14
|
+
export interface DirectorySchemaOptions {
|
|
15
|
+
logger: SchemaLogger;
|
|
16
|
+
/** Run a search as the server itself. */
|
|
17
|
+
search: DirectorySearch;
|
|
18
|
+
/** Whether a modify may add the classes it needs. */
|
|
19
|
+
autoRepair: () => boolean;
|
|
20
|
+
/** How long the schema index is kept, in milliseconds. */
|
|
21
|
+
schemaCacheTtl: () => number;
|
|
22
|
+
}
|
|
23
|
+
export default class DirectorySchema {
|
|
24
|
+
private readonly logger;
|
|
25
|
+
private readonly search;
|
|
26
|
+
private readonly autoRepair;
|
|
27
|
+
private readonly schemaCacheTtl;
|
|
28
|
+
/** See {@link declareObjectClasses}. */
|
|
29
|
+
private declaredClasses;
|
|
30
|
+
private schemaCache?;
|
|
31
|
+
private schemaLoading?;
|
|
32
|
+
/** Whether the last schema read failed, so a failure is logged once. */
|
|
33
|
+
private schemaUnavailable;
|
|
34
|
+
/**
|
|
35
|
+
* How this server spells attribute names, by name in lower case: the
|
|
36
|
+
* configuration's, then the loaded schemas'. See {@link canonicalName}.
|
|
37
|
+
*/
|
|
38
|
+
private spellings;
|
|
39
|
+
/** The first name given for each attribute type, to warn of another. */
|
|
40
|
+
private spelledTypes;
|
|
41
|
+
/** Class problems already reported, per entry. See {@link warnOnce}. */
|
|
42
|
+
private classWarnings;
|
|
43
|
+
constructor(options: DirectorySchemaOptions);
|
|
44
|
+
/**
|
|
45
|
+
* Declare the object classes an entity gives the entries of its branch.
|
|
46
|
+
*
|
|
47
|
+
* An entry created by another tool may lack some of them, and a class it
|
|
48
|
+
* lacks can forbid an attribute written here: the organization link of an
|
|
49
|
+
* account made as a bare `inetOrgPerson` is refused by the directory. A
|
|
50
|
+
* modify writing such an attribute adds the auxiliary class that allows
|
|
51
|
+
* it, see {@link classRepair}.
|
|
52
|
+
*
|
|
53
|
+
* @param base branch holding the entity's entries
|
|
54
|
+
* @param classes object classes the entity's schema declares; a schema
|
|
55
|
+
* file may give a single one as a string
|
|
56
|
+
* @param includeBase whether the base is itself one of the entries, as the
|
|
57
|
+
* top organization is
|
|
58
|
+
*/
|
|
59
|
+
declareObjectClasses(base: string, classes: string[] | string, includeBase?: boolean): void;
|
|
60
|
+
/**
|
|
61
|
+
* The classes declared for the branch holding a DN. Several entities may
|
|
62
|
+
* share a branch — the shipped `users`, `posixAccounts` and
|
|
63
|
+
* `sshPublicKeys` schemas all live in `ou=users` — so every declaration at
|
|
64
|
+
* the deepest matching base counts.
|
|
65
|
+
*/
|
|
66
|
+
private declaredFor;
|
|
67
|
+
/**
|
|
68
|
+
* Log a class problem of an entry once, not on every write to it: nothing
|
|
69
|
+
* an administrator can do stops a sync job from touching it again.
|
|
70
|
+
*/
|
|
71
|
+
private warnOnce;
|
|
72
|
+
/**
|
|
73
|
+
* The auxiliary classes a modify needs added: for each attribute it writes
|
|
74
|
+
* that the entry's classes do not allow, a class declared for its branch
|
|
75
|
+
* or by the writer that allows it, provided the entry holds or receives
|
|
76
|
+
* that class's mandatory attributes once the change is applied.
|
|
77
|
+
*
|
|
78
|
+
* Only what the write needs is added. Entities sharing a branch declare
|
|
79
|
+
* classes that are not all meant for every entry: `shadowAccount` is not
|
|
80
|
+
* something every account should be given because it sits in `ou=users`.
|
|
81
|
+
* And a write that the directory would have accepted is never changed.
|
|
82
|
+
*
|
|
83
|
+
* Nothing is added when the change names `objectClass` itself, and a
|
|
84
|
+
* schema or entry that cannot be read skips the repair.
|
|
85
|
+
*
|
|
86
|
+
* @param dn entry being modified
|
|
87
|
+
* @param changes the modification, after the hooks
|
|
88
|
+
* @param extra classes the writer declares, see {@link ModifyOptions}
|
|
89
|
+
* @returns names of the classes to add, or `allowed` when the entry's
|
|
90
|
+
* classes take the change as it is
|
|
91
|
+
*/
|
|
92
|
+
classRepair(dn: string, changes: ModifyRequest, extra?: string[]): Promise<string[] | 'allowed'>;
|
|
93
|
+
/**
|
|
94
|
+
* DN of the subschema entry the root DSE advertises, or the conventional
|
|
95
|
+
* `cn=Subschema` when it advertises none.
|
|
96
|
+
*/
|
|
97
|
+
subschemaDn(): Promise<string>;
|
|
98
|
+
/**
|
|
99
|
+
* The directory schema, kept for `--ldap-raw-schema-cache-ttl`: it is
|
|
100
|
+
* large and changes rarely. The raw browser and the object class repair
|
|
101
|
+
* share it, and concurrent callers share a single read.
|
|
102
|
+
*
|
|
103
|
+
* @returns indexed schema
|
|
104
|
+
*/
|
|
105
|
+
schemaIndex(): Promise<SchemaIndex>;
|
|
106
|
+
/**
|
|
107
|
+
* Say how this server spells attribute names: a schema loaded by a plugin
|
|
108
|
+
* names its attributes as the plugins compare them. The first spelling of
|
|
109
|
+
* a name wins.
|
|
110
|
+
*
|
|
111
|
+
* @param names attribute names
|
|
112
|
+
*/
|
|
113
|
+
knowAttributeNames(names: Iterable<string>): void;
|
|
114
|
+
/**
|
|
115
|
+
* Name each attribute of a write as this server does, in place.
|
|
116
|
+
*
|
|
117
|
+
* The directory takes `MAIL`, `rfc822Mailbox` and `0.9.2342.19200300.100.1.3`
|
|
118
|
+
* for `mail`, while the authorization plugins and the hooks look for
|
|
119
|
+
* `mail`: a write naming the organization link by its OID was judged as
|
|
120
|
+
* if it left the link alone. Each name is resolved through the directory
|
|
121
|
+
* schema — case, aliases, OIDs — and written as the configuration or a
|
|
122
|
+
* loaded schema spells it, or as the directory declares it first.
|
|
123
|
+
*
|
|
124
|
+
* An option names a value of its own (`member;lang-en` is not `member` to
|
|
125
|
+
* the hooks guarding `member`), and is refused, `;binary` aside. Two names
|
|
126
|
+
* for one attribute in one part of a write are refused too: on a modify
|
|
127
|
+
* the directory applies both, and only one would have been judged.
|
|
128
|
+
*
|
|
129
|
+
* Without the schema, the case is still set, and a name given as an OID,
|
|
130
|
+
* which nothing here could resolve, is refused. So is, with or without
|
|
131
|
+
* it, a name that is neither a descriptor nor a numeric OID.
|
|
132
|
+
*
|
|
133
|
+
* @param entry attributes of an add, or one part of a modify
|
|
134
|
+
* @throws BadRequestError on an option, on a name that describes no
|
|
135
|
+
* attribute, or on one attribute named twice
|
|
136
|
+
*/
|
|
137
|
+
canonicalAttributes(entry: AttributesList | undefined): Promise<void>;
|
|
138
|
+
/**
|
|
139
|
+
* {@link canonicalAttributes} for every part of a modify, in place.
|
|
140
|
+
*
|
|
141
|
+
* @param changes the modify
|
|
142
|
+
* @throws BadRequestError as {@link canonicalAttributes}
|
|
143
|
+
*/
|
|
144
|
+
canonicalChanges(changes: ModifyRequest | undefined): Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* The names of one part of a write, as {@link canonicalAttributes} writes
|
|
147
|
+
* them.
|
|
148
|
+
*/
|
|
149
|
+
private canonicalNames;
|
|
150
|
+
/**
|
|
151
|
+
* One attribute description, named as this server does.
|
|
152
|
+
*
|
|
153
|
+
* @param key the name as written, options included
|
|
154
|
+
* @param index the directory schema, when it could be read
|
|
155
|
+
*/
|
|
156
|
+
private canonicalName;
|
|
157
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { SearchOptions, SearchResult } from 'ldapts';
|
|
2
|
+
export interface CacheLogger {
|
|
3
|
+
info(message: string): void;
|
|
4
|
+
}
|
|
5
|
+
export interface SearchCacheOptions {
|
|
6
|
+
max: number;
|
|
7
|
+
/** Milliseconds; 0 switches the cache off. */
|
|
8
|
+
ttl: number;
|
|
9
|
+
logger: CacheLogger;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Short, stable stand-in for a cache key in a log line.
|
|
13
|
+
*
|
|
14
|
+
* The key is built from the search base, which comes from configuration and
|
|
15
|
+
* therefore ultimately from the environment — logging it verbatim is what
|
|
16
|
+
* CodeQL flags as clear-text logging of environment-derived data. A digest
|
|
17
|
+
* carries none of the key's content, but the same key always digests to the
|
|
18
|
+
* same token, so a "cache hit" line can still be matched back to the
|
|
19
|
+
* "cached" line that filled the entry it hit.
|
|
20
|
+
*/
|
|
21
|
+
export declare const digestKey: (key: string) => string;
|
|
22
|
+
/** The DNs that attributes of an entry or a change name as members. */
|
|
23
|
+
export declare const membersNamed: (attributes: Record<string, unknown> | undefined) => string[];
|
|
24
|
+
/** The parts of a change that name members, as `applyModify` reads them. */
|
|
25
|
+
type MembershipChanges = {
|
|
26
|
+
add?: Record<string, unknown>;
|
|
27
|
+
replace?: Record<string, unknown>;
|
|
28
|
+
delete?: string[] | Record<string, unknown>;
|
|
29
|
+
};
|
|
30
|
+
export default class SearchCache {
|
|
31
|
+
readonly enabled: boolean;
|
|
32
|
+
private readonly logger;
|
|
33
|
+
private readonly searchCache;
|
|
34
|
+
/**
|
|
35
|
+
* How many writes this cache has seen.
|
|
36
|
+
*
|
|
37
|
+
* A read can be overtaken by a write: the write lands and `invalidate`
|
|
38
|
+
* finds nothing to drop, because the read has not stored its answer yet —
|
|
39
|
+
* and the read then stores what it read *before* the write, an entry
|
|
40
|
+
* nothing will drop until its TTL runs out. A search remembers the count
|
|
41
|
+
* it started at and stores only if no write happened in between, so a read
|
|
42
|
+
* a write overtook costs a cache entry, never a stale answer.
|
|
43
|
+
*
|
|
44
|
+
* The count is per cache and per write, not per DN: a write to another
|
|
45
|
+
* branch keeps a concurrent read out of the cache too. That is hit rate
|
|
46
|
+
* given up, not freshness.
|
|
47
|
+
*/
|
|
48
|
+
private cacheGeneration;
|
|
49
|
+
constructor(options: SearchCacheOptions);
|
|
50
|
+
get generation(): number;
|
|
51
|
+
/**
|
|
52
|
+
* Generate cache key for LDAP search
|
|
53
|
+
*
|
|
54
|
+
* Every option that changes the answer belongs in here, or a search that
|
|
55
|
+
* asked for more is served what a search that asked for less got: the
|
|
56
|
+
* requested attributes above all, but also the ones that change the shape
|
|
57
|
+
* of what comes back. `timeLimit` is deliberately absent — it bounds how
|
|
58
|
+
* long the server may spend, not what it returns.
|
|
59
|
+
*/
|
|
60
|
+
key(base: string, opts: SearchOptions): string;
|
|
61
|
+
/** The cached answer, as a copy the caller may edit. */
|
|
62
|
+
get(key: string): SearchResult | undefined;
|
|
63
|
+
/** Store a copy of the answer; later callers cannot edit what was stored. */
|
|
64
|
+
set(key: string, result: SearchResult): void;
|
|
65
|
+
/**
|
|
66
|
+
* Drop every cached read of `dn`, and of everything below it.
|
|
67
|
+
*
|
|
68
|
+
* Called after each write, so the next read of what just changed goes to
|
|
69
|
+
* the directory. It compares DNs the way the directory does (see
|
|
70
|
+
* {@link foldDn}), so a write spelling a DN differently from the read that
|
|
71
|
+
* cached it still drops it; and it drops the subtree, because a rename or
|
|
72
|
+
* a move re-parents a whole branch in one operation — renaming an
|
|
73
|
+
* organization changes the DN of every entry under it — leaving the cached
|
|
74
|
+
* reads of those children filed under DNs that no longer exist. A leaf has
|
|
75
|
+
* nothing below it and the extra test costs nothing.
|
|
76
|
+
*/
|
|
77
|
+
invalidate(dn: string): void;
|
|
78
|
+
/**
|
|
79
|
+
* Drop every cached read holding a value that names `dn`, or an entry
|
|
80
|
+
* below it.
|
|
81
|
+
*
|
|
82
|
+
* The directory mirrors some writes onto other entries than the one
|
|
83
|
+
* written: the `memberof` overlay keeps each member's `memberOf` in step
|
|
84
|
+
* with the group's `member` / `uniqueMember`, and `refint` rewrites or
|
|
85
|
+
* removes the references to an entry renamed or deleted. Those entries'
|
|
86
|
+
* cached reads are what {@link invalidate} cannot find, and they name
|
|
87
|
+
* the entry written: the members of a group name it in their `memberOf`.
|
|
88
|
+
* A member a write adds names it only once the write has landed; the
|
|
89
|
+
* caller drops those by their own DN.
|
|
90
|
+
*
|
|
91
|
+
* Every attribute is looked at, not `memberOf` alone: which attributes an
|
|
92
|
+
* overlay maintains is the deployment's configuration. A read naming `dn`
|
|
93
|
+
* for another reason is dropped too, which costs a miss.
|
|
94
|
+
*/
|
|
95
|
+
invalidateReferences(dn: string): void;
|
|
96
|
+
/**
|
|
97
|
+
* Drop what a change of a group's members makes stale: the group, what
|
|
98
|
+
* names it, and the members the change names. A replace or a delete of
|
|
99
|
+
* every value removes members the change no longer names; their cached
|
|
100
|
+
* reads are found by the group's DN in their `memberOf`.
|
|
101
|
+
*/
|
|
102
|
+
invalidateMembership(dn: string, changes: MembershipChanges): void;
|
|
103
|
+
/**
|
|
104
|
+
* Get a sorted signature for an attribute list.
|
|
105
|
+
*
|
|
106
|
+
* The signature is JSON, not a separator-joined string: an attribute
|
|
107
|
+
* description may contain any character that could stand for a separator,
|
|
108
|
+
* so joining would let `['cn|mail']` and `['cn', 'mail']` share one.
|
|
109
|
+
*/
|
|
110
|
+
private getAttributeSignature;
|
|
111
|
+
}
|
|
112
|
+
export {};
|