ldap-rest 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (337) 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/scim.js +1 -1
  71. package/dist/plugins/scim/scim.options.js +1 -0
  72. package/dist/plugins/scim/users.js +1 -1
  73. package/dist/plugins/static.js +1 -1
  74. package/dist/plugins/static.options.js +1 -0
  75. package/dist/plugins/storage/file.js +1 -1
  76. package/dist/plugins/storage/ldap.js +1 -1
  77. package/dist/plugins/storage/postgres.js +1 -1
  78. package/dist/plugins/storage/valkey.js +1 -1
  79. package/dist/plugins/storage.js +1 -1
  80. package/dist/plugins/storage.options.js +1 -0
  81. package/dist/plugins/twake/appAccounts.options.js +1 -0
  82. package/dist/plugins/twake/appAccountsApi.js +1 -1
  83. package/dist/plugins/twake/appAccountsConsistency.js +1 -1
  84. package/dist/plugins/twake/calendar.js +1 -1
  85. package/dist/plugins/twake/calendar.options.js +1 -0
  86. package/dist/plugins/twake/calendarResources.js +1 -1
  87. package/dist/plugins/twake/cloudery.options.js +1 -0
  88. package/dist/plugins/twake/clouderyProvision.js +1 -1
  89. package/dist/plugins/twake/cozy.options.js +1 -0
  90. package/dist/plugins/twake/cozyProvision.js +1 -1
  91. package/dist/plugins/twake/drive.js +1 -1
  92. package/dist/plugins/twake/drive.options.js +1 -0
  93. package/dist/plugins/twake/groups.js +1 -1
  94. package/dist/plugins/twake/instances.js +1 -1
  95. package/dist/plugins/twake/james.js +1 -1
  96. package/dist/plugins/twake/james.options.js +1 -0
  97. package/dist/plugins/twake/lifecycleAttributes.js +1 -1
  98. package/dist/plugins/twake/lifecycleEvents.js +1 -1
  99. package/dist/plugins/twake/migrationProxy.js +1 -1
  100. package/dist/plugins/twake/migrationProxy.options.js +1 -0
  101. package/dist/plugins/twake/spaces.js +1 -1
  102. package/dist/plugins/twake/tombstone.js +1 -1
  103. package/dist/plugins/twake/twakeGroups.options.js +1 -0
  104. package/dist/plugins/twake/twakeInstances.options.js +1 -0
  105. package/dist/plugins/twake/twakeLifecycle.options.js +1 -0
  106. package/dist/plugins/twake/twakeSpaces.options.js +1 -0
  107. package/dist/plugins/twake/twakeTombstone.options.js +1 -0
  108. package/dist/plugins/weblogs.js +1 -1
  109. package/dist/src/abstract/ldapFlat.d.ts +28 -10
  110. package/dist/src/abstract/plugin.d.ts +10 -5
  111. package/dist/src/abstract/twakePlugin.d.ts +14 -4
  112. package/dist/src/bin/index.d.ts +17 -4
  113. package/dist/src/config/args.d.ts +2 -4
  114. package/dist/src/config/options.d.ts +89 -0
  115. package/dist/src/config/schema.d.ts +50 -1
  116. package/dist/src/hooks.d.ts +60 -22
  117. package/dist/src/lib/authz/base.d.ts +4 -51
  118. package/dist/src/lib/authz/composition.d.ts +2 -2
  119. package/dist/src/lib/entitySchema/validator.d.ts +63 -6
  120. package/dist/src/lib/errors.d.ts +56 -1
  121. package/dist/src/lib/expressFormatedResponses.d.ts +49 -7
  122. package/dist/src/lib/hookBus.d.ts +226 -0
  123. package/dist/src/lib/instanceProviders.d.ts +0 -8
  124. package/dist/src/lib/ldapActions.d.ts +7 -0
  125. package/dist/src/lib/parseConfig.d.ts +5 -0
  126. package/dist/src/lib/route.d.ts +99 -0
  127. package/dist/src/lib/utils.d.ts +31 -36
  128. package/dist/src/lib/version.d.ts +12 -0
  129. package/dist/src/plugins/auth/auth.options.d.ts +88 -0
  130. package/dist/src/plugins/auth/authzDynamic.d.ts +10 -1
  131. package/dist/src/plugins/auth/authzLinid1.d.ts +6 -0
  132. package/dist/src/plugins/auth/authzPerBranch.d.ts +9 -0
  133. package/dist/src/plugins/auth/authzPerRoute.d.ts +3 -0
  134. package/dist/src/plugins/auth/authzScope.d.ts +23 -14
  135. package/dist/src/plugins/auth/crowdsec.d.ts +5 -0
  136. package/dist/src/plugins/auth/fake.d.ts +3 -0
  137. package/dist/src/plugins/auth/hmac.d.ts +4 -0
  138. package/dist/src/plugins/auth/llng.d.ts +4 -0
  139. package/dist/src/plugins/auth/openidconnect.d.ts +7 -0
  140. package/dist/src/plugins/auth/rateLimit.d.ts +4 -0
  141. package/dist/src/plugins/auth/token.d.ts +3 -0
  142. package/dist/src/plugins/auth/totp.d.ts +5 -0
  143. package/dist/src/plugins/auth/trustedProxy.d.ts +4 -0
  144. package/dist/src/plugins/bcl.d.ts +3 -0
  145. package/dist/src/plugins/bcl.options.d.ts +8 -0
  146. package/dist/src/plugins/configApi.d.ts +24 -5
  147. package/dist/src/plugins/demo/helloworld.d.ts +8 -19
  148. package/dist/src/plugins/ldap/accountLifecycle.d.ts +17 -0
  149. package/dist/src/plugins/ldap/bulkImport.d.ts +6 -76
  150. package/dist/src/plugins/ldap/departmentSync.d.ts +1 -0
  151. package/dist/src/plugins/ldap/enterpriseRules.d.ts +8 -1
  152. package/dist/src/plugins/ldap/externalUsersInGroups.d.ts +4 -0
  153. package/dist/src/plugins/ldap/flatGeneric.d.ts +4 -0
  154. package/dist/src/plugins/ldap/groups.d.ts +27 -11
  155. package/dist/src/plugins/ldap/ldap.options.d.ts +75 -0
  156. package/dist/src/plugins/ldap/onChange.d.ts +10 -2
  157. package/dist/src/plugins/ldap/organizations.d.ts +16 -0
  158. package/dist/src/plugins/ldap/passwordPolicy.d.ts +32 -1
  159. package/dist/src/plugins/ldap/raw.d.ts +50 -19
  160. package/dist/src/plugins/ldap/trash.d.ts +6 -0
  161. package/dist/src/plugins/metrics.d.ts +3 -0
  162. package/dist/src/plugins/metrics.options.d.ts +8 -0
  163. package/dist/src/plugins/rabbitmq.d.ts +3 -0
  164. package/dist/src/plugins/rabbitmq.options.d.ts +8 -0
  165. package/dist/src/plugins/scim/baseResolver.d.ts +2 -1
  166. package/dist/src/plugins/scim/bulk.d.ts +6 -7
  167. package/dist/src/plugins/scim/discovery.d.ts +3 -0
  168. package/dist/src/plugins/scim/errors.d.ts +2 -1
  169. package/dist/src/plugins/scim/groups.d.ts +6 -4
  170. package/dist/src/plugins/scim/scim.d.ts +37 -3
  171. package/dist/src/plugins/scim/scim.options.d.ts +35 -0
  172. package/dist/src/plugins/scim/users.d.ts +6 -7
  173. package/dist/src/plugins/static.d.ts +9 -0
  174. package/dist/src/plugins/static.options.d.ts +13 -0
  175. package/dist/src/plugins/storage.d.ts +12 -1
  176. package/dist/src/plugins/storage.options.d.ts +16 -0
  177. package/dist/src/plugins/twake/appAccounts.options.d.ts +13 -0
  178. package/dist/src/plugins/twake/appAccountsApi.d.ts +18 -0
  179. package/dist/src/plugins/twake/appAccountsConsistency.d.ts +8 -0
  180. package/dist/src/plugins/twake/calendar.d.ts +25 -2
  181. package/dist/src/plugins/twake/calendar.options.d.ts +19 -0
  182. package/dist/src/plugins/twake/cloudery.options.d.ts +26 -0
  183. package/dist/src/plugins/twake/clouderyProvision.d.ts +18 -0
  184. package/dist/src/plugins/twake/cozy.options.d.ts +23 -0
  185. package/dist/src/plugins/twake/cozyProvision.d.ts +14 -0
  186. package/dist/src/plugins/twake/drive.d.ts +7 -0
  187. package/dist/src/plugins/twake/drive.options.d.ts +15 -0
  188. package/dist/src/plugins/twake/groups.d.ts +29 -1
  189. package/dist/src/plugins/twake/instances.d.ts +43 -10
  190. package/dist/src/plugins/twake/james.d.ts +17 -0
  191. package/dist/src/plugins/twake/james.options.d.ts +21 -0
  192. package/dist/src/plugins/twake/lifecycleEvents.d.ts +12 -0
  193. package/dist/src/plugins/twake/migrationProxy.d.ts +11 -11
  194. package/dist/src/plugins/twake/migrationProxy.options.d.ts +16 -0
  195. package/dist/src/plugins/twake/spaces.d.ts +30 -0
  196. package/dist/src/plugins/twake/tombstone.d.ts +16 -14
  197. package/dist/src/plugins/twake/twakeGroups.options.d.ts +20 -0
  198. package/dist/src/plugins/twake/twakeInstances.options.d.ts +43 -0
  199. package/dist/src/plugins/twake/twakeLifecycle.options.d.ts +21 -0
  200. package/dist/src/plugins/twake/twakeSpaces.options.d.ts +19 -0
  201. package/dist/src/plugins/twake/twakeTombstone.options.d.ts +17 -0
  202. package/docs/README.md +1 -0
  203. package/docs/client-development/api/rest-api.md +9 -3
  204. package/docs/client-development/schemas/README.md +96 -32
  205. package/docs/plugin-development/README.md +45 -94
  206. package/docs/plugin-development/dependencies.md +11 -6
  207. package/docs/plugin-development/hooks.md +142 -23
  208. package/docs/plugin-development/openapi.md +14 -1
  209. package/docs/plugin-development/options.md +109 -0
  210. package/docs/plugin-development/routes.md +152 -0
  211. package/docs/plugin-development/testing.md +3 -3
  212. package/docs/usage/configuration.md +18 -11
  213. package/docs/usage/operations.md +19 -4
  214. package/docs/usage/plugins/ldap/enterprise-rules.md +10 -1
  215. package/docs/usage/plugins/ldap/flat-generic.md +4 -0
  216. package/docs/usage/plugins/ldap/raw.md +17 -1
  217. package/docs/usage/upgrading.md +194 -0
  218. package/package.json +358 -277
  219. package/static/browser/ldap-browser/LdapBrowser.js +8 -6
  220. package/static/browser/ldap-browser/LdapBrowser.js.map +1 -1
  221. package/static/browser/ldap-browser/api/RawApiClient.js +2 -0
  222. package/static/browser/ldap-browser/api/RawApiClient.js.map +1 -1
  223. package/static/browser/ldap-browser/components/EntryTree.js +9 -5
  224. package/static/browser/ldap-browser/components/EntryTree.js.map +1 -1
  225. package/static/browser/ldap-browser/components/EntryView.js +2 -0
  226. package/static/browser/ldap-browser/components/EntryView.js.map +1 -1
  227. package/static/browser/ldap-browser/schema.js +3 -2
  228. package/static/browser/ldap-browser/schema.js.map +1 -1
  229. package/static/browser/ldap-browser.esm.js +27 -18
  230. package/static/browser/ldap-browser.esm.js.map +1 -1
  231. package/static/browser/ldap-browser.js +27 -18
  232. package/static/browser/ldap-browser.js.map +1 -1
  233. package/static/browser/ldap-browser.min.js +1 -1
  234. package/static/browser/ldap-browser.min.js.map +1 -1
  235. package/static/browser/ldap-group-editor/LdapGroupEditor.js +8 -6
  236. package/static/browser/ldap-group-editor/LdapGroupEditor.js.map +1 -1
  237. package/static/browser/ldap-group-editor/api/GroupApiClient.js +1 -0
  238. package/static/browser/ldap-group-editor/api/GroupApiClient.js.map +1 -1
  239. package/static/browser/ldap-group-editor/components/CreateGroupModal.js +5 -1
  240. package/static/browser/ldap-group-editor/components/CreateGroupModal.js.map +1 -1
  241. package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js +6 -3
  242. package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js.map +1 -1
  243. package/static/browser/ldap-group-editor/components/GroupTree.js +6 -3
  244. package/static/browser/ldap-group-editor/components/GroupTree.js.map +1 -1
  245. package/static/browser/ldap-group-editor/components/MoveGroupModal.js +6 -3
  246. package/static/browser/ldap-group-editor/components/MoveGroupModal.js.map +1 -1
  247. package/static/browser/ldap-group-editor.esm.js +32 -16
  248. package/static/browser/ldap-group-editor.esm.js.map +1 -1
  249. package/static/browser/ldap-group-editor.js +32 -16
  250. package/static/browser/ldap-group-editor.js.map +1 -1
  251. package/static/browser/ldap-group-editor.min.js +1 -1
  252. package/static/browser/ldap-group-editor.min.js.map +1 -1
  253. package/static/browser/ldap-resource-editor/api/ResourceApiClient.js +3 -0
  254. package/static/browser/ldap-resource-editor/api/ResourceApiClient.js.map +1 -1
  255. package/static/browser/ldap-resource-editor/cache/CacheManager.js +3 -1
  256. package/static/browser/ldap-resource-editor/cache/CacheManager.js.map +1 -1
  257. package/static/browser/ldap-tree-viewer/LdapTreeViewer.js +6 -1
  258. package/static/browser/ldap-tree-viewer/LdapTreeViewer.js.map +1 -1
  259. package/static/browser/ldap-tree-viewer/api/LdapApiClient.js +2 -0
  260. package/static/browser/ldap-tree-viewer/api/LdapApiClient.js.map +1 -1
  261. package/static/browser/ldap-tree-viewer/components/TreeNode.js +4 -0
  262. package/static/browser/ldap-tree-viewer/components/TreeNode.js.map +1 -1
  263. package/static/browser/ldap-tree-viewer/components/TreeRoot.js +3 -0
  264. package/static/browser/ldap-tree-viewer/components/TreeRoot.js.map +1 -1
  265. package/static/browser/ldap-tree-viewer/store/Store.js +3 -1
  266. package/static/browser/ldap-tree-viewer/store/Store.js.map +1 -1
  267. package/static/browser/ldap-tree-viewer.esm.js +18 -2
  268. package/static/browser/ldap-tree-viewer.esm.js.map +1 -1
  269. package/static/browser/ldap-tree-viewer.js +18 -2
  270. package/static/browser/ldap-tree-viewer.js.map +1 -1
  271. package/static/browser/ldap-tree-viewer.min.js +1 -1
  272. package/static/browser/ldap-tree-viewer.min.js.map +1 -1
  273. package/static/browser/ldap-unit-editor/LdapUnitEditor.js +7 -5
  274. package/static/browser/ldap-unit-editor/LdapUnitEditor.js.map +1 -1
  275. package/static/browser/ldap-unit-editor/api/UnitApiClient.js +1 -0
  276. package/static/browser/ldap-unit-editor/api/UnitApiClient.js.map +1 -1
  277. package/static/browser/ldap-unit-editor/components/MoveUnitModal.js +7 -4
  278. package/static/browser/ldap-unit-editor/components/MoveUnitModal.js.map +1 -1
  279. package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js +7 -4
  280. package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js.map +1 -1
  281. package/static/browser/ldap-unit-editor/components/UnitTree.js +6 -3
  282. package/static/browser/ldap-unit-editor/components/UnitTree.js.map +1 -1
  283. package/static/browser/ldap-unit-editor.esm.js +28 -16
  284. package/static/browser/ldap-unit-editor.esm.js.map +1 -1
  285. package/static/browser/ldap-unit-editor.js +28 -16
  286. package/static/browser/ldap-unit-editor.js.map +1 -1
  287. package/static/browser/ldap-unit-editor.min.js +1 -1
  288. package/static/browser/ldap-unit-editor.min.js.map +1 -1
  289. package/static/browser/ldap-user-editor/LdapUserEditor.js +9 -7
  290. package/static/browser/ldap-user-editor/LdapUserEditor.js.map +1 -1
  291. package/static/browser/ldap-user-editor/api/UserApiClient.js +2 -0
  292. package/static/browser/ldap-user-editor/api/UserApiClient.js.map +1 -1
  293. package/static/browser/ldap-user-editor/cache/CacheManager.js +3 -1
  294. package/static/browser/ldap-user-editor/cache/CacheManager.js.map +1 -1
  295. package/static/browser/ldap-user-editor/components/MoveUserModal.js +7 -4
  296. package/static/browser/ldap-user-editor/components/MoveUserModal.js.map +1 -1
  297. package/static/browser/ldap-user-editor/components/PointerField.js +6 -1
  298. package/static/browser/ldap-user-editor/components/PointerField.js.map +1 -1
  299. package/static/browser/ldap-user-editor/components/UserEditor.js +10 -5
  300. package/static/browser/ldap-user-editor/components/UserEditor.js.map +1 -1
  301. package/static/browser/ldap-user-editor/components/UserList.js +8 -4
  302. package/static/browser/ldap-user-editor/components/UserList.js.map +1 -1
  303. package/static/browser/ldap-user-editor/components/UserTree.js +7 -3
  304. package/static/browser/ldap-user-editor/components/UserTree.js.map +1 -1
  305. package/static/browser/ldap-user-editor.esm.js +55 -30
  306. package/static/browser/ldap-user-editor.esm.js.map +1 -1
  307. package/static/browser/ldap-user-editor.js +55 -30
  308. package/static/browser/ldap-user-editor.js.map +1 -1
  309. package/static/browser/ldap-user-editor.min.js +1 -1
  310. package/static/browser/ldap-user-editor.min.js.map +1 -1
  311. package/static/browser/shared/components/DisposableComponent.js +3 -5
  312. package/static/browser/shared/components/DisposableComponent.js.map +1 -1
  313. package/static/browser/shared/components/Modal.js +2 -1
  314. package/static/browser/shared/components/Modal.js.map +1 -1
  315. package/static/browser/shared/components/StatusMessage.js +3 -2
  316. package/static/browser/shared/components/StatusMessage.js.map +1 -1
  317. package/static/browser/shared/utils/form.js.map +1 -1
  318. package/static/browser/shared/utils/hmac.js +1 -0
  319. package/static/browser/shared/utils/hmac.js.map +1 -1
  320. package/static/browser/shared/utils/totp.js +1 -0
  321. package/static/browser/shared/utils/totp.js.map +1 -1
  322. package/static/browser/shared.esm.js +9 -8
  323. package/static/browser/shared.esm.js.map +1 -1
  324. package/static/browser/shared.js +9 -8
  325. package/static/browser/shared.js.map +1 -1
  326. package/static/browser/shared.min.js +1 -1
  327. package/static/browser/shared.min.js.map +1 -1
  328. package/static/schemas/ad/groups.json +1 -1
  329. package/static/schemas/ad/users.json +3 -3
  330. package/static/schemas/example/organizations.json +2 -2
  331. package/static/schemas/example/users.json +2 -2
  332. package/static/schemas/obm/users.json +2 -2
  333. package/static/schemas/standard/groups.json +1 -1
  334. package/static/schemas/standard/users.json +2 -2
  335. package/static/schemas/twake/groups.json +4 -4
  336. package/static/schemas/twake/teamMailboxes.json +3 -3
  337. package/static/schemas/twake/users.json +7 -7
@@ -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
- validateNewEntry(dn: string, entry: AttributesList): Promise<boolean>;
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 urlConfig Configuration key for WebAdmin URL
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, urlConfig: string, tokenConfig: string, concurrencyConfig: string);
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
@@ -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
- hooks: {
37
- [K in keyof Hooks]?: Function[];
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
- /** The headers `core/static` sends unless told otherwise: no framing by another site */
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
  *
@@ -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?: (args: [string, AttributesList], context?: ChangeContext) => MaybePromise<void>;
59
+ ldapadddone?: (dn: string, entry: AttributesList, context?: ChangeContext) => MaybePromise<void>;
60
60
  /** See docs/plugin-development/hooks.md#ldapaddafter */
61
- ldapaddafter?: (args: [string, AttributesList], context?: ChangeContext) => MaybePromise<void>;
61
+ ldapaddafter?: (dn: string, entry: AttributesList, context?: ChangeContext) => MaybePromise<void>;
62
62
  ldapmodifyrequest?: ChainedHook<[string, ModifyRequest, number, Request?]>;
63
- ldapmodifydone?: (args: [string, ModifyRequest, number], context?: ChangeContext) => MaybePromise<void>;
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?: (args: [string, string], context?: ChangeContext) => MaybePromise<void>;
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?: (args: [string, AttributesList]) => MaybePromise<void>;
125
+ ldapgroupadddone?: (dn: string, entry: AttributesList) => MaybePromise<void>;
126
126
  ldapgroupmodify?: ChainedHook<[string, ModifyRequest, number]>;
127
- ldapgroupmodifydone?: (args: [string, ModifyRequest, number]) => MaybePromise<void>;
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<AsyncGenerator<SearchResult>>;
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
- externaluseradded?: (dn: string, mail: string) => MaybePromise<void>;
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 move hooks
156
- * Pattern: {hookPrefix}move
157
- * Examples: ldapusermove, ldappositionmove, etc.
158
- *
159
- * move: before moving - can modify target or cancel
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
- Request | undefined,
169
- AttributesList | null
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}`;
@@ -10,8 +10,9 @@ import type { SearchOptions } from 'ldapts';
10
10
  import DmPlugin, { type Role } from '../../abstract/plugin';
11
11
  import type { DM } from '../../bin';
12
12
  import type { BranchPermissions } from '../../config/args';
13
+ import type { Hooks } from '../../hooks';
13
14
  import { type DmRequest } from '../auth/base';
14
- import type { AttributesList, ModifyRequest, SearchResult } from '../ldapActions';
15
+ import type { ModifyRequest } from '../ldapActions';
15
16
  /**
16
17
  * The rule of the most specific configured branch holding `target`.
17
18
  *
@@ -311,7 +312,7 @@ export default abstract class AuthzBase extends DmPlugin {
311
312
  *
312
313
  * @param req the request being authorized, which carries an identity
313
314
  * @returns the resolved user, or null when the policy is to allow it
314
- * @throws ForbiddenError when the policy is to deny it
315
+ * @throws AuthzRefusedError when the policy is to deny it
315
316
  */
316
317
  /** Said once: a rule keyed on a value the authenticator did not publish. */
317
318
  private fallbackWarned;
@@ -382,53 +383,5 @@ export default abstract class AuthzBase extends DmPlugin {
382
383
  /**
383
384
  * Common hooks for all authorization plugins
384
385
  */
385
- hooks: {
386
- /**
387
- * A rename moves the entry an identity resolves to, so what was
388
- * resolved is dropped.
389
- *
390
- * `authzLinid1` keys its permissions on the administrator's DN: renamed
391
- * or moved, they resolve to a DN no organization names any more, and
392
- * every operation of theirs is refused until the TTL runs out. The
393
- * whole map goes rather than one key, since what is cached is
394
- * identity → DN and the hook carries DNs: a handful of lookups is
395
- * cheaper than working out which identity moved.
396
- */
397
- ldaprenamedone: () => void;
398
- /**
399
- * A delete removes the entry an identity resolves to, exactly as a
400
- * rename moves it, so what was resolved is dropped the same way.
401
- *
402
- * An administrator deleted through the API is, for `authzLinid1`, still
403
- * resolved to the former DN where the organization still names them:
404
- * their session keeps its rights until the TTL runs out. Like the rename
405
- * hook, the whole map goes rather than one key — the hook carries the
406
- * deleted DN, and the cache keys identities, so a handful of lookups is
407
- * cheaper than working out which identity the DN held.
408
- */
409
- ldapdeletedone: () => void;
410
- ldapmodifyrequest: ([dn, changes, opNumber, req]: [string, ModifyRequest, number, DmRequest?]) => Promise<[string, ModifyRequest, number, DmRequest?]>;
411
- ldapaddrequest: ([dn, entry, req]: [string, AttributesList, DmRequest?]) => Promise<[string, AttributesList, DmRequest?]>;
412
- ldapsearchrequest: ([base, opts, req]: [string, SearchOptions, DmRequest?]) => Promise<[string, SearchOptions, DmRequest?]>;
413
- /**
414
- * Drop what this caller may not read from a search it was granted.
415
- *
416
- * Judged by branch, the search was granted on its base: each entry is
417
- * kept only where the caller may read it, a rule narrower than the base
418
- * refusing part of what it holds. Judged by attachment, the accounts
419
- * attached outside the branches this caller manages are dropped.
420
- *
421
- * Runs after the cache and carries the request, so what one caller may
422
- * not see never reaches another. An entry with no organization link —
423
- * an organization, a group, a nomenclature value — is left alone: those
424
- * are the reference data every administrator reads.
425
- *
426
- * So is an entry attached to the transit branch: it is waiting for an
427
- * administrator to claim it.
428
- */
429
- ldapsearchfilter: ([result, req, opts]: [SearchResult, DmRequest?, SearchOptions?]) => Promise<[SearchResult, DmRequest?, SearchOptions?]>;
430
- getOrganisationTop: ([req, defaultTop]: [DmRequest | undefined, AttributesList | null]) => Promise<[DmRequest | undefined, AttributesList | null]>;
431
- ldaprenamerequest: ([oldDn, newDn, req]: [string, string, DmRequest?]) => Promise<[string, string, DmRequest?]>;
432
- ldapdeleterequest: ([dn, req]: [string | string[], DmRequest?]) => Promise<[string | string[], DmRequest?]>;
433
- };
386
+ hooks: Hooks;
434
387
  }
@@ -5,8 +5,8 @@
5
5
  * How authorization plugins loaded together compose, and the configurations
6
6
  * where that composition is nobody's decision.
7
7
  *
8
- * Every plugin judging LDAP operations registers the same hooks, and
9
- * `launchHooksChained` runs them all: two of them compose as an AND, the
8
+ * Every plugin judging LDAP operations registers the same hooks, and the
9
+ * chain runs them all: two of them compose as an AND, the
10
10
  * first refusal winning. That is a sound rule when it is meant. It is a
11
11
  * refusal nobody can explain when it is not — `authzPerBranch` beside
12
12
  * `authzDynamic` judges a machine token by a branch configuration written