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.
Files changed (342) hide show
  1. package/bin/sync-james.mjs +13 -2
  2. package/dist/abstract/ldapFlat.js +1 -1
  3. package/dist/abstract/plugin.js +1 -1
  4. package/dist/abstract/twakePlugin.js +1 -1
  5. package/dist/bin/index.js +1 -1
  6. package/dist/config/args.js +1 -1
  7. package/dist/config/options.js +1 -0
  8. package/dist/config/schema.js +1 -1
  9. package/dist/lib/auth/base.js +1 -1
  10. package/dist/lib/authz/base.js +1 -1
  11. package/dist/lib/entitySchema/validator.js +1 -1
  12. package/dist/lib/errors.js +1 -1
  13. package/dist/lib/expressFormatedResponses.js +1 -1
  14. package/dist/lib/health.js +1 -1
  15. package/dist/lib/hookBus.js +1 -0
  16. package/dist/lib/instanceProviders.js +1 -1
  17. package/dist/lib/ldapActions.js +1 -1
  18. package/dist/lib/ldapSchema.js +1 -1
  19. package/dist/lib/parseConfig.js +1 -1
  20. package/dist/lib/route.js +1 -0
  21. package/dist/lib/schemaFile.js +1 -1
  22. package/dist/lib/storage/store.js +1 -1
  23. package/dist/lib/utils.js +1 -1
  24. package/dist/lib/version.js +1 -0
  25. package/dist/logger/winston.js +1 -1
  26. package/dist/plugins/auth/auth.options.js +1 -0
  27. package/dist/plugins/auth/authzDynamic.js +1 -1
  28. package/dist/plugins/auth/authzLinid1.js +1 -1
  29. package/dist/plugins/auth/authzPerBranch.js +1 -1
  30. package/dist/plugins/auth/authzPerRoute.js +1 -1
  31. package/dist/plugins/auth/authzScope.js +1 -1
  32. package/dist/plugins/auth/crowdsec.js +1 -1
  33. package/dist/plugins/auth/fake.js +1 -1
  34. package/dist/plugins/auth/hmac.js +1 -1
  35. package/dist/plugins/auth/llng.js +1 -1
  36. package/dist/plugins/auth/openidconnect.js +1 -1
  37. package/dist/plugins/auth/rateLimit.js +1 -1
  38. package/dist/plugins/auth/token.js +1 -1
  39. package/dist/plugins/auth/totp.js +1 -1
  40. package/dist/plugins/auth/trustedProxy.js +1 -1
  41. package/dist/plugins/bcl.js +1 -1
  42. package/dist/plugins/bcl.options.js +1 -0
  43. package/dist/plugins/configApi.js +1 -1
  44. package/dist/plugins/demo/helloworld.js +1 -1
  45. package/dist/plugins/ldap/accountLifecycle.js +1 -1
  46. package/dist/plugins/ldap/bulkImport.js +1 -1
  47. package/dist/plugins/ldap/departmentSync.js +1 -1
  48. package/dist/plugins/ldap/enterpriseRules.js +1 -1
  49. package/dist/plugins/ldap/externalUsersInGroups.js +1 -1
  50. package/dist/plugins/ldap/flatGeneric.js +1 -1
  51. package/dist/plugins/ldap/groups.js +1 -1
  52. package/dist/plugins/ldap/ldap.options.js +1 -0
  53. package/dist/plugins/ldap/onChange.js +1 -1
  54. package/dist/plugins/ldap/organizations.js +1 -1
  55. package/dist/plugins/ldap/passwordPolicy.js +1 -1
  56. package/dist/plugins/ldap/raw.js +1 -1
  57. package/dist/plugins/ldap/trash.js +1 -1
  58. package/dist/plugins/metrics.js +1 -1
  59. package/dist/plugins/metrics.options.js +1 -0
  60. package/dist/plugins/rabbitmq.js +1 -1
  61. package/dist/plugins/rabbitmq.options.js +1 -0
  62. package/dist/plugins/scim/baseResolver.js +1 -1
  63. package/dist/plugins/scim/bulk.js +1 -1
  64. package/dist/plugins/scim/discovery.js +1 -1
  65. package/dist/plugins/scim/errors.js +1 -1
  66. package/dist/plugins/scim/filter.js +1 -1
  67. package/dist/plugins/scim/groups.js +1 -1
  68. package/dist/plugins/scim/mapping.js +1 -1
  69. package/dist/plugins/scim/patch.js +1 -1
  70. package/dist/plugins/scim/projection.js +1 -1
  71. package/dist/plugins/scim/scim.js +1 -1
  72. package/dist/plugins/scim/scim.options.js +1 -0
  73. package/dist/plugins/scim/users.js +1 -1
  74. package/dist/plugins/static.js +1 -1
  75. package/dist/plugins/static.options.js +1 -0
  76. package/dist/plugins/storage/file.js +1 -1
  77. package/dist/plugins/storage/ldap.js +1 -1
  78. package/dist/plugins/storage/postgres.js +1 -1
  79. package/dist/plugins/storage/valkey.js +1 -1
  80. package/dist/plugins/storage.js +1 -1
  81. package/dist/plugins/storage.options.js +1 -0
  82. package/dist/plugins/twake/appAccounts.options.js +1 -0
  83. package/dist/plugins/twake/appAccountsApi.js +1 -1
  84. package/dist/plugins/twake/appAccountsConsistency.js +1 -1
  85. package/dist/plugins/twake/calendar.js +1 -1
  86. package/dist/plugins/twake/calendar.options.js +1 -0
  87. package/dist/plugins/twake/calendarResources.js +1 -1
  88. package/dist/plugins/twake/cloudery.options.js +1 -0
  89. package/dist/plugins/twake/clouderyProvision.js +1 -1
  90. package/dist/plugins/twake/cozy.options.js +1 -0
  91. package/dist/plugins/twake/cozyProvision.js +1 -1
  92. package/dist/plugins/twake/drive.js +1 -1
  93. package/dist/plugins/twake/drive.options.js +1 -0
  94. package/dist/plugins/twake/groups.js +1 -1
  95. package/dist/plugins/twake/instances.js +1 -1
  96. package/dist/plugins/twake/james.js +1 -1
  97. package/dist/plugins/twake/james.options.js +1 -0
  98. package/dist/plugins/twake/lifecycleAttributes.js +1 -1
  99. package/dist/plugins/twake/lifecycleEvents.js +1 -1
  100. package/dist/plugins/twake/migrationProxy.js +1 -1
  101. package/dist/plugins/twake/migrationProxy.options.js +1 -0
  102. package/dist/plugins/twake/spaces.js +1 -1
  103. package/dist/plugins/twake/tombstone.js +1 -1
  104. package/dist/plugins/twake/twakeGroups.options.js +1 -0
  105. package/dist/plugins/twake/twakeInstances.options.js +1 -0
  106. package/dist/plugins/twake/twakeLifecycle.options.js +1 -0
  107. package/dist/plugins/twake/twakeSpaces.options.js +1 -0
  108. package/dist/plugins/twake/twakeTombstone.options.js +1 -0
  109. package/dist/plugins/weblogs.js +1 -1
  110. package/dist/src/abstract/ldapFlat.d.ts +28 -10
  111. package/dist/src/abstract/plugin.d.ts +10 -5
  112. package/dist/src/abstract/twakePlugin.d.ts +14 -4
  113. package/dist/src/bin/index.d.ts +17 -4
  114. package/dist/src/config/args.d.ts +2 -4
  115. package/dist/src/config/options.d.ts +89 -0
  116. package/dist/src/config/schema.d.ts +50 -1
  117. package/dist/src/hooks.d.ts +60 -22
  118. package/dist/src/lib/authz/base.d.ts +4 -51
  119. package/dist/src/lib/authz/composition.d.ts +2 -2
  120. package/dist/src/lib/entitySchema/validator.d.ts +63 -6
  121. package/dist/src/lib/errors.d.ts +56 -1
  122. package/dist/src/lib/expressFormatedResponses.d.ts +49 -7
  123. package/dist/src/lib/hookBus.d.ts +226 -0
  124. package/dist/src/lib/instanceProviders.d.ts +0 -8
  125. package/dist/src/lib/ldapActions.d.ts +11 -2
  126. package/dist/src/lib/parseConfig.d.ts +5 -0
  127. package/dist/src/lib/route.d.ts +99 -0
  128. package/dist/src/lib/utils.d.ts +37 -36
  129. package/dist/src/lib/version.d.ts +12 -0
  130. package/dist/src/plugins/auth/auth.options.d.ts +88 -0
  131. package/dist/src/plugins/auth/authzDynamic.d.ts +10 -1
  132. package/dist/src/plugins/auth/authzLinid1.d.ts +6 -0
  133. package/dist/src/plugins/auth/authzPerBranch.d.ts +9 -0
  134. package/dist/src/plugins/auth/authzPerRoute.d.ts +3 -0
  135. package/dist/src/plugins/auth/authzScope.d.ts +23 -14
  136. package/dist/src/plugins/auth/crowdsec.d.ts +5 -0
  137. package/dist/src/plugins/auth/fake.d.ts +3 -0
  138. package/dist/src/plugins/auth/hmac.d.ts +4 -0
  139. package/dist/src/plugins/auth/llng.d.ts +4 -0
  140. package/dist/src/plugins/auth/openidconnect.d.ts +7 -0
  141. package/dist/src/plugins/auth/rateLimit.d.ts +4 -0
  142. package/dist/src/plugins/auth/token.d.ts +3 -0
  143. package/dist/src/plugins/auth/totp.d.ts +5 -0
  144. package/dist/src/plugins/auth/trustedProxy.d.ts +4 -0
  145. package/dist/src/plugins/bcl.d.ts +3 -0
  146. package/dist/src/plugins/bcl.options.d.ts +8 -0
  147. package/dist/src/plugins/configApi.d.ts +24 -5
  148. package/dist/src/plugins/demo/helloworld.d.ts +8 -19
  149. package/dist/src/plugins/ldap/accountLifecycle.d.ts +17 -0
  150. package/dist/src/plugins/ldap/bulkImport.d.ts +6 -76
  151. package/dist/src/plugins/ldap/departmentSync.d.ts +1 -0
  152. package/dist/src/plugins/ldap/enterpriseRules.d.ts +8 -1
  153. package/dist/src/plugins/ldap/externalUsersInGroups.d.ts +4 -0
  154. package/dist/src/plugins/ldap/flatGeneric.d.ts +4 -0
  155. package/dist/src/plugins/ldap/groups.d.ts +27 -11
  156. package/dist/src/plugins/ldap/ldap.options.d.ts +75 -0
  157. package/dist/src/plugins/ldap/onChange.d.ts +10 -2
  158. package/dist/src/plugins/ldap/organizations.d.ts +16 -0
  159. package/dist/src/plugins/ldap/passwordPolicy.d.ts +32 -1
  160. package/dist/src/plugins/ldap/raw.d.ts +50 -19
  161. package/dist/src/plugins/ldap/trash.d.ts +6 -0
  162. package/dist/src/plugins/metrics.d.ts +3 -0
  163. package/dist/src/plugins/metrics.options.d.ts +8 -0
  164. package/dist/src/plugins/rabbitmq.d.ts +3 -0
  165. package/dist/src/plugins/rabbitmq.options.d.ts +8 -0
  166. package/dist/src/plugins/scim/baseResolver.d.ts +2 -1
  167. package/dist/src/plugins/scim/bulk.d.ts +6 -7
  168. package/dist/src/plugins/scim/discovery.d.ts +3 -0
  169. package/dist/src/plugins/scim/errors.d.ts +4 -1
  170. package/dist/src/plugins/scim/groups.d.ts +6 -4
  171. package/dist/src/plugins/scim/scim.d.ts +37 -3
  172. package/dist/src/plugins/scim/scim.options.d.ts +35 -0
  173. package/dist/src/plugins/scim/types.d.ts +2 -1
  174. package/dist/src/plugins/scim/users.d.ts +6 -7
  175. package/dist/src/plugins/static.d.ts +9 -0
  176. package/dist/src/plugins/static.options.d.ts +13 -0
  177. package/dist/src/plugins/storage.d.ts +12 -1
  178. package/dist/src/plugins/storage.options.d.ts +16 -0
  179. package/dist/src/plugins/twake/appAccounts.options.d.ts +13 -0
  180. package/dist/src/plugins/twake/appAccountsApi.d.ts +18 -0
  181. package/dist/src/plugins/twake/appAccountsConsistency.d.ts +8 -0
  182. package/dist/src/plugins/twake/calendar.d.ts +25 -2
  183. package/dist/src/plugins/twake/calendar.options.d.ts +19 -0
  184. package/dist/src/plugins/twake/cloudery.options.d.ts +26 -0
  185. package/dist/src/plugins/twake/clouderyProvision.d.ts +18 -0
  186. package/dist/src/plugins/twake/cozy.options.d.ts +23 -0
  187. package/dist/src/plugins/twake/cozyProvision.d.ts +14 -0
  188. package/dist/src/plugins/twake/drive.d.ts +7 -0
  189. package/dist/src/plugins/twake/drive.options.d.ts +15 -0
  190. package/dist/src/plugins/twake/groups.d.ts +30 -3
  191. package/dist/src/plugins/twake/instances.d.ts +43 -10
  192. package/dist/src/plugins/twake/james.d.ts +17 -0
  193. package/dist/src/plugins/twake/james.options.d.ts +21 -0
  194. package/dist/src/plugins/twake/lifecycleEvents.d.ts +12 -0
  195. package/dist/src/plugins/twake/migrationProxy.d.ts +11 -11
  196. package/dist/src/plugins/twake/migrationProxy.options.d.ts +16 -0
  197. package/dist/src/plugins/twake/spaces.d.ts +30 -0
  198. package/dist/src/plugins/twake/tombstone.d.ts +16 -14
  199. package/dist/src/plugins/twake/twakeGroups.options.d.ts +20 -0
  200. package/dist/src/plugins/twake/twakeInstances.options.d.ts +43 -0
  201. package/dist/src/plugins/twake/twakeLifecycle.options.d.ts +21 -0
  202. package/dist/src/plugins/twake/twakeSpaces.options.d.ts +19 -0
  203. package/dist/src/plugins/twake/twakeTombstone.options.d.ts +17 -0
  204. package/docs/README.md +1 -0
  205. package/docs/client-development/api/rest-api.md +9 -3
  206. package/docs/client-development/schemas/README.md +96 -32
  207. package/docs/plugin-development/README.md +45 -94
  208. package/docs/plugin-development/dependencies.md +11 -6
  209. package/docs/plugin-development/hooks.md +142 -23
  210. package/docs/plugin-development/openapi.md +14 -1
  211. package/docs/plugin-development/options.md +109 -0
  212. package/docs/plugin-development/routes.md +152 -0
  213. package/docs/plugin-development/testing.md +3 -3
  214. package/docs/usage/configuration.md +31 -24
  215. package/docs/usage/operations.md +19 -4
  216. package/docs/usage/plugins/integrations/groups.md +2 -2
  217. package/docs/usage/plugins/integrations/scim.md +12 -1
  218. package/docs/usage/plugins/integrations/spaces.md +1 -1
  219. package/docs/usage/plugins/ldap/enterprise-rules.md +10 -1
  220. package/docs/usage/plugins/ldap/flat-generic.md +4 -0
  221. package/docs/usage/plugins/ldap/raw.md +17 -1
  222. package/docs/usage/upgrading.md +207 -0
  223. package/package.json +358 -277
  224. package/static/browser/ldap-browser/LdapBrowser.js +8 -6
  225. package/static/browser/ldap-browser/LdapBrowser.js.map +1 -1
  226. package/static/browser/ldap-browser/api/RawApiClient.js +2 -0
  227. package/static/browser/ldap-browser/api/RawApiClient.js.map +1 -1
  228. package/static/browser/ldap-browser/components/EntryTree.js +9 -5
  229. package/static/browser/ldap-browser/components/EntryTree.js.map +1 -1
  230. package/static/browser/ldap-browser/components/EntryView.js +2 -0
  231. package/static/browser/ldap-browser/components/EntryView.js.map +1 -1
  232. package/static/browser/ldap-browser/schema.js +3 -2
  233. package/static/browser/ldap-browser/schema.js.map +1 -1
  234. package/static/browser/ldap-browser.esm.js +27 -18
  235. package/static/browser/ldap-browser.esm.js.map +1 -1
  236. package/static/browser/ldap-browser.js +27 -18
  237. package/static/browser/ldap-browser.js.map +1 -1
  238. package/static/browser/ldap-browser.min.js +1 -1
  239. package/static/browser/ldap-browser.min.js.map +1 -1
  240. package/static/browser/ldap-group-editor/LdapGroupEditor.js +8 -6
  241. package/static/browser/ldap-group-editor/LdapGroupEditor.js.map +1 -1
  242. package/static/browser/ldap-group-editor/api/GroupApiClient.js +1 -0
  243. package/static/browser/ldap-group-editor/api/GroupApiClient.js.map +1 -1
  244. package/static/browser/ldap-group-editor/components/CreateGroupModal.js +5 -1
  245. package/static/browser/ldap-group-editor/components/CreateGroupModal.js.map +1 -1
  246. package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js +6 -3
  247. package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js.map +1 -1
  248. package/static/browser/ldap-group-editor/components/GroupTree.js +6 -3
  249. package/static/browser/ldap-group-editor/components/GroupTree.js.map +1 -1
  250. package/static/browser/ldap-group-editor/components/MoveGroupModal.js +6 -3
  251. package/static/browser/ldap-group-editor/components/MoveGroupModal.js.map +1 -1
  252. package/static/browser/ldap-group-editor.esm.js +32 -16
  253. package/static/browser/ldap-group-editor.esm.js.map +1 -1
  254. package/static/browser/ldap-group-editor.js +32 -16
  255. package/static/browser/ldap-group-editor.js.map +1 -1
  256. package/static/browser/ldap-group-editor.min.js +1 -1
  257. package/static/browser/ldap-group-editor.min.js.map +1 -1
  258. package/static/browser/ldap-resource-editor/api/ResourceApiClient.js +3 -0
  259. package/static/browser/ldap-resource-editor/api/ResourceApiClient.js.map +1 -1
  260. package/static/browser/ldap-resource-editor/cache/CacheManager.js +3 -1
  261. package/static/browser/ldap-resource-editor/cache/CacheManager.js.map +1 -1
  262. package/static/browser/ldap-tree-viewer/LdapTreeViewer.js +6 -1
  263. package/static/browser/ldap-tree-viewer/LdapTreeViewer.js.map +1 -1
  264. package/static/browser/ldap-tree-viewer/api/LdapApiClient.js +2 -0
  265. package/static/browser/ldap-tree-viewer/api/LdapApiClient.js.map +1 -1
  266. package/static/browser/ldap-tree-viewer/components/TreeNode.js +4 -0
  267. package/static/browser/ldap-tree-viewer/components/TreeNode.js.map +1 -1
  268. package/static/browser/ldap-tree-viewer/components/TreeRoot.js +3 -0
  269. package/static/browser/ldap-tree-viewer/components/TreeRoot.js.map +1 -1
  270. package/static/browser/ldap-tree-viewer/store/Store.js +3 -1
  271. package/static/browser/ldap-tree-viewer/store/Store.js.map +1 -1
  272. package/static/browser/ldap-tree-viewer.esm.js +18 -2
  273. package/static/browser/ldap-tree-viewer.esm.js.map +1 -1
  274. package/static/browser/ldap-tree-viewer.js +18 -2
  275. package/static/browser/ldap-tree-viewer.js.map +1 -1
  276. package/static/browser/ldap-tree-viewer.min.js +1 -1
  277. package/static/browser/ldap-tree-viewer.min.js.map +1 -1
  278. package/static/browser/ldap-unit-editor/LdapUnitEditor.js +7 -5
  279. package/static/browser/ldap-unit-editor/LdapUnitEditor.js.map +1 -1
  280. package/static/browser/ldap-unit-editor/api/UnitApiClient.js +1 -0
  281. package/static/browser/ldap-unit-editor/api/UnitApiClient.js.map +1 -1
  282. package/static/browser/ldap-unit-editor/components/MoveUnitModal.js +7 -4
  283. package/static/browser/ldap-unit-editor/components/MoveUnitModal.js.map +1 -1
  284. package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js +7 -4
  285. package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js.map +1 -1
  286. package/static/browser/ldap-unit-editor/components/UnitTree.js +6 -3
  287. package/static/browser/ldap-unit-editor/components/UnitTree.js.map +1 -1
  288. package/static/browser/ldap-unit-editor.esm.js +28 -16
  289. package/static/browser/ldap-unit-editor.esm.js.map +1 -1
  290. package/static/browser/ldap-unit-editor.js +28 -16
  291. package/static/browser/ldap-unit-editor.js.map +1 -1
  292. package/static/browser/ldap-unit-editor.min.js +1 -1
  293. package/static/browser/ldap-unit-editor.min.js.map +1 -1
  294. package/static/browser/ldap-user-editor/LdapUserEditor.js +9 -7
  295. package/static/browser/ldap-user-editor/LdapUserEditor.js.map +1 -1
  296. package/static/browser/ldap-user-editor/api/UserApiClient.js +2 -0
  297. package/static/browser/ldap-user-editor/api/UserApiClient.js.map +1 -1
  298. package/static/browser/ldap-user-editor/cache/CacheManager.js +3 -1
  299. package/static/browser/ldap-user-editor/cache/CacheManager.js.map +1 -1
  300. package/static/browser/ldap-user-editor/components/MoveUserModal.js +7 -4
  301. package/static/browser/ldap-user-editor/components/MoveUserModal.js.map +1 -1
  302. package/static/browser/ldap-user-editor/components/PointerField.js +6 -1
  303. package/static/browser/ldap-user-editor/components/PointerField.js.map +1 -1
  304. package/static/browser/ldap-user-editor/components/UserEditor.js +10 -5
  305. package/static/browser/ldap-user-editor/components/UserEditor.js.map +1 -1
  306. package/static/browser/ldap-user-editor/components/UserList.js +8 -4
  307. package/static/browser/ldap-user-editor/components/UserList.js.map +1 -1
  308. package/static/browser/ldap-user-editor/components/UserTree.js +7 -3
  309. package/static/browser/ldap-user-editor/components/UserTree.js.map +1 -1
  310. package/static/browser/ldap-user-editor.esm.js +55 -30
  311. package/static/browser/ldap-user-editor.esm.js.map +1 -1
  312. package/static/browser/ldap-user-editor.js +55 -30
  313. package/static/browser/ldap-user-editor.js.map +1 -1
  314. package/static/browser/ldap-user-editor.min.js +1 -1
  315. package/static/browser/ldap-user-editor.min.js.map +1 -1
  316. package/static/browser/shared/components/DisposableComponent.js +3 -5
  317. package/static/browser/shared/components/DisposableComponent.js.map +1 -1
  318. package/static/browser/shared/components/Modal.js +2 -1
  319. package/static/browser/shared/components/Modal.js.map +1 -1
  320. package/static/browser/shared/components/StatusMessage.js +3 -2
  321. package/static/browser/shared/components/StatusMessage.js.map +1 -1
  322. package/static/browser/shared/utils/form.js.map +1 -1
  323. package/static/browser/shared/utils/hmac.js +1 -0
  324. package/static/browser/shared/utils/hmac.js.map +1 -1
  325. package/static/browser/shared/utils/totp.js +1 -0
  326. package/static/browser/shared/utils/totp.js.map +1 -1
  327. package/static/browser/shared.esm.js +9 -8
  328. package/static/browser/shared.esm.js.map +1 -1
  329. package/static/browser/shared.js +9 -8
  330. package/static/browser/shared.js.map +1 -1
  331. package/static/browser/shared.min.js +1 -1
  332. package/static/browser/shared.min.js.map +1 -1
  333. package/static/schemas/ad/groups.json +1 -1
  334. package/static/schemas/ad/users.json +3 -3
  335. package/static/schemas/example/organizations.json +2 -2
  336. package/static/schemas/example/users.json +2 -2
  337. package/static/schemas/obm/users.json +2 -2
  338. package/static/schemas/standard/groups.json +1 -1
  339. package/static/schemas/standard/users.json +2 -2
  340. package/static/schemas/twake/groups.json +4 -4
  341. package/static/schemas/twake/teamMailboxes.json +3 -3
  342. package/static/schemas/twake/users.json +7 -7
@@ -66,7 +66,7 @@ A complete schema consists of two main sections: entity metadata and attribute d
66
66
  },
67
67
  "mail": {
68
68
  "type": "string",
69
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
69
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$",
70
70
  "required": false,
71
71
  "role": "primaryEmail"
72
72
  },
@@ -74,7 +74,7 @@ A complete schema consists of two main sections: entity metadata and attribute d
74
74
  "type": "array",
75
75
  "items": {
76
76
  "type": "string",
77
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
77
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
78
78
  },
79
79
  "required": false,
80
80
  "role": "emailAliases"
@@ -197,7 +197,7 @@ Simple text values.
197
197
  },
198
198
  "mail": {
199
199
  "type": "string",
200
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
200
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$",
201
201
  "required": false,
202
202
  "role": "primaryEmail"
203
203
  },
@@ -237,7 +237,7 @@ Multi-valued attributes containing multiple items of the same type.
237
237
  "type": "array",
238
238
  "items": {
239
239
  "type": "string",
240
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
240
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
241
241
  },
242
242
  "required": false,
243
243
  "role": "emailAliases"
@@ -338,19 +338,20 @@ Complete reference for all attribute properties.
338
338
 
339
339
  ### Attribute Properties Table
340
340
 
341
- | Property | Type | Applies To | Description | Example |
342
- | ---------- | --------------- | ------------------- | ------------------------------------------------------------------------------------- | ----------------------------------- |
343
- | `type` | string | All | `"string"`, `"array"`, `"pointer"`, `"number"`, `"integer"`, `"boolean"`, `"date"` | `"string"` |
344
- | `required` | boolean | All | Whether the attribute must be present | `true` |
345
- | `fixed` | boolean | All | Whether the attribute value cannot be changed after creation | `true` |
346
- | `role` | string/string[] | String | Semantic role, or roles, for special handling (see [Semantic Roles](#semantic-roles)) | `"identifier"` |
347
- | `test` | string/regex | String, Array items | Regular expression for validation | `"^[a-zA-Z0-9._-]{1,255}$"` |
348
- | `branch` | string[] | Pointer | LDAP branches where referenced entries exist | `["ou=users,dc=example,dc=com"]` |
349
- | `ui` | object | Pointer | UI configuration for pointer selection (future feature) | `{}` |
350
- | `hint` | text | String, Array items | What a valid value looks like, shown under the field and quoted in refusals | `"Expected pattern 999 9999"` |
351
- | `group` | text | All | Form section the attribute is shown in | `"Mailbox Settings"` |
352
- | `default` | any | All | Default value for new entries | `["top", "person"]` |
353
- | `items` | object | Array | Schema for array element validation | `{"type": "string", "test": "..."}` |
341
+ | Property | Type | Applies To | Description | Example |
342
+ | ----------- | --------------- | ------------------- | ------------------------------------------------------------------------------------- | ----------------------------------- |
343
+ | `type` | string | All | `"string"`, `"array"`, `"pointer"`, `"number"`, `"integer"`, `"boolean"`, `"date"` | `"string"` |
344
+ | `required` | boolean | All | Whether the attribute must be present | `true` |
345
+ | `fixed` | boolean | All | Whether the attribute value cannot be changed after creation | `true` |
346
+ | `role` | string/string[] | String | Semantic role, or roles, for special handling (see [Semantic Roles](#semantic-roles)) | `"identifier"` |
347
+ | `test` | string/regex | String, Array items | Regular expression for validation | `"^[a-zA-Z0-9._-]{1,255}$"` |
348
+ | `maxLength` | number | String, Array items | Longest value accepted, in characters; a longer one is refused before `test` (4096) | `255` |
349
+ | `branch` | string[] | Pointer | LDAP branches where referenced entries exist | `["ou=users,dc=example,dc=com"]` |
350
+ | `ui` | object | Pointer | UI configuration for pointer selection (future feature) | `{}` |
351
+ | `hint` | text | String, Array items | What a valid value looks like, shown under the field and quoted in refusals | `"Expected pattern 999 9999"` |
352
+ | `group` | text | All | Form section the attribute is shown in | `"Mailbox Settings"` |
353
+ | `default` | any | All | Default value for new entries | `["top", "person"]` |
354
+ | `items` | object | Array | Schema for array element validation | `{"type": "string", "test": "..."}` |
354
355
 
355
356
  A _text_ is a plain string or a map keyed by language tag,
356
357
  `{ "en": "Mailbox Settings", "fr": "Paramètres de messagerie" }`, picked as
@@ -378,16 +379,40 @@ answers, one value where the attribute holds one, can be sent back as is;
378
379
  two values or more for a single-valued attribute are refused. Each element
379
380
  of an `array` follows `items.type`, text without `items`. A `number` or
380
381
  `integer` is a JSON number or its text (`42`, `"42"`), a `boolean` a JSON
381
- boolean or the directory's `TRUE` / `FALSE`; anything else for them, and
382
+ boolean or the directory's `TRUE` / `FALSE`. A JSON number is judged on the
383
+ text written for it, what the directory is sent: `1e21` and `1e-7` are
384
+ written `1e+21` and `1e-7`, so they are refused like that text, and a whole
385
+ number beyond 2⁵³ is refused whatever the type, as the JSON parser has
386
+ already lost its digits. Anything else for them, and
382
387
  anything but text for a `string`, `date` or `pointer`, is refused; an
383
388
  attribute with `normalize` takes any text, read after validation. In a
384
389
  `replace`, `null` or `[]` clears an attribute; in an `add` or on a creation
385
- they are refused as no value. A `delete` naming the attribute, or naming it
386
- with no value, removes it, unless it is `required`; naming values removes
387
- those, every value it holds included, as the server does not read what
388
- remains, and `''` names no value. An empty string sent as a value is
389
- checked against `test`, refused for a pointer, and missing for a `required`
390
- attribute. Every refusal answers 400 with the attribute's name.
390
+ they are refused as no value when the schema declares the attribute. A
391
+ `null` under a name the schema does not declare — and, for an entity without
392
+ a schema, under any name — is dropped from a creation and from an `add`, as
393
+ if the key were left out, and clears the attribute in a `replace`. A
394
+ `delete` naming the attribute, or naming it with no value, removes it,
395
+ unless it is `required`; naming values removes those, every value it holds
396
+ included, as the server does not read what remains, and `''` names no
397
+ value. An empty string sent as a value is checked against `test`, refused
398
+ for a pointer, and missing for a `required` attribute. Every refusal answers
399
+ 400 with the attribute's name.
400
+
401
+ #### What an attribute may be called
402
+
403
+ A name is read as the directory reads it: case and the `;binary` option do
404
+ not change the attribute it names, so `Cn`, `CN` and `cn;binary` all name
405
+ `cn`. Each key the schema declares is written under the schema's spelling —
406
+ `Cn` becomes `cn`, `L;binary` becomes `l;binary` — before any check, so that
407
+ a `required` attribute counts as present wherever it was sent, and a `fixed`
408
+ one is written once, at its default. The attribute's type is read the same
409
+ way when a value is converted, so a boolean sent as `pwdReset;binary` is
410
+ written `TRUE`.
411
+
412
+ Two keys of an entry or of one part of a change naming one attribute are
413
+ refused; the option does not make them two, so `l` and `l;binary` are
414
+ refused together. A name the schema does not declare is left as sent, and a
415
+ strict schema refuses it.
391
416
 
392
417
  Every entity route applies these rules; `test/lib/entitySchema.contract.test.ts`
393
418
  asserts them on a flat entity, a group and an organization alike. In the
@@ -451,7 +476,10 @@ Assigns semantic meaning to attributes for special handling by the application.
451
476
 
452
477
  #### `test` (Optional)
453
478
 
454
- Regular expression pattern for validating attribute values.
479
+ Regular expression pattern for validating attribute values. The value is
480
+ judged only up to its attribute's length: see
481
+ [`maxLength`](#maxlength-optional) below, which a deployment writing its own
482
+ pattern should read.
455
483
 
456
484
  ```json
457
485
  {
@@ -461,7 +489,7 @@ Regular expression pattern for validating attribute values.
461
489
  },
462
490
  "mail": {
463
491
  "type": "string",
464
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
492
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
465
493
  }
466
494
  }
467
495
  ```
@@ -474,7 +502,38 @@ Regular expression pattern for validating attribute values.
474
502
  "type": "array",
475
503
  "items": {
476
504
  "type": "string",
477
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
505
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
506
+ }
507
+ }
508
+ }
509
+ ```
510
+
511
+ #### `maxLength` (Optional)
512
+
513
+ Longest value, in UTF-16 code units (what `.length` counts, so an emoji or an
514
+ accented letter outside the basic plane counts as two), accepted before `test`
515
+ runs on it. A value past
516
+ it is refused with 400, and the pattern never reads it — whether or not it
517
+ would have matched. On an array, `maxLength` on the attribute governs every
518
+ item; one declared under `items` is read only when the attribute declares
519
+ none. It is a positive whole number of characters: the server refuses to load
520
+ a schema where it is not.
521
+
522
+ A pattern is written for values of its attribute, not for a payload of any
523
+ size: handed several kilobytes, a pattern whose parts overlap walks the value
524
+ in quadratic time, on the event loop that answers every other request
525
+ meanwhile. The limit keeps the pattern within what it was written for. It is
526
+ **4096** unless the schema says otherwise; an attribute holding longer values
527
+ — a description, a public key — declares its own.
528
+
529
+ ```json
530
+ {
531
+ "sshPublicKey": {
532
+ "type": "array",
533
+ "items": {
534
+ "type": "string",
535
+ "maxLength": 16384,
536
+ "test": "^[^\\r\\n]*$"
478
537
  }
479
538
  }
480
539
  }
@@ -594,7 +653,7 @@ Email-related roles help applications handle email addresses consistently across
594
653
  {
595
654
  "mail": {
596
655
  "type": "string",
597
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
656
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$",
598
657
  "required": false,
599
658
  "role": "primaryEmail"
600
659
  }
@@ -609,7 +668,7 @@ Email-related roles help applications handle email addresses consistently across
609
668
  "type": "array",
610
669
  "items": {
611
670
  "type": "string",
612
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
671
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
613
672
  },
614
673
  "required": false,
615
674
  "role": "emailAliases"
@@ -686,9 +745,14 @@ Common validation patterns:
686
745
  **Email:**
687
746
 
688
747
  ```regex
689
- ^[^@\s]+@[^@\s]+\.[^@\s]+$
748
+ ^[^@\s]+@[^@\s.]+(\.[^@\s.]+)+$
690
749
  ```
691
750
 
751
+ The domain part holds no dot of its own: where it did, the pattern tried every
752
+ dot as the last separator and read the value again each time — seconds for a
753
+ value of a few kilobytes. The result is that a trailing dot (`a@b.c.`) is
754
+ refused, where the older form accepted it.
755
+
692
756
  **DN (Distinguished Name):**
693
757
 
694
758
  ```regex
@@ -1045,7 +1109,7 @@ Start with the schema that most closely matches your directory type:
1045
1109
  "attributes": {
1046
1110
  "mail": {
1047
1111
  "type": "string",
1048
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
1112
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$",
1049
1113
  "required": false,
1050
1114
  "role": "primaryEmail"
1051
1115
  },
@@ -1238,7 +1302,7 @@ Use `test` patterns to enforce data quality:
1238
1302
  },
1239
1303
  "mail": {
1240
1304
  "type": "string",
1241
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
1305
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
1242
1306
  }
1243
1307
  }
1244
1308
  ```
@@ -8,6 +8,7 @@ This guide will help you create powerful, production-ready plugins for LDAP-Rest
8
8
  - [Plugin Structure](#plugin-structure)
9
9
  - [Creating Your Plugin](#creating-your-plugin)
10
10
  - [Custom Configuration Options](#custom-configuration-options)
11
+ - [Plugin options](./options.md)
11
12
  - [Loading Your Plugin](#loading-your-plugin)
12
13
  - [Plugin Features](#plugin-features)
13
14
  - [Configuration Best Practices](#configuration-best-practices)
@@ -59,8 +60,11 @@ export default class MyPlugin extends DmPlugin {
59
60
 
60
61
  - **this.server**: DM server instance
61
62
  - **this.config**: Complete configuration object
63
+ - **this.options(declarations)**: Values of the options the plugin declares,
64
+ typed and checked, see [plugin options](options.md)
62
65
  - **this.logger**: Winston logger instance
63
- - **this.registeredHooks**: Access to global hook registry
66
+ - **this.hookBus**: The server's hooks: chain, notify and wait on them, see
67
+ [hooks](hooks.md#the-bus)
64
68
  - **this.fetch**: `fetch` timed into the
65
69
  [outbound metrics](../usage/operations.md#metrics) under the plugin's
66
70
  name; the lint refuses a bare `fetch` or `node-fetch` under `src`, and a
@@ -272,99 +276,15 @@ npx ldap-rest
272
276
 
273
277
  ## Custom Configuration Options
274
278
 
275
- LDAP-Rest automatically accepts and stores any unknown command-line options in the config object. This allows your plugin to define custom configuration without modifying core files.
279
+ A core plugin declares its options next to its code, in a
280
+ `<plugin>.options.ts` file, and reads them through `this.options()` with their
281
+ types, defaults and checks. See [plugin options](options.md).
276
282
 
277
- ### How It Works
278
-
279
- 1. **Command-line option format**: `--option-name value`
280
- 2. **Converted to config key**: `option_name` (dashes become underscores)
281
- 3. **Environment variable format**: `DM_OPTION_NAME` (uppercase with underscores)
282
- 4. **Access in plugin**: `this.config.option_name`
283
-
284
- ### Single Value Options
285
-
286
- ```bash
287
- # Command line
288
- npx ldap-rest \
289
- --webhook-url https://example.com/webhook \
290
- --webhook-timeout 5000 \
291
- --webhook-retry true
292
-
293
- # Environment variables
294
- export DM_WEBHOOK_URL=https://example.com/webhook
295
- export DM_WEBHOOK_TIMEOUT=5000
296
- export DM_WEBHOOK_RETRY=true
297
- ```
298
-
299
- In your plugin:
300
-
301
- ```typescript
302
- constructor(server: DM) {
303
- super(server);
304
-
305
- // Access custom options (converted to snake_case)
306
- const url = this.config.webhook_url; // string
307
- const timeout = this.config.webhook_timeout; // string (convert if needed)
308
- const retry = this.config.webhook_retry; // string 'true' (convert if needed)
309
-
310
- // Type conversion
311
- const timeoutNum = parseInt(timeout as string || '3000');
312
- const retryBool = (retry as string) === 'true';
313
- }
314
- ```
315
-
316
- ### Multiple Value Options
317
-
318
- Repeat the same option multiple times to create an array:
319
-
320
- ```bash
321
- # Command line
322
- npx ldap-rest \
323
- --allowed-domain example.com \
324
- --allowed-domain test.com \
325
- --allowed-domain dev.com
326
-
327
- # Environment variable (comma or semicolon separated)
328
- export DM_ALLOWED_DOMAINS=example.com,test.com,dev.com
329
- ```
330
-
331
- In your plugin:
332
-
333
- ```typescript
334
- constructor(server: DM) {
335
- super(server);
336
-
337
- // Access as array (note: singular becomes plural in config key)
338
- const domains = this.config.allowed_domain as string[];
339
- // or sometimes stored as: this.config.allowed_domains
340
-
341
- this.logger.info(`Allowed domains: ${domains.join(', ')}`);
342
- }
343
- ```
344
-
345
- ### Configuration Priority
346
-
347
- Values are resolved in this order (later overrides earlier):
348
-
349
- 1. **Default value** in your plugin
350
- 2. **Environment variable** (`DM_OPTION_NAME`)
351
- 3. **Command-line argument** (`--option-name`)
352
-
353
- ```typescript
354
- constructor(server: DM) {
355
- super(server);
356
-
357
- // Provide defaults for optional configuration
358
- const webhookUrl = this.config.webhook_url || 'http://localhost:8080/webhook';
359
- const webhookTimeout = parseInt(this.config.webhook_timeout as string || '3000');
360
- const maxRetries = parseInt(this.config.max_retries as string || '3');
361
-
362
- // Validate required configuration
363
- if (!this.config.webhook_secret) {
364
- throw new Error('--webhook-secret is required for authentication');
365
- }
366
- }
367
- ```
283
+ A plugin outside the repository cannot add to `src/config/args.ts` yet. It keeps
284
+ reading `this.config.<name>`: LDAP-Rest accepts and stores unknown
285
+ `--option-name value` pairs under `option_name`, with `--option-name` and
286
+ `DM_OPTION_NAME` overriding in that order. See
287
+ [configuration](../usage/configuration.md#environment-variables).
368
288
 
369
289
  ---
370
290
 
@@ -684,7 +604,15 @@ hooks: Hooks = {
684
604
 
685
605
  ### Exposing API Endpoints
686
606
 
687
- Create REST API endpoints for your plugin. **Important:** Always use the `asyncHandler` wrapper for async routes to ensure errors are properly handled and don't crash the server.
607
+ Declare routes with [`defineRoute`](routes.md): it types and validates the
608
+ request (`params`, `query`, `body`), answers a failing request with a 400
609
+ naming the paths at fault, and records the route for the OpenAPI document.
610
+ The `asyncHandler` form below remains for routes that have not been migrated
611
+ yet.
612
+
613
+ Create REST API endpoints for your plugin. Always use the `asyncHandler`
614
+ wrapper for async routes, so that errors are properly handled and do not
615
+ crash the server.
688
616
 
689
617
  ```typescript
690
618
  import DmPlugin, { asyncHandler } from '../../abstract/plugin';
@@ -791,6 +719,29 @@ app.get(
791
719
  );
792
720
  ```
793
721
 
722
+ **Wrapping an error keeps its answer:**
723
+
724
+ Wrap with `cause` rather than in the message alone. The status and the class
725
+ of what was thrown are read through the chain, so a refusal stays a 403 and
726
+ a timeout a 504 wherever it surfaces:
727
+
728
+ ```typescript
729
+ // ✅ GOOD: the refusal keeps its 403 when it reaches the client
730
+ throw new Error(`Failed to add group ${dn}: ${String(err)}`, { cause: err });
731
+
732
+ // ❌ BAD: a 403 becomes a 500, and its message is all that is left
733
+ throw new Error(`Failed to add group ${dn}: ${String(err)}`);
734
+ ```
735
+
736
+ **Refusing a caller:**
737
+
738
+ An authorization plugin throws `AuthzRefusedError`, whose message is for the
739
+ operator: it names the model and the branch it judged. The client is told
740
+ `AUTHZ_REFUSED` with `code: "forbidden"` instead, whatever a plugin wrapped
741
+ it in on the way. A refusal that is not an authorization plugin's is any
742
+ `ForbiddenError`, answered with its own message; `isAuthzRefusal(err)` tells
743
+ the two apart.
744
+
794
745
  **Custom error handling:**
795
746
 
796
747
  ```typescript
@@ -27,7 +27,7 @@ overrides builds a second instance, and it needs a name of its own.
27
27
  ### Hook Dependencies
28
28
 
29
29
  **Hook Providers**: Plugins that implement hooks (via `hooks: Hooks = { ... }`)
30
- **Hook Consumers**: Plugins that call hooks (via `runHook()` or `launchHooks()`)
30
+ **Hook Consumers**: Plugins that raise hooks (via `this.hookBus`)
31
31
 
32
32
  ## Complete Dependency Graph
33
33
 
@@ -601,10 +601,10 @@ refused before a plugin judges its content, or replaces it.
601
601
  2. Returns modified data to next plugin
602
602
  3. Last plugin's return value is used
603
603
 
604
- For non-chained hooks:
604
+ For notified hooks, see [hooks](hooks.md#the-bus):
605
605
 
606
- - All hooks execute in parallel
607
- - Order doesn't matter
606
+ - All hooks run one after the other, in load order
607
+ - A failing one is logged and the next still runs
608
608
 
609
609
  **Example**: `ldapdeleterequest` chain
610
610
 
@@ -642,11 +642,16 @@ export default class MyPlugin extends DmPlugin {
642
642
 
643
643
  ### Providing Hooks
644
644
 
645
+ A hook is declared in `src/hooks.ts` before anything raises it, and raised
646
+ through the bus of the kind it is, see [hooks](hooks.md#the-bus):
647
+
645
648
  ```typescript
646
649
  export default class MyPlugin extends DmPlugin {
647
650
  async someMethod() {
648
- // Fire a hook for other plugins to consume
649
- await this.server.launchHooks('myCustomHook', [param1, param2]);
651
+ // Thread a value through the subscribers of a chained hook
652
+ value = await this.hookBus.chain('ldapsearchopts', value);
653
+ // Tell the subscribers of a notified one, without waiting for them
654
+ this.hookBus.notify('ldapadddone', dn, entry, changeContext(req));
650
655
  }
651
656
  }
652
657
  ```
@@ -1,34 +1,143 @@
1
1
  # Core hooks
2
2
 
3
- The hooks a plugin can register. Their arguments and return values are
4
- those typed in [hooks.ts](../../src/hooks.ts), which is the reference.
3
+ Hooks are how plugins talk to each other. Each one is declared in
4
+ [hooks.ts](../../src/hooks.ts), which is the reference for the arguments it
5
+ gets and what it must answer; a plugin subscribing to a hook it does not
6
+ name there does not compile.
5
7
 
6
- ## Demo hooks
8
+ ## The bus
7
9
 
8
- - **hello**: called by [helloworld demo plugin](../../src/plugins/demo/helloworld.ts)
10
+ `this.hookBus` (or `server.hookBus`) is how a plugin raises a hook, and how a
11
+ test waits for one. It has one method per kind of hook, told apart by what a
12
+ subscriber answers:
9
13
 
10
- ## [LDAP](../../src/lib/ldapActions.ts) hooks
14
+ | Kind | Method | Subscriber |
15
+ | -------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------ |
16
+ | chained | `chain(name, value)` | answers the value, changed or not |
17
+ | notified | `notify(name, ...args)` | answers nothing, not awaited |
18
+ | notified | `notifyAndWait(name, ...args)` | answered nothing, awaited |
19
+ | notified | `notifyStrict(name, ...args)` | as `notifyAndWait`, but a failure goes to the caller |
20
+ | any | `has(name)`, `subscribers(name)` | — |
21
+ | any | `settled()` | resolves once every notification so far has run |
22
+ | any | `set`, `subscribe`, `insert`, `unsubscribe`, `toLast` | add a subscriber, or move one: for a test, or for a plugin changing what its base declares |
11
23
 
12
- - **ldapsearchopts**: called before any ldapsearch to modify search options
13
- - **ldapsearchresult**: called after any ldapsearch to modify search result
14
- - **ldapaddrequest**: called before any ldapadd
15
- - **ldapadddone**: launched once an ldapadd has written the entry, not awaited
16
- - **ldapaddafter**: awaited once an ldapadd has written the entry, before it
17
- answers, see [below](#ldapaddafter)
18
- - **ldapmodifyrequest**: called before any ldapmodify
19
- - **ldapdeleterequest**: called before any ldapdelete
20
- - **ldaprenamerequest**: called before any ldap rename/modifyDN operation
21
- - **ldapmodifyend**, **ldapdeleteend**, **ldaprenameend**: launched once a
22
- modify, delete or rename is over, whatever became of it, see
23
- [below](#ldapmodifyend-ldapdeleteend-ldaprenameend)
24
+ A chained hook is handed its arguments as one value, a tuple when it has
25
+ several. A notified hook is handed them one by one: its subscriber is
26
+ `(dn, entry, context) => …`, never `([dn, entry], context) => …` — that form
27
+ still compiles, the parameters being bivariant, but then reads the DN's first
28
+ character as the DN.
29
+
30
+ - **Chained.** The value goes from one subscriber to the next, each getting
31
+ what the one before answered; the last answer is the result. A subscriber
32
+ that throws stops the chain and the error travels up: a refusal is a
33
+ refusal. Several values are passed as one tuple.
34
+ - **Notified.** Subscribers run one after the other, outside the request
35
+ that caused the write, and are told what happened and was judged: they
36
+ react as the server — synchronizing, publishing, keeping invariants —
37
+ whoever asked. `notify` does not wait for them and never fails: the write
38
+ they react to is done, and a failing subscriber is logged, with its plugin
39
+ and hook, and counted in the [hook failure
40
+ metrics](../usage/operations.md#metrics), then the next one still runs.
41
+ - **`notifyStrict`** is for the rare subscriber whose failure is part of the
42
+ answer: a logout token reaches the provider only once what it closes is
43
+ recorded, so the library answers 400 and the provider retries. Every
44
+ subscriber runs even when one fails, and the first failure is raised once
45
+ they all have.
46
+
47
+ A test that would otherwise sleep waits instead:
48
+
49
+ ```ts
50
+ // One hook's subscribers
51
+ await server.hookBus.notifyAndWait('ldapadddone', dn, entry, {});
52
+ // or whatever the write under test started
53
+ await server.hookBus.settled();
54
+ ```
55
+
56
+ ## Hooks
57
+
58
+ <!-- hooks: generated from src/hooks.ts, do not edit by hand -->
59
+
60
+ - **hello**: raised by the [demo plugin](../../src/plugins/demo/helloworld.ts) for `/hello`, answered by the demo plugins
61
+ - **ldapsearchopts**: chained before any search, with its options
62
+ - **ldapsearchrequest**: chained before any search, with `[base, options, request]`; an authorization plugin refuses by throwing
63
+ - **ldapsearchresult**: chained after any search, with its result, before it is cached
64
+ - **ldapsearchfilter**: chained on the result of a search after the cache, once per page, with `[result, request, options]`, to drop what the caller may not see
65
+ - **ldapaddrequest**: chained before any add, with `[dn, entry, request]`
66
+ - **ldapadddone**: notified once an add has written the entry, with the DN, the entry and the context, one by one; not awaited
67
+ - **ldapaddafter**: notified once an add has written the entry, before it answers, with the DN, the entry and the context, one by one, see [below](#ldapaddafter)
68
+ - **ldapmodifyrequest**: chained before any modify, with `[dn, changes, op, request]`
69
+ - **ldapmodifydone**: notified once a modify is written, with the DN, the changes, the operation number and the context, one by one
70
+ - **ldapmodifyend**: notified once a modify is over, whatever became of it, and its done subscribers have returned
71
+ - **ldapdeleterequest**: chained before any delete, with `[dn, request]`
72
+ - **ldapdeletedone**: notified once a delete is written, with the DN and the context, one by one
73
+ - **ldapdeleteend**: notified once a delete is over, whatever became of it, and its done subscribers have returned
74
+ - **ldaprenamerequest**: chained before any rename or move, with `[dn, newDn, request]`
75
+ - **ldaprenamedone**: notified once a rename or a move is written, with the DN, its new DN and the context, one by one
76
+ - **ldaprenameend**: notified once a rename is over, whatever became of it, and its done subscribers have returned
77
+ - **oidcsessionvalid**: chained with the claims of a session and the verdict so far, see [the OpenID Connect page](../usage/plugins/auth/openid-connect.md)
78
+ - **oidclogouttoken**: notified with the claims of a session a logout token or `/logout` ends
79
+ - **oidclogin**: notified with the claims of a session just established
80
+ - **ldapgroupvalidatenew**: chained before a group is created, with `[dn, entry]`
81
+ - **ldapgroupadd**: chained before a group is added, with `[dn, entry]`
82
+ - **ldapgroupadddone**: notified once a group is added, with the DN and the entry, one by one
83
+ - **ldapgroupvalidatechanges**: chained before a group is modified, with `[dn, changes]`
84
+ - **ldapgroupmodify**: chained before a group is modified, with `[dn, changes, op]`
85
+ - **ldapgroupmodifydone**: notified once a group is modified, with the DN, the changes and the operation number, one by one
86
+ - **ldapgrouprename**: chained before a group is renamed, with `[dn, newDn]`
87
+ - **ldapgrouprenamedone**: notified once a group is renamed, with the DN and its new DN, one by one
88
+ - **ldapgroupdelete**: chained before a group is deleted, with its DN
89
+ - **ldapgroupdeletedone**: notified once a group is deleted, with its DN
90
+ - **ldapgroupaddmember**: chained before members are added, with `[cn, members]`
91
+ - **ldapgroupdeletemember**: chained before a member is removed, with `[cn, member]`
92
+ - **ldapgroupvalidatemembers**: chained before a member is added, to judge the members, with `[dn, members]`
93
+ - **\_ldapgrouplist**: chained around the list of groups, internal to the groups plugin
94
+ - **onLdapEntryChange**: notified with the entry before and after any write, and who made it
95
+ - **onLdapChange**: notified with the attributes a write changed
96
+ - **onLdapMailChange**: notified with the mail before and after a change, or with no new mail for a delete
97
+ - **onLdapAliasChange**: notified with the mail and the aliases before and after a change
98
+ - **onLdapQuotaChange**: notified with the mail and the quota before and after a change
99
+ - **onLdapForwardChange**: notified with the mail and the forwards before and after a change
100
+ - **onLdapDisplayNameChange**: notified with the name before and after a change
101
+ - **onLdapDriveQuotaChange**: notified with the drive quota before and after a change
102
+ - **externaluserentry**: chained before an external member is created, with `[dn, entry]`
103
+ - **externaluseradded**: notified once an external member is created, with the DN and the entry, one by one
104
+ - **beforeAuth**: chained on a request before it is authenticated, with `[request, response]`
105
+ - **afterAuth**: chained on a request once it is authenticated, with `[request, response]`
106
+ - **getOrganisationTop**: chained before the top organization is answered, with `[request, entry]`
107
+ - **scimusercreate**: chained before a SCIM user is created, with `[user, request]`
108
+ - **scimusercreatedone**: notified once a SCIM user is created
109
+ - **scimuserupdate**: chained before a SCIM user is replaced, with `[id, user, request]`
110
+ - **scimuserupdatedone**: notified once a SCIM user is replaced, with id, user (one by one)
111
+ - **scimuserdelete**: chained before a SCIM user is deleted, with `[id, request]`
112
+ - **scimuserdeletedone**: notified once a SCIM user is deleted, with its id
113
+ - **scimgroupcreate**: chained before a SCIM group is created, with `[group, request, base]`
114
+ - **scimgroupcreatedone**: notified once a SCIM group is created
115
+ - **scimgroupupdate**: chained before a SCIM group is replaced, with `[id, group, request]`
116
+ - **scimgroupupdatedone**: notified once a SCIM group is replaced, with id, group (one by one)
117
+ - **scimgroupdelete**: chained before a SCIM group is deleted, with `[id, request]`
118
+ - **scimgroupdeletedone**: notified once a SCIM group is deleted, with its id
119
+ - **scimbulkdone**: notified once a SCIM bulk request is answered
120
+ - **twakedeletionreplay**: notified with the DN, the entry and the context, one by one when a delete asks of an entry that is already a tombstone
121
+ <!-- /hooks -->
122
+
123
+ ## Flat entities
124
+
125
+ A plugin built on `LdapFlat` (`--ldap-flat-schema`) raises one set of hooks
126
+ per entity: `ldap${entity.name}` followed by the suffix of the shared hook of
127
+ the same kind — `add` and `adddone`, `modify` and `modifydone`, `rename` and
128
+ `renamedone`, `delete` and `deletedone`, with the same arguments — plus
129
+ `{prefix}move` before an entry is moved, followed by `onLdapChange` as a
130
+ modify is. A plugin watching one declares it beside its other hooks.
131
+
132
+ ## LDAP write hooks
24
133
 
25
134
  ### ldapaddafter
26
135
 
27
136
  For what must follow an add within the same request, such as provisioning
28
137
  the account the entry describes.
29
138
 
30
- - Subscribers get `[dn, entry]` and the change context, as `ldapadddone`
31
- does. They run one after the other, and `add()` answers once the last has
139
+ - Subscribers get the DN, the entry and the change context, one by one, as
140
+ `ldapadddone` does. They run one after the other, and `add()` answers once the last has
32
141
  returned.
33
142
  - An error is logged and goes no further: the add has succeeded, and the
34
143
  next subscriber still runs. A subscriber that can fail owns its repair
@@ -38,7 +147,8 @@ the account the entry describes.
38
147
  empty. Some of these follow an API call all the same, so select entries by
39
148
  their DN or attributes rather than by an empty context.
40
149
  - The entry is the one submitted, after `ldapaddrequest`, not what the
41
- directory holds: no operational attributes, and the password as given.
150
+ directory holds: no operational attributes, and a `Buffer` value — a
151
+ password sent that way — read as a string.
42
152
  - `ldapadddone` and `onLdapEntryChange` fire whatever this hook does, and in
43
153
  no set order with it: an add event may or may not include what a
44
154
  subscriber here wrote.
@@ -65,8 +175,17 @@ never reaches its "done" hook; its "end" hook fires all the same.
65
175
  its `ldaprenamedone` runs outside any operation, even when a request hook
66
176
  made it.
67
177
 
68
- ## [onChange](../usage/plugins/ldap/on-change.md) hooks
178
+ ## onLdapChange
69
179
 
70
180
  - **onLdapEntryChange**: called after any write, with the entry before and
71
- after it, and who made it
72
- - **onLdapChange**, **onLdapMailChange**…: derived from it
181
+ after it, and who made it.
182
+ - **onLdapChange**, **onLdapMailChange**…: derived from it, and the
183
+ attribute ones fire only while the entry has the attribute configured for
184
+ them. A delete fires `onLdapEntryChange`, `onLdapChange` and
185
+ `onLdapMailChange` only.
186
+
187
+ ## Ordering
188
+
189
+ Hooks run in the order their plugins loaded, but for the request hooks of
190
+ authorization plugins, which run before every other plugin's, see
191
+ [dependencies](dependencies.md#hook-execution-order).