ldap-rest 1.2.0 → 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/projection.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 +11 -2
- 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 +37 -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 +4 -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/types.d.ts +2 -1
- 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 +30 -3
- 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 +31 -24
- package/docs/usage/operations.md +19 -4
- package/docs/usage/plugins/integrations/groups.md +2 -2
- package/docs/usage/plugins/integrations/scim.md +12 -1
- package/docs/usage/plugins/integrations/spaces.md +1 -1
- 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 +207 -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
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"../../abstract/plugin.js";import{BadRequestError as
|
|
1
|
+
import{Type as e}from"@sinclair/typebox";import t from"../../abstract/plugin.js";import{BadRequestError as s,NotFoundError as r,ConflictError as a}from"../../lib/errors.js";import{changeContext as o}from"../../lib/changeContext.js";import{extractLdapCode as i}from"../../lib/ldapCodes.js";import{escapeLdapFilter as n,isDnInBranch as h}from"../../lib/utils.js";import{scimOptionsOf as c}from"../scim/scim.options.js";import{lifecycleAttributes as l,isTombstone as d,first as f,valueOf as m,parseDeletedAt as p,formatDeletedAt as u}from"./lifecycleAttributes.js";import{twakeTombstoneOptions as w,twakeTombstoneOptionsOf as g}from"./twakeTombstone.options.js";import{defineRoute as b}from"../../lib/route.js";const y=/^[A-Za-z0-9_.-]{1,64}$/,_=e.Object({dn:e.String(),force:e.Optional(e.Boolean())}),k=e.Object({success:e.Boolean()});class TwakeTombstone extends t{static optionDefinitions=w;name="twakeTombstone";roles=["consistency","api"];attrs;patterns;defaultReason;reasonHeader;reasons;clearAttributes;eraseMinAge;groupBases;scimPrefix;reasonFor=new Map;erasing=new Set;constructor(e){super(e);const t=g(e.config);if(this.attrs=l(e.config),!this.attrs.deleted)throw new Error(`${this.name}: --twake-lifecycle-deleted-attribute is required`);this.patterns=t.twake_tombstone_dn.map(e=>new RegExp(e,"i")),this.reasons=t.twake_tombstone_reasons,this.defaultReason=this.checkReason(t.twake_tombstone_default_reason),this.reasonHeader=t.twake_tombstone_reason_header.toLowerCase(),this.clearAttributes=t.twake_tombstone_clear_attributes,this.eraseMinAge=t.twake_tombstone_erase_min_age,this.groupBases=t.twake_tombstone_group_bases.length?t.twake_tombstone_group_bases:[e.config.ldap_group_base||e.config.ldap_base||""],this.scimPrefix=c(e.config).scim_prefix,0===this.patterns.length&&this.logger.warn(`${this.name}: --twake-tombstone-dn is empty, every delete stays a delete`)}afterLoad(){for(const e of["ldapdeleterequest","ldapaddrequest"]){const t=this.hooks?.[e];t&&this.hookBus.toLast(e,t)}}hooks={ldapdeleterequest:async([e,t])=>{const s=[];for(const r of Array.isArray(e)?e:[e]){if(this.keeps(r)){const e=await this.read(r);if(e){await this.writeTombstone(r,e,this.reasonOf(r,t),t);continue}}s.push(r)}return[s,t]},ldapaddrequest:async([e,t,s])=>{if(this.isScim(s)&&this.matches(e)){const t=await this.read(e);t&&d(t,this.attrs)&&await this.replace(e,t,s)}return[e,t,s]},ldapsearchrequest:([e,t,s])=>{if(!this.isScim(s))return[e,t,s];const r=t.filter||"(objectClass=*)",a=`(!(${this.attrs.deleted}=${n(this.attrs.deletedValue)}))`;return[e,{...t,filter:`(&${String(r).startsWith("(")?String(r):`(${String(r)})`}${a})`},s]}};api(e){b(e,{method:"post",path:`${this.config.api_prefix}/v1/twake/tombstones/erase`,body:_,responses:{200:k},handler:async({req:e,res:t,body:s})=>{await this.erase(s.dn,{force:!0===s.force,req:e}),t.json({success:!0})}})}async tombstone(e,t,r){if(!this.matches(e))throw new s(`${e} is not an entry that becomes a tombstone`);this.reasonFor.set(e,this.checkReason(t));try{await this.server.ldap.delete(e,r)}finally{this.reasonFor.delete(e)}}async erase(e,{force:t=!1,req:s}={}){await this.server.ldap.authorize("ldapdeleterequest",[e,s]);const o=await this.read(e);if(!o||!d(o,this.attrs))throw new r(`No tombstone at ${e}`);if(!t){const t=f(m(o,this.attrs.deletedAt)),s=t&&p(t,this.attrs.deletedAtFormat);if(!s)throw new a(`${e} holds no readable deletion date (--twake-lifecycle-deleted-at-attribute); force it to erase now`);if(Date.now()-s.getTime()<1e3*this.eraseMinAge)throw new a(`${e} was deleted less than ${this.eraseMinAge} seconds ago; force it to erase now`)}this.erasing.add(e);try{await this.server.ldap.delete(e,s)}finally{this.erasing.delete(e)}await this.leaveGroups(e),this.logger.info({plugin:this.name,event:"erase",dn:e,force:t})}keeps(e){return!this.erasing.has(e)&&this.matches(e)}assertComposition(){if(!this.server.loadedPlugins.trash)return;const e=String(this.config.trash_watched_bases||"").split(";").map(e=>e.trim()).filter(Boolean),t=this.patterns.filter(t=>{const s=t.source.replace(/\$$/,"");return 0===e.length||e.some(e=>h(s,e)||h(e,s))});if(t.length)throw new Error(`${this.name}: core/ldap/trash watches the branch of ${t.map(e=>e.source).join(", ")}, and would move those entries instead of leaving a tombstone. Leave that branch out of --trash-watched-bases, or load one of the two plugins only`)}async replace(e,t,s){const r=await this.groupsOf(e),a=this.config.group_dummy_user,o=new Set(a?await this.groupsOf(a):[]);await this.erase(e,{force:!0,req:s});const i=s.res;if(!i)return;const n=i.end.bind(i);i.end=(...s)=>{i.end=n;const a=n(...s);return this.restore(e,t,r,o).catch(t=>this.logger.error({plugin:this.name,event:"restore",dn:e,error:String(t)})),a}}async restore(e,t,s,r){if(await this.read(e))return;const a=Object.fromEntries(Object.entries(t).filter(([e,t])=>"dn"!==e&&!(Array.isArray(t)&&0===t.length)));await this.server.ldap.system.add(e,a);const o=this.config.group_dummy_user;for(const t of s)await this.server.ldap.system.modify(t,{add:{member:e}}),o&&!r.has(t)&&await this.server.ldap.system.modify(t,{delete:{member:o}}).catch(e=>{if(16!==i(e))throw e});this.logger.warn({plugin:this.name,event:"restore",dn:e})}matches(e){return this.patterns.some(t=>t.test(e))}isScim(e){const t=e?.originalUrl??e?.url;return"string"==typeof t&&t.startsWith(this.scimPrefix)}checkReason(e){if(!(this.reasons.length?this.reasons.includes(e):y.test(e)))throw new s(`Unknown deletion reason "${e}"${this.reasons.length?`; known: ${this.reasons.join(", ")}`:""}`);return e}reasonOf(e,t){const s=this.reasonFor.get(e);if(s)return s;const r=t?.headers?.[this.reasonHeader],a=Array.isArray(r)?r[0]:r;return a?this.checkReason(a):this.defaultReason}async read(e){try{return(await this.server.ldap.system.search({paged:!1,scope:"base",attributes:["*",this.attrs.lock]},e)).searchEntries[0]}catch(e){if(32===i(e))return;throw e}}async writeTombstone(e,t,s,r){const{attrs:a}=this;if(d(t,a))return this.hookBus.notify("twakedeletionreplay",e,t,o(r)),void this.logger.info({plugin:this.name,event:"replay",dn:e});const i={[a.deleted]:a.deletedValue,[a.lock]:a.lockValue};a.deletedAt&&(i[a.deletedAt]=u(new Date,a.deletedAtFormat)),a.reason&&(i[a.reason]=s);const n=this.clearAttributes.filter(e=>void 0!==m(t,e));await this.server.ldap.modify(e,n.length?{replace:i,delete:n}:{replace:i},void 0,{context:o(r)}),this.logger.info({plugin:this.name,event:"tombstone",dn:e,reason:s})}async groupsOf(e){const t=[];for(const s of this.groupBases.filter(Boolean))try{const r=await this.server.ldap.system.search({paged:!1,scope:"sub",filter:`(member=${n(e)})`,attributes:["dn"]},s);t.push(...r.searchEntries.map(e=>e.dn))}catch(e){if(32!==i(e))throw e}return t}async leaveGroups(e){for(const t of await this.groupsOf(e))await this.leaveGroup(t,e)}async leaveGroup(e,t){const s=this.config.group_dummy_user;let r=!1;for(let a=0;;a++)try{return void await this.server.ldap.system.modify(e,r?{add:{member:s},delete:{member:t}}:{delete:{member:t}})}catch(e){const t=i(e);if(16===t)return;if(a>=2)throw e;if(65===t&&s)r=!0;else{if(20!==t)throw e;r=!1}}}}export{_ as EraseTombstoneBody,k as EraseTombstoneResponse,TwakeTombstone as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{defineOptions as _,opt as t,readOptions as e}from"../../config/options.js";const a=_({twake_group_base:t.string({env:"DM_TWAKE_GROUP_BASE",default:""}),twake_group_user_base:t.string({env:"DM_TWAKE_GROUP_USER_BASE",default:""}),twake_group_organization_dn:t.string({env:"DM_TWAKE_GROUP_ORGANIZATION_DN",default:""}),twake_group_organization_status_attribute:t.string({env:"DM_TWAKE_GROUP_ORGANIZATION_STATUS_ATTRIBUTE",default:"twakeOrgStatus"}),twake_group_organization_deleted_value:t.string({env:"DM_TWAKE_GROUP_ORGANIZATION_DELETED_VALUE",default:"deleted"}),twake_group_display_name_attribute:t.string({env:"DM_TWAKE_GROUP_DISPLAY_NAME_ATTRIBUTE",default:"twakeDisplayName"}),twake_group_color_attribute:t.string({env:"DM_TWAKE_GROUP_COLOR_ATTRIBUTE",default:"twakeGroupColor"}),twake_group_created_at_attribute:t.string({env:"DM_TWAKE_GROUP_CREATED_AT_ATTRIBUTE",default:"twakeCreatedAt"}),twake_group_max_page_limit:t.number({env:"DM_TWAKE_GROUP_MAX_PAGE_LIMIT",default:1e3,min:1}),twake_group_member_fields:t.json({env:"DM_TWAKE_GROUP_MEMBER_FIELDS",default:{}})});function twakeGroupsOptionsOf(_){return e(a,_)}export{a as twakeGroupsOptions,twakeGroupsOptionsOf};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{defineOptions as _,opt as e,readOptions as t}from"../../config/options.js";const n=_({twake_instance_dn:e.array({env:"DM_TWAKE_INSTANCE_DN",default:[],kind:"dns"}),twake_instance_skip_attribute:e.string({env:"DM_TWAKE_INSTANCE_SKIP_ATTRIBUTE",default:""}),twake_instance_skip_value:e.string({env:"DM_TWAKE_INSTANCE_SKIP_VALUE",default:""}),twake_instance_provider:e.string({env:"DM_TWAKE_INSTANCE_PROVIDER",default:"cloudery"}),twake_instance_id:e.string({env:"DM_TWAKE_INSTANCE_ID",default:"{uid}"}),twake_instance_fqdn_attribute:e.string({env:"DM_TWAKE_INSTANCE_FQDN_ATTRIBUTE",default:"twakeWorkspaceUrl"}),twake_instance_sent_attribute:e.string({env:"DM_TWAKE_INSTANCE_SENT_ATTRIBUTE",default:"twakeCreatedEventAt"}),twake_instance_locale:e.string({env:"DM_TWAKE_INSTANCE_LOCALE",default:""}),twake_instance_timeout:e.number({env:"DM_TWAKE_INSTANCE_TIMEOUT",default:3e4,min:1}),twake_instance_cloudery_url:e.string({env:"DM_TWAKE_INSTANCE_CLOUDERY_URL",default:""}),twake_instance_cloudery_token:e.string({env:"DM_TWAKE_INSTANCE_CLOUDERY_TOKEN",default:""}),twake_instance_cloudery_offer:e.string({env:"DM_TWAKE_INSTANCE_CLOUDERY_OFFER",default:"b2b_twake_default",emptyIsValue:!0}),twake_instance_cloudery_domain:e.string({env:"DM_TWAKE_INSTANCE_CLOUDERY_DOMAIN",default:""}),twake_instance_cloudery_organization_offer:e.string({env:"DM_TWAKE_INSTANCE_CLOUDERY_ORGANIZATION_OFFER",default:""}),twake_instance_cozy_url:e.string({env:"DM_TWAKE_INSTANCE_COZY_URL",default:""}),twake_instance_cozy_user:e.string({env:"DM_TWAKE_INSTANCE_COZY_USER",default:"admin"}),twake_instance_cozy_passphrase:e.string({env:"DM_TWAKE_INSTANCE_COZY_PASSPHRASE",default:""}),twake_instance_cozy_domain:e.string({env:"DM_TWAKE_INSTANCE_COZY_DOMAIN",default:""}),twake_instance_cozy_context:e.string({env:"DM_TWAKE_INSTANCE_COZY_CONTEXT",default:"default",emptyIsValue:!0}),twake_instance_cozy_apps:e.string({env:"DM_TWAKE_INSTANCE_COZY_APPS",default:"home,drive,settings,notes,dataproxy",emptyIsValue:!0}),twake_instance_cozy_org_id:e.string({env:"DM_TWAKE_INSTANCE_COZY_ORG_ID",default:""}),twake_instance_cozy_org_domain:e.string({env:"DM_TWAKE_INSTANCE_COZY_ORG_DOMAIN",default:""}),twake_instance_auth_exchange:e.string({env:"DM_TWAKE_INSTANCE_AUTH_EXCHANGE",default:"auth"}),twake_instance_b2b_exchange:e.string({env:"DM_TWAKE_INSTANCE_B2B_EXCHANGE",default:"b2b"}),twake_instance_user_created_key:e.string({env:"DM_TWAKE_INSTANCE_USER_CREATED_KEY",default:"user.created"}),twake_instance_organization_created_key:e.string({env:"DM_TWAKE_INSTANCE_ORGANIZATION_CREATED_KEY",default:"organization.created"}),twake_instance_workplace_created_key:e.string({env:"DM_TWAKE_INSTANCE_WORKPLACE_CREATED_KEY",default:"workplace.created"}),twake_instance_queue:e.string({env:"DM_TWAKE_INSTANCE_QUEUE",default:"workplace.created.ldap-rest"}),twake_instance_organization_base:e.string({env:"DM_TWAKE_INSTANCE_ORGANIZATION_BASE",default:""}),twake_instance_organization_id_attribute:e.string({env:"DM_TWAKE_INSTANCE_ORGANIZATION_ID_ATTRIBUTE",default:"ou"}),twake_instance_organization_domain_attribute:e.string({env:"DM_TWAKE_INSTANCE_ORGANIZATION_DOMAIN_ATTRIBUTE",default:"twakeDomain"}),twake_instance_organization_name_attribute:e.string({env:"DM_TWAKE_INSTANCE_ORGANIZATION_NAME_ATTRIBUTE",default:"description"}),twake_instance_organization_fqdn_attribute:e.string({env:"DM_TWAKE_INSTANCE_ORGANIZATION_FQDN_ATTRIBUTE",default:""})});function twakeInstancesOptionsOf(_){return t(n,_)}export{n as twakeInstancesOptions,twakeInstancesOptionsOf};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{defineOptions as e,opt as _,readOptions as t}from"../../config/options.js";const E=e({twake_lifecycle_role_attribute:_.string({env:"DM_TWAKE_LIFECYCLE_ROLE_ATTRIBUTE",default:""}),twake_lifecycle_lock_attribute:_.string({env:"DM_TWAKE_LIFECYCLE_LOCK_ATTRIBUTE",default:""}),twake_lifecycle_lock_value:_.string({env:"DM_TWAKE_LIFECYCLE_LOCK_VALUE",default:""}),twake_lifecycle_deleted_attribute:_.string({env:"DM_TWAKE_LIFECYCLE_DELETED_ATTRIBUTE",default:""}),twake_lifecycle_deleted_value:_.string({env:"DM_TWAKE_LIFECYCLE_DELETED_VALUE",default:"TRUE"}),twake_lifecycle_deleted_at_attribute:_.string({env:"DM_TWAKE_LIFECYCLE_DELETED_AT_ATTRIBUTE",default:""}),twake_lifecycle_deleted_at_format:_.string({env:"DM_TWAKE_LIFECYCLE_DELETED_AT_FORMAT",default:"iso8601"}),twake_lifecycle_reason_attribute:_.string({env:"DM_TWAKE_LIFECYCLE_REASON_ATTRIBUTE",default:""}),twake_lifecycle_rules:_.string({env:"DM_TWAKE_LIFECYCLE_RULES",default:""}),twake_lifecycle_member_attribute:_.string({env:"DM_TWAKE_LIFECYCLE_MEMBER_ATTRIBUTE",default:"member"})});function twakeLifecycleOptionsOf(e){return t(E,e)}export{E as twakeLifecycleOptions,twakeLifecycleOptionsOf};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{defineOptions as e,opt as t,readOptions as _}from"../../config/options.js";const a=e({twake_space_base:t.string({env:"DM_TWAKE_SPACE_BASE",default:""}),twake_space_class:t.array({env:"DM_TWAKE_SPACE_CLASS",default:["top","twakeSpace"],kind:"identifiers"}),twake_space_display_name_attribute:t.string({env:"DM_TWAKE_SPACE_DISPLAY_NAME_ATTRIBUTE",default:"twakeDisplayName"}),twake_space_admin_attribute:t.string({env:"DM_TWAKE_SPACE_ADMIN_ATTRIBUTE",default:"twakeSpaceAdmin"}),twake_space_editor_attribute:t.string({env:"DM_TWAKE_SPACE_EDITOR_ATTRIBUTE",default:"twakeSpaceEditor"}),twake_space_viewer_attribute:t.string({env:"DM_TWAKE_SPACE_VIEWER_ATTRIBUTE",default:"twakeSpaceViewer"}),twake_space_user_role_attribute:t.string({env:"DM_TWAKE_SPACE_USER_ROLE_ATTRIBUTE",default:""}),twake_space_exchange:t.string({env:"DM_TWAKE_SPACE_EXCHANGE",default:"space"}),twake_space_sync_queue:t.string({env:"DM_TWAKE_SPACE_SYNC_QUEUE",default:"twake.space.sync.requested.ldap-rest"})});function twakeSpacesOptionsOf(e){return _(a,e)}export{a as twakeSpacesOptions,twakeSpacesOptionsOf};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{defineOptions as e,opt as t,readOptions as _}from"../../config/options.js";const n=e({twake_tombstone_dn:t.array({env:"DM_TWAKE_TOMBSTONE_DN",default:[],kind:"dns"}),twake_tombstone_default_reason:t.string({env:"DM_TWAKE_TOMBSTONE_DEFAULT_REASON",default:"deleted"}),twake_tombstone_reason_header:t.string({env:"DM_TWAKE_TOMBSTONE_REASON_HEADER",default:"x-deletion-reason"}),twake_tombstone_reasons:t.array({env:"DM_TWAKE_TOMBSTONE_REASONS",default:[],kind:"identifiers"}),twake_tombstone_clear_attributes:t.array({env:"DM_TWAKE_TOMBSTONE_CLEAR_ATTRIBUTES",default:[],kind:"identifiers"}),twake_tombstone_erase_min_age:t.number({env:"DM_TWAKE_TOMBSTONE_ERASE_MIN_AGE",default:2592e3}),twake_tombstone_group_bases:t.array({env:"DM_TWAKE_TOMBSTONE_GROUP_BASES",default:[],kind:"dns"})});function twakeTombstoneOptionsOf(e){return _(n,e)}export{n as twakeTombstoneOptions,twakeTombstoneOptionsOf};
|
package/dist/plugins/weblogs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"../abstract/plugin.js";import{requestId as r}from"../lib/changeContext.js";import{runWithLogContext as o}from"../lib/requestContext.js";class WebLogs extends e{
|
|
1
|
+
import e from"../abstract/plugin.js";import{requestId as r}from"../lib/changeContext.js";import{runWithLogContext as o}from"../lib/requestContext.js";class WebLogs extends e{name="weblogs";roles=["logging"];api(e){e.use((e,r,o)=>{let t=!0;this.logger.debug(`Incoming request: ${e.method} ${e.originalUrl}`);const s=Date.now();r.on("finish",()=>{const o={};e.proxyAuthUser?o.user=e.proxyAuthUser:e.user&&(o.user=e.user),t&&this.log(e,r,s,o),t=!1}),r.on("error",o=>{t&&this.log(e,r,s,{error:o.message}),t=!1}),r.on("close",()=>{r.writableEnded||(t&&this.log(e,r,s,{error:`Connection closed before response was sent for ${e.method} ${e.originalUrl}`}),t=!1)}),o()})}log(e,t,s,n){const i=Date.now()-s,l={...e.logDetails||{},requestId:r(e),method:e.method,url:e.originalUrl,status:t.statusCode,duration:i,ip:this.getClientIp(e),...n};o({requestId:l.requestId},()=>this.logger.notice(l))}getClientIp(e){const r=e.headers["x-forwarded-for"];if(r){return(Array.isArray(r)?r[0]:r).split(",")[0].trim()}return e.socket.remoteAddress||"unknown"}}export{WebLogs as default};
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module abstract/ldapFlat
|
|
3
|
-
* @author Xavier Guimard <xguimard@linagora.com>
|
|
4
|
-
*
|
|
5
|
-
* Abstract class to manage LDAP entries in a flat branch
|
|
6
|
-
* - add/delete entries
|
|
7
|
-
* - modify entries
|
|
8
|
-
* - validate with schema
|
|
9
|
-
*/
|
|
10
1
|
import type { Express, Request, Response } from 'express';
|
|
11
2
|
import type { DM } from '../bin';
|
|
12
3
|
import type ldapActions from '../lib/ldapActions';
|
|
@@ -210,6 +201,26 @@ export interface LdapFlatConfig {
|
|
|
210
201
|
* dn: uid=bob,ou=users,dc=example,dc=com
|
|
211
202
|
* referencesUpdated: 3
|
|
212
203
|
*/
|
|
204
|
+
export declare const FlatListQuery: import("@sinclair/typebox").TObject<{
|
|
205
|
+
match: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
206
|
+
attribute: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
207
|
+
attributes: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
208
|
+
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
209
|
+
}>;
|
|
210
|
+
export declare const FlatEntryParams: import("@sinclair/typebox").TObject<{
|
|
211
|
+
id: import("@sinclair/typebox").TString;
|
|
212
|
+
}>;
|
|
213
|
+
export declare const FlatMoveBody: import("@sinclair/typebox").TObject<{
|
|
214
|
+
targetOrgDn: import("@sinclair/typebox").TString;
|
|
215
|
+
}>;
|
|
216
|
+
export declare const FlatMoveResponse: import("@sinclair/typebox").TObject<{
|
|
217
|
+
success: import("@sinclair/typebox").TBoolean;
|
|
218
|
+
departmentPath: import("@sinclair/typebox").TString;
|
|
219
|
+
departmentLink: import("@sinclair/typebox").TString;
|
|
220
|
+
}>;
|
|
221
|
+
export declare const FlatRenameBody: import("@sinclair/typebox").TObject<{
|
|
222
|
+
newId: import("@sinclair/typebox").TString;
|
|
223
|
+
}>;
|
|
213
224
|
export default abstract class LdapFlat extends DmPlugin {
|
|
214
225
|
base: string;
|
|
215
226
|
ldap: ldapActions;
|
|
@@ -220,6 +231,12 @@ export default abstract class LdapFlat extends DmPlugin {
|
|
|
220
231
|
singularName: string;
|
|
221
232
|
pluralName: string;
|
|
222
233
|
hookPrefix: string;
|
|
234
|
+
/** `hookBus`, raising this entity's own hooks */
|
|
235
|
+
private readonly ownHooks;
|
|
236
|
+
/**
|
|
237
|
+
* @param server the server
|
|
238
|
+
* @param config the entity, its hook prefix included
|
|
239
|
+
*/
|
|
223
240
|
constructor(server: DM, config: LdapFlatConfig);
|
|
224
241
|
/**
|
|
225
242
|
* Attributes never sent back over the API. A manager may reset a password
|
|
@@ -421,6 +438,7 @@ export default abstract class LdapFlat extends DmPlugin {
|
|
|
421
438
|
truncated: boolean;
|
|
422
439
|
}>;
|
|
423
440
|
searchEntriesByName(name: string, partial?: boolean, attrs?: string[]): Promise<LdapList>;
|
|
424
|
-
|
|
441
|
+
/** @returns the entry to write, under the schema's spellings */
|
|
442
|
+
validateNewEntry(dn: string, entry: AttributesList): Promise<AttributesList>;
|
|
425
443
|
validateChanges(dn: string, changes: ModifyRequest): Promise<boolean>;
|
|
426
444
|
}
|
|
@@ -3,25 +3,28 @@
|
|
|
3
3
|
* @author Xavier Guimard <xguimard@linagora.com>
|
|
4
4
|
*/
|
|
5
5
|
import type { Express } from 'express';
|
|
6
|
-
import fetch, { type RequestInit, type Response } from 'node-fetch';
|
|
7
6
|
import type winston from 'winston';
|
|
8
7
|
import type { Config, DM } from '../bin';
|
|
9
8
|
import type { Hooks, MaybePromise } from '../hooks';
|
|
9
|
+
import type HookBus from '../lib/hookBus';
|
|
10
10
|
import type { HealthStatus } from '../lib/health';
|
|
11
|
+
import { type OptionsOf, type OptionSet } from '../config/options';
|
|
11
12
|
export { asyncHandler, escapeDnValue, unescapeDnValue, escapeLdapFilter, validateDnValue, } from '../lib/utils';
|
|
13
|
+
export { defineRoute, registeredRoutes } from '../lib/route';
|
|
12
14
|
export { HttpError, BadRequestError, UnauthorizedError, ForbiddenError, NotFoundError, ConflictError, UriTooLongError, TooManyRequestsError, BadGatewayError, ServiceUnavailableError, GatewayTimeoutError, } from '../lib/errors';
|
|
13
15
|
export type { HealthStatus };
|
|
14
16
|
export type Role = 'auth' | 'authz' | 'protect' | 'api' | 'logging' | 'demo' | 'consistency' | 'configurable';
|
|
15
17
|
export default abstract class DmPlugin {
|
|
18
|
+
/** Options this plugin declares, see `docs/plugin-development/options.md` */
|
|
19
|
+
static optionDefinitions: OptionSet;
|
|
16
20
|
/**
|
|
17
21
|
* Properties inherited from parent (DM)
|
|
18
22
|
*/
|
|
19
23
|
server: DM;
|
|
20
24
|
config: Config;
|
|
25
|
+
hookBus: HookBus;
|
|
21
26
|
logger: winston.Logger;
|
|
22
|
-
registeredHooks:
|
|
23
|
-
[K in keyof Hooks]?: Function[];
|
|
24
|
-
};
|
|
27
|
+
registeredHooks: Hooks;
|
|
25
28
|
/**
|
|
26
29
|
* Interfaces
|
|
27
30
|
*/
|
|
@@ -76,12 +79,14 @@ export default abstract class DmPlugin {
|
|
|
76
79
|
* Call another service: `fetch`, timed into the outbound metrics under
|
|
77
80
|
* this plugin's name, see `docs/usage/operations.md#metrics`.
|
|
78
81
|
*/
|
|
79
|
-
protected fetch(url: Parameters<typeof fetch>[0], init?: RequestInit): Promise<Response>;
|
|
82
|
+
protected fetch(url: Parameters<typeof globalThis.fetch>[0], init?: RequestInit): Promise<Response>;
|
|
80
83
|
/**
|
|
81
84
|
* Constructor
|
|
82
85
|
* @param server DM object
|
|
83
86
|
*/
|
|
84
87
|
constructor(server: DM);
|
|
88
|
+
/** Values of the options this plugin declares, read and checked */
|
|
89
|
+
options<T extends OptionSet>(definitions: T): OptionsOf<T>;
|
|
85
90
|
/**
|
|
86
91
|
* Uniq ID, to be used when calling hooks
|
|
87
92
|
*
|
|
@@ -9,6 +9,18 @@ import type { DM } from '../bin';
|
|
|
9
9
|
import { type HealthStatus } from '../lib/health';
|
|
10
10
|
import type { AttributesList } from '../lib/ldapActions';
|
|
11
11
|
import DmPlugin, { type Role } from './plugin';
|
|
12
|
+
/** What a Twake plugin needs to reach its WebAdmin API */
|
|
13
|
+
export interface TwakeConnection {
|
|
14
|
+
url: string;
|
|
15
|
+
token: string;
|
|
16
|
+
concurrency: number;
|
|
17
|
+
/** Option holding the URL, named in the startup error when it is empty */
|
|
18
|
+
urlOption: string;
|
|
19
|
+
/** Option holding the token, named in the warning when it is empty */
|
|
20
|
+
tokenOption: string;
|
|
21
|
+
/** Option holding the concurrency, named when it is not a positive integer */
|
|
22
|
+
concurrencyOption: string;
|
|
23
|
+
}
|
|
12
24
|
/**
|
|
13
25
|
* Abstract base class for all Twake-related plugins (James, Calendar, Matrix, etc.)
|
|
14
26
|
* Consolidates common patterns for HTTP communication with Twake WebAdmin APIs
|
|
@@ -26,11 +38,9 @@ export declare abstract class TwakePlugin extends DmPlugin {
|
|
|
26
38
|
/**
|
|
27
39
|
* Constructor - initializes common Twake plugin configuration
|
|
28
40
|
* @param server DM server instance
|
|
29
|
-
* @param
|
|
30
|
-
* @param tokenConfig Configuration key for WebAdmin token
|
|
31
|
-
* @param concurrencyConfig Configuration key for concurrency limit
|
|
41
|
+
* @param connection WebAdmin URL, token and concurrency
|
|
32
42
|
*/
|
|
33
|
-
constructor(server: DM,
|
|
43
|
+
constructor(server: DM, connection: TwakeConnection);
|
|
34
44
|
/**
|
|
35
45
|
* Create HTTP headers with optional Authorization token
|
|
36
46
|
* @param contentType Optional Content-Type header value
|
package/dist/src/bin/index.d.ts
CHANGED
|
@@ -19,10 +19,11 @@ import type { Hooks } from '../hooks';
|
|
|
19
19
|
import ldapActions from '../lib/ldapActions';
|
|
20
20
|
import type DmPlugin from '../abstract/plugin';
|
|
21
21
|
import AuthBase from '../lib/auth/base';
|
|
22
|
+
import HookBus from '../lib/hookBus';
|
|
22
23
|
export type { Config };
|
|
23
24
|
export * from '../lib/utils';
|
|
24
25
|
export { asyncHandler } from '../lib/utils';
|
|
25
|
-
export { HttpError, BadRequestError, UnauthorizedError, ForbiddenError, NotFoundError, ConflictError, UriTooLongError, TooManyRequestsError, BadGatewayError, ServiceUnavailableError, GatewayTimeoutError, } from '../lib/errors';
|
|
26
|
+
export { HttpError, BadRequestError, UnauthorizedError, ForbiddenError, AuthzRefusedError, NotFoundError, ConflictError, UriTooLongError, TooManyRequestsError, BadGatewayError, ServiceUnavailableError, GatewayTimeoutError, } from '../lib/errors';
|
|
26
27
|
/**
|
|
27
28
|
* @class DM
|
|
28
29
|
*/
|
|
@@ -33,9 +34,13 @@ export declare class DM {
|
|
|
33
34
|
/** The listener, once `run()` opened it; shared by the `withConfig` views */
|
|
34
35
|
get server(): import('http').Server | undefined;
|
|
35
36
|
set server(server: import('http').Server | undefined);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
/**
|
|
38
|
+
* What each hook's subscribers do, as a plugin declares them. The type
|
|
39
|
+
* names every hook: raising or subscribing to one goes through `hookBus`,
|
|
40
|
+
* which checks the arguments.
|
|
41
|
+
*/
|
|
42
|
+
hooks: Hooks;
|
|
43
|
+
readonly hookBus: HookBus;
|
|
39
44
|
loadedPlugins: {
|
|
40
45
|
[key: string]: DmPlugin;
|
|
41
46
|
};
|
|
@@ -87,6 +92,8 @@ export declare class DM {
|
|
|
87
92
|
* whose name is taken. A Map, so that `withConfig` views share it.
|
|
88
93
|
*/
|
|
89
94
|
private readonly pluginLoads;
|
|
95
|
+
/** CLI arguments no declared option matched, reported after the logger exists */
|
|
96
|
+
private readonly unknownCliOptions;
|
|
90
97
|
/**
|
|
91
98
|
* The request hooks of the authorization plugins, which `registerPlugin`
|
|
92
99
|
* keeps ahead of every other plugin's. See {@link AUTHZ_FIRST_HOOKS}.
|
|
@@ -123,6 +130,12 @@ export declare class DM {
|
|
|
123
130
|
* @returns a server view backed by this instance
|
|
124
131
|
*/
|
|
125
132
|
withConfig(config: Config): DM;
|
|
133
|
+
/**
|
|
134
|
+
* Name the CLI arguments and `DM_*` variables nothing declares, among the
|
|
135
|
+
* plugins `--plugins` loaded and their `optionDefinitions`. A host that
|
|
136
|
+
* registers its own plugins after `ready` is not covered.
|
|
137
|
+
*/
|
|
138
|
+
private reportUnknownOptions;
|
|
126
139
|
/**
|
|
127
140
|
* Mount the authentication dispatcher, once, before the first plugin that
|
|
128
141
|
* can register a route.
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { AttributesList } from '../lib/ldapActions';
|
|
2
2
|
import type { ConfigTemplate } from '../lib/parseConfig';
|
|
3
|
-
|
|
4
|
-
export declare const STATIC_CSP = "frame-ancestors 'self'";
|
|
5
|
-
export declare const STATIC_FRAME_OPTIONS = "SAMEORIGIN";
|
|
3
|
+
export { STATIC_CSP, STATIC_FRAME_OPTIONS } from '../plugins/static.options';
|
|
6
4
|
export interface BranchPermissions {
|
|
7
5
|
read?: boolean;
|
|
8
6
|
write?: boolean;
|
|
@@ -34,6 +32,7 @@ export interface Config {
|
|
|
34
32
|
log_level: 'error' | 'warn' | 'notice' | 'info' | 'debug';
|
|
35
33
|
shutdown_timeout?: number;
|
|
36
34
|
debug: boolean;
|
|
35
|
+
strict_options?: boolean;
|
|
37
36
|
logger: 'console' | 'file';
|
|
38
37
|
log_file?: string;
|
|
39
38
|
api_prefix: string;
|
|
@@ -364,7 +363,6 @@ export interface Config {
|
|
|
364
363
|
scim_bulk_max_payload_size?: number;
|
|
365
364
|
scim_etag?: boolean;
|
|
366
365
|
scim_base_url?: string;
|
|
367
|
-
[key: string]: string | string[] | boolean | number | AttributesList | AuthConfig | undefined;
|
|
368
366
|
}
|
|
369
367
|
/**
|
|
370
368
|
* Config arguments
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options declared by the code that reads them.
|
|
3
|
+
*
|
|
4
|
+
* A plugin describes its options next to its implementation with
|
|
5
|
+
* {@link defineOptions}; `src/config/args.ts` concatenates the declarations so
|
|
6
|
+
* the parser sees the same entries it always did. {@link readOptions} gives a
|
|
7
|
+
* plugin typed values, with the defaults applied and the value checked once.
|
|
8
|
+
*
|
|
9
|
+
* See `docs/plugin-development/options.md`.
|
|
10
|
+
*/
|
|
11
|
+
import { type ArrayKind, type ConfigEntry, type ConfigTemplate } from '../lib/parseConfig';
|
|
12
|
+
import type { Config } from './args';
|
|
13
|
+
export type OptionType = 'string' | 'number' | 'boolean' | 'array' | 'json';
|
|
14
|
+
export interface OptionSpec<T> {
|
|
15
|
+
readonly type: OptionType;
|
|
16
|
+
/** Environment variable the option reads when the CLI does not set it */
|
|
17
|
+
readonly env?: string;
|
|
18
|
+
/** Value kept when neither the CLI nor the environment sets the option */
|
|
19
|
+
readonly default?: T;
|
|
20
|
+
/** Refuse an empty value rather than read it as unset */
|
|
21
|
+
readonly required?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* A string option whose empty value means something: keep it instead of
|
|
24
|
+
* reading the declared default, as an empty environment or CLI value does.
|
|
25
|
+
*/
|
|
26
|
+
readonly emptyIsValue?: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface StringOption extends OptionSpec<string> {
|
|
29
|
+
readonly type: 'string';
|
|
30
|
+
/** Values the option accepts, when it is a closed set */
|
|
31
|
+
readonly choices?: readonly string[];
|
|
32
|
+
}
|
|
33
|
+
export interface StringOrFalseOption extends OptionSpec<string | false> {
|
|
34
|
+
readonly type: 'string';
|
|
35
|
+
readonly allowFalse: true;
|
|
36
|
+
}
|
|
37
|
+
export interface NumberOption extends OptionSpec<number> {
|
|
38
|
+
readonly type: 'number';
|
|
39
|
+
readonly min?: number;
|
|
40
|
+
readonly max?: number;
|
|
41
|
+
}
|
|
42
|
+
export interface BooleanOption extends OptionSpec<boolean> {
|
|
43
|
+
readonly type: 'boolean';
|
|
44
|
+
}
|
|
45
|
+
export interface ArrayOption extends OptionSpec<string[]> {
|
|
46
|
+
readonly type: 'array';
|
|
47
|
+
/** CLI name of the plural form, without the leading `--` */
|
|
48
|
+
readonly plural?: string;
|
|
49
|
+
/** How a comma, space or semicolon separated value splits, see ArrayKind */
|
|
50
|
+
readonly kind?: ArrayKind;
|
|
51
|
+
}
|
|
52
|
+
export interface JsonOption<T> extends OptionSpec<T> {
|
|
53
|
+
readonly type: 'json';
|
|
54
|
+
}
|
|
55
|
+
export type OptionSet = Record<string, OptionSpec<unknown>>;
|
|
56
|
+
/** The typed values {@link readOptions} returns for a set of declarations */
|
|
57
|
+
export type OptionsOf<T extends OptionSet> = {
|
|
58
|
+
[K in keyof T]: T[K] extends OptionSpec<infer V> ? V : never;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* A set of options, keyed by their `Config` name (`scim_max_results` for
|
|
62
|
+
* `--scim-max-results`). Identity function: its only job is to keep the
|
|
63
|
+
* literal types of the declarations.
|
|
64
|
+
*/
|
|
65
|
+
export declare function defineOptions<T extends OptionSet>(options: T): T;
|
|
66
|
+
export declare const opt: {
|
|
67
|
+
string: (options?: Omit<StringOption, "type">) => StringOption;
|
|
68
|
+
number: (options?: Omit<NumberOption, "type">) => NumberOption;
|
|
69
|
+
boolean: (options?: Omit<BooleanOption, "type">) => BooleanOption;
|
|
70
|
+
array: (options?: Omit<ArrayOption, "type">) => ArrayOption;
|
|
71
|
+
stringOrFalse: (options?: Omit<StringOrFalseOption, "type" | "allowFalse">) => StringOrFalseOption;
|
|
72
|
+
json: <T = Record<string, unknown>>(options?: Omit<JsonOption<T>, "type">) => JsonOption<T>;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* The parser entries a set of declarations amounts to. `args.ts` concatenates
|
|
76
|
+
* them with the global options and the other plugins' declarations.
|
|
77
|
+
*/
|
|
78
|
+
export declare function toConfigEntries(options: OptionSet): ConfigEntry[];
|
|
79
|
+
/**
|
|
80
|
+
* `DM_*` variables no declared option reads. Test-only variables are left
|
|
81
|
+
* out: a suite sets its own infrastructure through the same prefix.
|
|
82
|
+
*/
|
|
83
|
+
export declare function unknownEnvironmentOptions(entries: ConfigTemplate, env?: Record<string, string | undefined>, extra?: OptionSet[]): string[];
|
|
84
|
+
/**
|
|
85
|
+
* Read a plugin's options from a parsed configuration, applying the declared
|
|
86
|
+
* defaults and checking the values. Called once per instance by
|
|
87
|
+
* `DmPlugin.options()`.
|
|
88
|
+
*/
|
|
89
|
+
export declare function readOptions<T extends OptionSet>(options: T, config: Config | Record<string, unknown>, parsedKeys?: ReadonlySet<string>): OptionsOf<T>;
|
|
@@ -75,6 +75,16 @@ export declare function localizedText(text: LocalizedText | undefined, language?
|
|
|
75
75
|
* @returns the message
|
|
76
76
|
*/
|
|
77
77
|
export declare function invalidValueMessage(name: string, attr: SchemaAttribute | undefined): string;
|
|
78
|
+
/**
|
|
79
|
+
* Refusal of a value longer than its attribute accepts, for a
|
|
80
|
+
* `BadRequestError`. Said apart from {@link invalidValueMessage}: the value
|
|
81
|
+
* is not malformed, it is longer than the attribute may hold.
|
|
82
|
+
*
|
|
83
|
+
* @param name attribute name
|
|
84
|
+
* @param maxLength the length it may not exceed
|
|
85
|
+
* @returns the message
|
|
86
|
+
*/
|
|
87
|
+
export declare function tooLongMessage(name: string, maxLength: number): string;
|
|
78
88
|
/** Roles the core itself looks for. See {@link SchemaRole}. */
|
|
79
89
|
export declare const CORE_ROLES: readonly ["identifier", "displayName", "primaryEmail", "emailAliases", "emailQuota", "organizationLink", "organizationPath", "members", "owners", "accountStatus", "password", "passwordReset", "accountExpiry", "domainLink", "domainName"];
|
|
80
90
|
/**
|
|
@@ -142,10 +152,19 @@ export interface SchemaAttribute {
|
|
|
142
152
|
test?: string | RegExp;
|
|
143
153
|
hint?: LocalizedText;
|
|
144
154
|
branch?: string[];
|
|
155
|
+
/** Longest value, in UTF-16 code units, before `test` runs on it */
|
|
156
|
+
maxLength?: number;
|
|
145
157
|
};
|
|
146
158
|
default?: AttributeValue;
|
|
147
159
|
required?: boolean;
|
|
148
160
|
test?: string | RegExp;
|
|
161
|
+
/**
|
|
162
|
+
* Longest value, in UTF-16 code units, `test` is asked to judge: a longer one is
|
|
163
|
+
* refused before the pattern reads it. Without it, the length is
|
|
164
|
+
* {@link DEFAULT_MAX_LENGTH}; an attribute holding longer values declares
|
|
165
|
+
* it here, the reason being in `docs/client-development/schemas/README.md`.
|
|
166
|
+
*/
|
|
167
|
+
maxLength?: number;
|
|
149
168
|
/**
|
|
150
169
|
* Plain-language description of `test`, shown to the user *before* they get
|
|
151
170
|
* a value wrong — "Expected pattern 999 9999". It travels with the
|
|
@@ -334,6 +353,10 @@ export declare function modifiedAttributeNames(body: {
|
|
|
334
353
|
* written as text where the schema declares one, a boolean as LDAP's
|
|
335
354
|
* `TRUE` / `FALSE`, and anything else is refused.
|
|
336
355
|
*
|
|
356
|
+
* The attribute is read as a request names it, under any case and with or
|
|
357
|
+
* without a `;binary` option: the value is converted by its type, as the
|
|
358
|
+
* validator accepted it.
|
|
359
|
+
*
|
|
337
360
|
* @param schema schema of the entry, if any
|
|
338
361
|
* @param field attribute name
|
|
339
362
|
* @param value value as the client sent it
|
|
@@ -391,15 +414,41 @@ export declare function withoutAttributes<T extends object>(entry: T, hidden: Se
|
|
|
391
414
|
* @throws BadRequestError naming the first DN that breaks a rule
|
|
392
415
|
*/
|
|
393
416
|
export declare function checkDnValues(field: string, attr: SchemaAttribute, value: AttributeValue, entryExists: (dn: string) => Promise<boolean>): Promise<void>;
|
|
417
|
+
/**
|
|
418
|
+
* Longest value a pattern is asked to judge when the schema declares no
|
|
419
|
+
* `maxLength`. See `docs/client-development/schemas/README.md`.
|
|
420
|
+
*/
|
|
421
|
+
export declare const DEFAULT_MAX_LENGTH = 4096;
|
|
422
|
+
/**
|
|
423
|
+
* The length a value is judged against: `maxLength` on the attribute, else
|
|
424
|
+
* the one on its `items`, else {@link DEFAULT_MAX_LENGTH}.
|
|
425
|
+
*/
|
|
426
|
+
export declare function maxLengthOf(attr: SchemaAttribute | undefined): number;
|
|
394
427
|
/**
|
|
395
428
|
* Tell whether every value of an attribute matches its pattern: `test`, or
|
|
396
429
|
* `items.test` for the elements of an array.
|
|
397
430
|
*
|
|
431
|
+
* A value longer than {@link maxLengthOf} is refused without running the
|
|
432
|
+
* pattern: see {@link SchemaAttribute.maxLength}.
|
|
433
|
+
*
|
|
398
434
|
* @param attr schema definition
|
|
399
435
|
* @param value value submitted
|
|
400
|
-
* @returns false when one value does not match
|
|
436
|
+
* @returns false when one value does not match, or is too long
|
|
401
437
|
*/
|
|
402
438
|
export declare function matchesPattern(attr: SchemaAttribute, value: AttributeValue): boolean;
|
|
439
|
+
/**
|
|
440
|
+
* The value past the length its attribute accepts, if one is: what a refusal
|
|
441
|
+
* says, where the pattern would only have said the value is wrong.
|
|
442
|
+
*
|
|
443
|
+
* The default length applies to an attribute carrying a pattern, which it
|
|
444
|
+
* keeps within what the pattern was written for; an attribute without one is
|
|
445
|
+
* bounded only by the `maxLength` its schema declares.
|
|
446
|
+
*
|
|
447
|
+
* @param attr schema definition
|
|
448
|
+
* @param value value submitted
|
|
449
|
+
* @returns the too long value, or undefined when none is
|
|
450
|
+
*/
|
|
451
|
+
export declare function tooLongValue(attr: SchemaAttribute, value: AttributeValue): AttributeValue | undefined;
|
|
403
452
|
/**
|
|
404
453
|
* Refuse a payload naming an attribute the server owns.
|
|
405
454
|
*
|
package/dist/src/hooks.d.ts
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { Entry, SearchOptions, SearchResult } from 'ldapts';
|
|
6
6
|
import type { Request, Response } from 'express';
|
|
7
|
-
import type { ModifyRequest, AttributesList, AttributeValue } from './lib/ldapActions';
|
|
7
|
+
import type { ModifyRequest, AttributesList, AttributeValue, LdapList } from './lib/ldapActions';
|
|
8
8
|
import type { ChangesToNotify } from './plugins/ldap/onChange';
|
|
9
9
|
import type { ChangeContext } from './lib/changeContext';
|
|
10
|
+
import type { BulkResponse, ScimGroup, ScimUser } from './plugins/scim/types';
|
|
10
11
|
import * as utils from './lib/utils';
|
|
11
12
|
export type MaybePromise<T> = Promise<T> | T;
|
|
12
13
|
export type ChainedHook<T> = (arg: T) => MaybePromise<T>;
|
|
@@ -29,7 +30,6 @@ export interface OidcSessionClaims {
|
|
|
29
30
|
}
|
|
30
31
|
export type OidcLogoutToken = OidcSessionClaims;
|
|
31
32
|
export type VoidHook<T extends unknown[]> = (...args: T) => MaybePromise<void>;
|
|
32
|
-
export type OtherHook = Function;
|
|
33
33
|
export { utils };
|
|
34
34
|
/**
|
|
35
35
|
* All available hooks
|
|
@@ -56,11 +56,11 @@ export interface Hooks {
|
|
|
56
56
|
*/
|
|
57
57
|
ldapsearchfilter?: ChainedHook<[SearchResult, Request?, SearchOptions?]>;
|
|
58
58
|
ldapaddrequest?: ChainedHook<[string, AttributesList, Request?]>;
|
|
59
|
-
ldapadddone?: (
|
|
59
|
+
ldapadddone?: (dn: string, entry: AttributesList, context?: ChangeContext) => MaybePromise<void>;
|
|
60
60
|
/** See docs/plugin-development/hooks.md#ldapaddafter */
|
|
61
|
-
ldapaddafter?: (
|
|
61
|
+
ldapaddafter?: (dn: string, entry: AttributesList, context?: ChangeContext) => MaybePromise<void>;
|
|
62
62
|
ldapmodifyrequest?: ChainedHook<[string, ModifyRequest, number, Request?]>;
|
|
63
|
-
ldapmodifydone?: (
|
|
63
|
+
ldapmodifydone?: (dn: string, changes: ModifyRequest, op: number, context?: ChangeContext) => MaybePromise<void>;
|
|
64
64
|
/**
|
|
65
65
|
* Once a modify is over, whatever became of it, after its `ldapmodifydone`
|
|
66
66
|
* subscribers have returned; with the operation number
|
|
@@ -75,7 +75,7 @@ export interface Hooks {
|
|
|
75
75
|
*/
|
|
76
76
|
ldapdeleteend?: (op: number) => MaybePromise<void>;
|
|
77
77
|
ldaprenamerequest?: ChainedHook<[string, string, Request?]>;
|
|
78
|
-
ldaprenamedone?: (
|
|
78
|
+
ldaprenamedone?: (dn: string, newDn: string, context?: ChangeContext) => MaybePromise<void>;
|
|
79
79
|
/**
|
|
80
80
|
* Once a rename is over, whatever became of it, after its `ldaprenamedone`
|
|
81
81
|
* subscribers have returned; with the operation number its request and
|
|
@@ -122,14 +122,20 @@ export interface Hooks {
|
|
|
122
122
|
/** LdapGroups plugin */
|
|
123
123
|
ldapgroupvalidatemembers?: ChainedHook<[string, string[]]>;
|
|
124
124
|
ldapgroupadd?: ChainedHook<[string, AttributesList]>;
|
|
125
|
-
ldapgroupadddone?: (
|
|
125
|
+
ldapgroupadddone?: (dn: string, entry: AttributesList) => MaybePromise<void>;
|
|
126
126
|
ldapgroupmodify?: ChainedHook<[string, ModifyRequest, number]>;
|
|
127
|
-
ldapgroupmodifydone?: (
|
|
127
|
+
ldapgroupmodifydone?: (dn: string, changes: ModifyRequest, op: number) => MaybePromise<void>;
|
|
128
|
+
ldapgrouprename?: ChainedHook<[string, string]>;
|
|
129
|
+
ldapgrouprenamedone?: (dn: string, newDn: string) => MaybePromise<void>;
|
|
130
|
+
/** Before `ldapgroupadd`, to judge the group about to be created */
|
|
131
|
+
ldapgroupvalidatenew?: ChainedHook<[string, AttributesList]>;
|
|
132
|
+
/** Before `ldapgroupmodify`, to judge the changes about to be written */
|
|
133
|
+
ldapgroupvalidatechanges?: ChainedHook<[string, ModifyRequest]>;
|
|
128
134
|
ldapgroupdelete?: ChainedHook<string>;
|
|
129
135
|
ldapgroupdeletedone?: (dn: string) => MaybePromise<void>;
|
|
130
136
|
ldapgroupaddmember?: ChainedHook<[string, string[]]>;
|
|
131
|
-
ldapgroupdeletemember?: ChainedHook<[string, string
|
|
132
|
-
_ldapgrouplist?: ChainedHook<
|
|
137
|
+
ldapgroupdeletemember?: ChainedHook<[string, string]>;
|
|
138
|
+
_ldapgrouplist?: ChainedHook<LdapList>;
|
|
133
139
|
/**
|
|
134
140
|
* "onLdapChange"
|
|
135
141
|
*
|
|
@@ -150,22 +156,54 @@ export interface Hooks {
|
|
|
150
156
|
onLdapDriveQuotaChange?: (dn: string, oldDriveQuota: number | null, newDriveQuota: number | null) => MaybePromise<void>;
|
|
151
157
|
/** externalUsersInGroup */
|
|
152
158
|
externaluserentry?: ChainedHook<[string, AttributesList]>;
|
|
153
|
-
|
|
159
|
+
/** The external member was created, with its DN and the entry written */
|
|
160
|
+
externaluseradded?: (dn: string, entry: AttributesList) => MaybePromise<void>;
|
|
154
161
|
/**
|
|
155
|
-
* Generic flat resource
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* Note: After move, onLdapChange is triggered automatically by modifyEntry()
|
|
162
|
+
* Generic flat resource hooks, one set per `--ldap-flat-schema` entity,
|
|
163
|
+
* `ldapusermove` for one renamed user: `{hookPrefix}` followed by the
|
|
164
|
+
* suffix of the shared hook of the same kind, see {@link FlatHookPrefix}.
|
|
165
|
+
* An entity's `move` hook takes `[dn, targetOrgDn, request?]`, and a move
|
|
166
|
+
* is followed by `onLdapChange`, as a modify is.
|
|
161
167
|
*/
|
|
162
|
-
[K: string]: ChainedHook<unknown> | VoidHook<unknown[]> | OtherHook | undefined;
|
|
163
168
|
/** Common authentication hooks */
|
|
164
169
|
beforeAuth?: ChainedHook<[Request, Response]>;
|
|
165
170
|
afterAuth?: ChainedHook<[Request, Response]>;
|
|
166
171
|
/** Organization hooks */
|
|
167
|
-
getOrganisationTop?: ChainedHook<[
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
getOrganisationTop?: ChainedHook<[Request | undefined, AttributesList]>;
|
|
173
|
+
/**
|
|
174
|
+
* SCIM plugin hooks
|
|
175
|
+
*
|
|
176
|
+
* The `create` and `update` hooks are chained and can mutate the resource
|
|
177
|
+
* about to be written; the `done` ones are notified once it is written.
|
|
178
|
+
* `scimgroupcreate` gets the group's base beside the request, which is
|
|
179
|
+
* always there: a group is created through the API.
|
|
180
|
+
*/
|
|
181
|
+
scimusercreate?: ChainedHook<[ScimUser, Request?]>;
|
|
182
|
+
scimusercreatedone?: VoidHook<[ScimUser]>;
|
|
183
|
+
scimuserupdate?: ChainedHook<[string, ScimUser, Request?]>;
|
|
184
|
+
scimuserupdatedone?: VoidHook<[string, ScimUser]>;
|
|
185
|
+
scimuserdelete?: ChainedHook<[string, Request?]>;
|
|
186
|
+
scimuserdeletedone?: VoidHook<[string]>;
|
|
187
|
+
/** `[group, request, base]` */
|
|
188
|
+
scimgroupcreate?: ChainedHook<[ScimGroup, Request, string]>;
|
|
189
|
+
scimgroupcreatedone?: VoidHook<[ScimGroup]>;
|
|
190
|
+
scimgroupupdate?: ChainedHook<[string, ScimGroup, Request?]>;
|
|
191
|
+
scimgroupupdatedone?: VoidHook<[string, ScimGroup]>;
|
|
192
|
+
scimgroupdelete?: ChainedHook<[string, Request?]>;
|
|
193
|
+
scimgroupdeletedone?: VoidHook<[string]>;
|
|
194
|
+
scimbulkdone?: VoidHook<[BulkResponse]>;
|
|
195
|
+
/**
|
|
196
|
+
* A delete asked of an entry that is already a tombstone. Fired
|
|
197
|
+
* synchronously with the request that asked it: whoever replays deletions
|
|
198
|
+
* records it from there. `[dn, entry, context]`.
|
|
199
|
+
*/
|
|
200
|
+
twakedeletionreplay?: (dn: string, entry: AttributesList, context: ChangeContext) => MaybePromise<void>;
|
|
171
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* The prefix of a flat entity's hooks, as `abstract/ldapFlat.ts` builds it:
|
|
204
|
+
* `ldap${entity.name}`. They are `{prefix}add`, `{prefix}adddone` and so on,
|
|
205
|
+
* one convention each, as the shared hook of the same suffix declares it,
|
|
206
|
+
* plus `{prefix}move` before an entry is moved. A plugin watching one
|
|
207
|
+
* declares them beside its other hooks.
|
|
208
|
+
*/
|
|
209
|
+
export type FlatHookPrefix = `ldap${string}`;
|