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
@@ -3,7 +3,15 @@
3
3
  The published API reference at <https://linagora.github.io/ldap-rest/api/> is
4
4
  generated from `openapi.json`, which itself is built statically by
5
5
  [`scripts/generate-openapi.ts`](../../scripts/generate-openapi.ts) — it walks
6
- every plugin's `api()` method and reads the JSDoc comments above each route.
6
+ every plugin's `api()` method and reads the JSDoc comments above each route,
7
+ and reads the declaration of every [`defineRoute`](routes.md) call.
8
+
9
+ With `defineRoute` the declaration is the document: the generator takes its
10
+ `summary`, `description`, `tags`, `params`, `query`, `body` and `responses`,
11
+ and imports the exported schema constants it names. An `@openapi` block above
12
+ the call still wins over what the declaration says — a `requestBody` it
13
+ describes replaces the declared `body` schema whole, rather than merging into
14
+ it — and a route registered with `app.<verb>(` still needs one.
7
15
 
8
16
  You don't need to touch the generator to enrich your plugin's documentation.
9
17
  Two JSDoc directives drive the entire pipeline:
@@ -139,6 +147,11 @@ npm run generate:openapi # writes openapi.json
139
147
  npm run build:pages # rebuilds the static site under _site/
140
148
  ```
141
149
 
150
+ `openapi.json` is committed: it is what the site publishes, so a change to a
151
+ route or to a schema it declares must come with a regenerated document. CI
152
+ runs `npm run check:openapi`, which builds the spec and fails when the file
153
+ differs, naming the first line at fault.
154
+
142
155
  Open `_site/api/index.html` to preview the Redoc rendering. The published
143
156
  site at <https://linagora.github.io/ldap-rest/api/> is rebuilt automatically
144
157
  on every `v*` tag push.
@@ -0,0 +1,109 @@
1
+ # Plugin options
2
+
3
+ A core plugin declares its configuration options next to its code, in a
4
+ `<plugin>.options.ts` file. A family that shares imports and lifecycle keeps
5
+ one set per plugin in a single `<family>.options.ts` file, named after the
6
+ family. `src/config/args.ts` concatenates those declarations,
7
+ so the parser, the command line and the environment variables see the same
8
+ entries as before. The plugin then reads typed values instead of casting
9
+ `this.config`.
10
+
11
+ ## Declaring options
12
+
13
+ ```typescript
14
+ // src/plugins/example/example.options.ts
15
+ import { defineOptions, opt, type OptionsOf } from '../../config/options';
16
+
17
+ export const exampleOptions = defineOptions({
18
+ example_url: opt.string({
19
+ env: 'DM_EXAMPLE_URL',
20
+ required: true,
21
+ }),
22
+ example_timeout: opt.number({
23
+ env: 'DM_EXAMPLE_TIMEOUT',
24
+ default: 5000,
25
+ min: 1,
26
+ }),
27
+ example_retry: opt.boolean({
28
+ env: 'DM_EXAMPLE_RETRY',
29
+ default: false,
30
+ }),
31
+ example_domains: opt.array({
32
+ env: 'DM_EXAMPLE_DOMAINS',
33
+ default: [],
34
+ plural: 'example-domains',
35
+ kind: 'identifiers',
36
+ }),
37
+ });
38
+
39
+ export type ExampleOptions = OptionsOf<typeof exampleOptions>;
40
+ ```
41
+
42
+ The key is the `Config` name: `example_timeout` is `--example-timeout`,
43
+ `DM_EXAMPLE_TIMEOUT` when the descriptor names that variable.
44
+
45
+ Each declaration takes:
46
+
47
+ | Field | Meaning |
48
+ | ---------- | ------------------------------------------------------------------- |
49
+ | `env` | Environment variable read when the CLI does not set the option |
50
+ | `default` | Value kept when neither the CLI nor the environment sets the option |
51
+ | `required` | Refuse an empty value instead of reading it as unset |
52
+ | `plural` | Array only: CLI name of the plural form, without the leading `--` |
53
+ | `kind` | Array only: how a value splits, see `ArrayKind` in `parseConfig.ts` |
54
+ | `min/max` | Number only: bounds checked when the option is read |
55
+ | `choices` | String only: the values the option accepts |
56
+
57
+ ## Reading options
58
+
59
+ The plugin names the declaration as a static field, and asks for the values in
60
+ its constructor:
61
+
62
+ ```typescript
63
+ export default class Example extends DmPlugin {
64
+ static optionDefinitions = exampleOptions;
65
+ name = 'example';
66
+
67
+ private readonly settings: ExampleOptions;
68
+
69
+ constructor(server: DM) {
70
+ super(server);
71
+ this.settings = this.options(exampleOptions);
72
+ }
73
+ }
74
+ ```
75
+
76
+ `options(exampleOptions)` applies the defaults, copies array defaults and
77
+ checks `required`, `min`, `max` and `choices`. The base constructor asks once,
78
+ so a bad value stops the plugin from loading rather than failing at the first
79
+ request. Each later call reads the configuration again rather than keeping
80
+ the first values. `--plugin example:name:{...}` overrides still work: the view the
81
+ plugin receives carries the merged configuration, and `options()` reads that
82
+ view. Give it the same declaration object the class names in
83
+ `optionDefinitions`: any other one is refused, so the generic type parameter
84
+ cannot describe the wrong view.
85
+
86
+ ## Wiring it in
87
+
88
+ Import the declaration in `src/config/args.ts` and spread it where the plugin's
89
+ options used to be:
90
+
91
+ ```typescript
92
+ import { exampleOptions } from '../plugins/example/example.options';
93
+ import { toConfigEntries } from './options';
94
+
95
+ const configArgs: ConfigTemplate = [
96
+ // ...
97
+ ...toConfigEntries(exampleOptions),
98
+ ];
99
+ ```
100
+
101
+ The `Config` interface keeps the hand-written fields until every plugin has
102
+ moved. A migrated plugin no longer reads them: it uses `options()`.
103
+
104
+ Plugins outside the repository cannot add to `args.ts` yet, but they can
105
+ declare options the same way and name them in `optionDefinitions`: the startup
106
+ report subtracts those keys, and `options()` reads a declared `env` variable
107
+ when the parser did not carry the key. An option without a declaration still
108
+ reaches `this.config` from an unknown `--key value` pair, and the startup
109
+ report names it.
@@ -0,0 +1,152 @@
1
+ # Declaring routes
2
+
3
+ `defineRoute` declares an HTTP route once: its inputs, their schemas, what
4
+ the OpenAPI document says of it, and the handler. The schemas are checked
5
+ before the handler runs, the openapi generator describes the route from the
6
+ declaration, and the route probe finds it in the registry.
7
+
8
+ ```typescript
9
+ import { Type } from '@sinclair/typebox';
10
+ import type { Express } from 'express';
11
+
12
+ import DmPlugin, { defineRoute, type Role } from '../../abstract/plugin';
13
+
14
+ /** Exported: the OpenAPI generator reads it without running the plugin. */
15
+ export const GroupParams = Type.Object({
16
+ cn: Type.String({ minLength: 1 }),
17
+ });
18
+
19
+ export const GroupQuery = Type.Object({
20
+ dryRun: Type.Optional(Type.Boolean()),
21
+ });
22
+
23
+ export const GroupMembers = Type.Object(
24
+ { member: Type.Union([Type.String(), Type.Array(Type.String())]) },
25
+ { additionalProperties: false }
26
+ );
27
+
28
+ export const MembersAdded = Type.Object({ added: Type.Number() });
29
+
30
+ export default class MyPlugin extends DmPlugin {
31
+ name = 'myPlugin';
32
+ roles: Role[] = ['api'] as const;
33
+
34
+ api(app: Express): void {
35
+ defineRoute(app, {
36
+ method: 'post',
37
+ path: `${this.config.api_prefix}/v1/ldap/groups/:cn/members`,
38
+ params: GroupParams,
39
+ query: GroupQuery,
40
+ body: GroupMembers,
41
+ responses: {
42
+ 200: { schema: MembersAdded, description: 'Members added' },
43
+ },
44
+ summary: 'Add members to a group',
45
+ tags: ['Groups'],
46
+ handler: async ({ params, query, body, res }) => {
47
+ const added = await this.addMembers(params.cn, body.member, {
48
+ dryRun: query.dryRun ?? false,
49
+ });
50
+ res.json({ added });
51
+ },
52
+ });
53
+ }
54
+ }
55
+ ```
56
+
57
+ The handler has the same duties as an `asyncHandler` callback: it answers
58
+ through `res`, and a rejection is passed to the error middleware.
59
+
60
+ ## Schemas
61
+
62
+ Each schema is an **exported constant**, possibly imported from another
63
+ module. `npm run generate:openapi` does not run the plugin: it finds the
64
+ `defineRoute` call in the source, imports the module that exports each
65
+ schema, and reads its value — a TypeBox schema is JSON Schema already. A
66
+ schema written inline in the declaration is checked at runtime but is not
67
+ in the document, and the generator warns about it.
68
+
69
+ Keep in mind that the schemas are part of the published API: give the
70
+ properties a `description`, or rely on what TypeBox emits.
71
+
72
+ ## Inputs
73
+
74
+ | Field | Source | Checked as |
75
+ | -------- | ------------ | ---------- |
76
+ | `params` | `req.params` | converted |
77
+ | `query` | `req.query` | converted |
78
+ | `body` | `req.body` | as parsed |
79
+
80
+ A route that needs a route middleware — a multipart parser, say — passes it
81
+ as `middleware`. Express runs it before the handler, and before the `Accept`
82
+ and schema checks: a multipart body is read before the request may be
83
+ refused.
84
+
85
+ Path and query values always arrive as strings, so they are converted before
86
+ being checked: a `Type.Number()` query accepts `?limit=10` and hands the
87
+ handler `10`. The body is whatever the registered parser left in `req.body`;
88
+ a request carrying none is checked as an empty object, so an all-optional
89
+ schema accepts it and a required field is refused by name.
90
+
91
+ A part the route declares no schema for is handed to the handler unchecked,
92
+ as before. The declaration feeds the document the same way: `params` becomes
93
+ the path parameters, `query` the query parameters, `body` the JSON request
94
+ body, and `responses` the answers; the standard 400/401/404/500 entries stay.
95
+
96
+ An answer is a schema, or `{ schema, description }` when the document needs
97
+ a sentence of its own:
98
+
99
+ ```typescript
100
+ responses: {
101
+ 200: { schema: MembersAdded, description: 'Members added' },
102
+ 409: { schema: AlreadyMember, description: 'Already a member' },
103
+ 500: ErrorSchema,
104
+ }
105
+ ```
106
+
107
+ Only the parameters the path actually carries (`:cn` → `{cn}`) become path
108
+ parameters: a `params` key the path does not name is reported by the
109
+ generator and left out, since OpenAPI refuses it.
110
+
111
+ ## Refusals
112
+
113
+ A request that fails a schema is answered with `400` and a body naming every
114
+ failing path:
115
+
116
+ ```json
117
+ {
118
+ "error": "Invalid request: body.member: Expected string; query.limit: Expected number"
119
+ }
120
+ ```
121
+
122
+ A caller whose `Accept` excludes JSON is answered `400 Bad request` before
123
+ the handler runs. A route serving another media type sets `json: false` and
124
+ answers it itself; SCIM does, for `application/scim+json`. Its declared
125
+ schemas are still checked first, and a failure there is answered in the REST
126
+ shape (`Invalid request: …`): such a route validates the rest of its input
127
+ itself. The handler also receives Express's `next`, for a route that falls
128
+ through or has already answered.
129
+
130
+ `Type.Object` accepts unknown keys unless the schema says
131
+ `{ additionalProperties: false }`. A route migrated from hand-written checks
132
+ starts without that option, so it refuses no client it accepted before;
133
+ tighten it one route at a time.
134
+
135
+ ## The registry
136
+
137
+ Every declaration is recorded on the app, schemas included, without its
138
+ handler. `registeredRoutes(app)` returns them, and `unregisteredRoutes(app)`
139
+ lists what the Express stack carries that `defineRoute` did not declare.
140
+
141
+ ## The migration guard
142
+
143
+ `npm run lint` refuses `app.<verb>(` under `src/plugins` and `src/abstract`,
144
+ through `eslint-rules/no-raw-route.mjs`. `eslint-rules/route-allowlist.json`
145
+ names the files a raw route is tolerated in; it is empty, so every route in
146
+ the tree is declared. `test/scripts/routeAllowlist.test.ts` keeps the list
147
+ exact: a listed file must still declare a raw route, and an unlisted file
148
+ must not.
149
+
150
+ `test/security/routeValidation.test.ts` reads the registry and sends each
151
+ declared query and body a value it must refuse, so a new declaration is
152
+ probed without touching the test.
@@ -92,12 +92,12 @@ jobs:
92
92
  runs-on: ubuntu-latest
93
93
 
94
94
  steps:
95
- - uses: actions/checkout@v3
95
+ - uses: actions/checkout@v7
96
96
 
97
97
  - name: Setup Node.js
98
- uses: actions/setup-node@v3
98
+ uses: actions/setup-node@v7
99
99
  with:
100
- node-version: '20'
100
+ node-version: '22'
101
101
 
102
102
  - name: Install dependencies
103
103
  run: npm ci
@@ -149,6 +149,11 @@ of the environment variable. An empty value on the command line, such as an
149
149
  unset variable expanding to nothing, stops the server: leave the option out
150
150
  to keep the default.
151
151
 
152
+ An option or `DM_*` variable that no declared option reads is named in a
153
+ warning at startup. `--strict-options` refuses it instead. Kubernetes injects
154
+ `DM_*` variables for services whose name starts with `dm`; leave the flag off
155
+ where those names are in use.
156
+
152
157
  ## General Options
153
158
 
154
159
  | CLI | Plural | Env | Default | Description |
@@ -160,6 +165,7 @@ to keep the default.
160
165
  | `--logger` | | `DM_LOGGER` | `console` | `console` or `file`, see [Logger](#logger---logger) |
161
166
  | `--log-file` | | `DM_LOG_FILE` | `error.log` | File `--logger file` writes to |
162
167
  | `--debug` | | `DM_DEBUG` | `false` | Name the failure in the 5xx answers of the error middleware and `serverError`; not for production |
168
+ | `--strict-options` | | `DM_STRICT_OPTIONS` | `false` | Refuse an option or `DM_*` variable no declared option reads |
163
169
  | `--api-prefix` | | `DM_API_PREFIX` | `/api` | API URL prefix |
164
170
  | `--mail-domain` | `--mail-domains` | `DM_MAIL_DOMAIN` | `[]` | Mail domains |
165
171
  | `--schemas-path` | | `DM_SCHEMAS_PATH` | `static/schemas` | Path to JSON schemas |
@@ -229,15 +235,15 @@ at `0` otherwise.
229
235
 
230
236
  #### `core/ldap/groups`
231
237
 
232
- | CLI | Plural | Env | Default | Description |
233
- | ---------------------------------- | ----------------- | ------------------------------- | ---------------------------------- | --------------------------- |
234
- | `--ldap-group-base` | | `DM_LDAP_GROUP_BASE` | | Groups base DN |
235
- | `--ldap-groups-main-attribute` | | `DM_LDAP_GROUPS_MAIN_ATTRIBUTE` | `cn` | Group identifier attribute |
236
- | `--group-class` | `--group-classes` | `DM_GROUP_CLASSES` | `top,groupOfNames` | Group objectClasses |
237
- | `--group-allow-unexistent-members` | | `DM_ALLOW_UNEXISTENT_MEMBERS` | `false` | Allow non-existent members |
238
- | `--group-default-attributes` | | `DM_GROUP_DEFAULT_ATTRIBUTES` | `{}` | Default attributes (JSON) |
239
- | `--group-dummy-user` | | `DM_GROUP_DUMMY_USER` | `cn=fakeuser` | Dummy user for empty groups |
240
- | `--group-schema` | | `DM_GROUP_SCHEMA` | `static/schemas/twake/groups.json` | Group JSON schema path |
238
+ | CLI | Plural | Env | Default | Description |
239
+ | ----------------------------------- | ----------------- | ------------------------------- | ---------------------------------- | --------------------------- |
240
+ | `--ldap-group-base` | | `DM_LDAP_GROUP_BASE` | | Groups base DN |
241
+ | `--ldap-groups-main-attribute` | | `DM_LDAP_GROUPS_MAIN_ATTRIBUTE` | `cn` | Group identifier attribute |
242
+ | `--group-class` | `--group-classes` | `DM_GROUP_CLASSES` | `top,groupOfNames` | Group objectClasses |
243
+ | `--groups-allow-unexistent-members` | | `DM_ALLOW_UNEXISTENT_MEMBERS` | `false` | Allow non-existent members |
244
+ | `--group-default-attributes` | | `DM_GROUP_DEFAULT_ATTRIBUTES` | `{}` | Default attributes (JSON) |
245
+ | `--group-dummy-user` | | `DM_GROUP_DUMMY_USER` | `cn=fakeuser` | Dummy user for empty groups |
246
+ | `--group-schema` | | `DM_GROUP_SCHEMA` | `static/schemas/twake/groups.json` | Group JSON schema path |
241
247
 
242
248
  #### `core/ldap/externalUsersInGroups`
243
249
 
@@ -429,6 +435,7 @@ whichever of the two names it selects. A value other than `req.user` or
429
435
  | ---------------------------------- | ----------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
430
436
  | `--authz-per-branch-config` | `DM_AUTHZ_PER_BRANCH_CONFIG` | `{default:{read:true,write:false,delete:false}}` | Authorization config (JSON) |
431
437
  | `--authz-per-branch-cache-ttl` | `DM_AUTHZ_PER_BRANCH_CACHE_TTL` | `60` | Cache TTL (seconds) |
438
+ | `--ldap-group-member-attribute` | `DM_LDAP_GROUP_MEMBER_ATTRIBUTE` | `member` | Attribute a group lists its members in |
432
439
  | `--authz-filter-attached-entries` | `DM_AUTHZ_FILTER_ATTACHED_ENTRIES` | `false` | Judge an attached entry by the organization it hangs off, and filter listings accordingly. Honoured by `authzPerBranch`; always on under `authzLinid1`; does nothing under `authzDynamic`. |
433
440
  | `--authz-transit-branch` | `DM_AUTHZ_TRANSIT_BRANCH` | | Organization entries are handed over through when they are judged by attachment: every administrator sees and may claim what is there, and puts an entry there with write on the organization it leaves. An entry attached to no organization is in transit too. See [authzLinid1](plugins/auth/authz-linid1.md#transit-handing-an-entry-over). |
434
441
  | `--authz-transit-origin-attribute` | `DM_AUTHZ_TRANSIT_ORIGIN_ATTRIBUTE` | | Attribute an entry put in transit keeps the organization it leaves in, removed when it is claimed. Empty records nothing; it must exist in the directory's schema. See [authzLinid1](plugins/auth/authz-linid1.md#where-an-entry-comes-from). |
@@ -523,7 +530,7 @@ Formerly `core/twake/calendarResources`, still accepted as a deprecated alias.
523
530
  | `--calendar-concurrency` | `DM_CALENDAR_CONCURRENCY` | `10` | API concurrency |
524
531
  | `--calendar-resource-base` | `DM_CALENDAR_RESOURCE_BASE` | | Resource base DN |
525
532
  | `--calendar-resource-objectclass` | `DM_CALENDAR_RESOURCE_OBJECTCLASS` | | Resource objectClass |
526
- | `--calendar-resource-creator` | `DM_CALENDAR_RESOURCE_CREATOR` | | Resource creator |
533
+ | `--calendar-resource-creator` | `DM_CALENDAR_RESOURCE_CREATOR` | `admin@example.com` | Resource creator |
527
534
  | `--calendar-resource-domain` | `DM_CALENDAR_RESOURCE_DOMAIN` | | Resource domain |
528
535
  | `--calendar-firstname-attribute` | `DM_CALENDAR_FIRSTNAME_ATTRIBUTE` | `givenName` | Registered user first name |
529
536
  | `--calendar-lastname-attribute` | `DM_CALENDAR_LASTNAME_ATTRIBUTE` | `sn` | Registered user last name |
@@ -643,8 +650,8 @@ Extends `core/ldap/groups`, whose options it reads too.
643
650
 
644
651
  | CLI | Env | Default | Description |
645
652
  | --------------------------------------------- | ---------------------------------------------- | ------------------ | ----------------------------------------------------- |
646
- | `--twake-group-base` | `DM_TWAKE_GROUP_BASE` | | Group branch of an organization, with `{org}` |
647
- | `--twake-group-user-base` | `DM_TWAKE_GROUP_USER_BASE` | | User branch of an organization, with `{org}` |
653
+ | `--twake-group-base` | `DM_TWAKE_GROUP_BASE` | | Group branch of an organization, holding `{org}` once |
654
+ | `--twake-group-user-base` | `DM_TWAKE_GROUP_USER_BASE` | | User branch of an organization, holding `{org}` once |
648
655
  | `--twake-group-organization-dn` | `DM_TWAKE_GROUP_ORGANIZATION_DN` | | Organization entry, with `{org}` (empty: not checked) |
649
656
  | `--twake-group-organization-status-attribute` | `DM_TWAKE_GROUP_ORGANIZATION_STATUS_ATTRIBUTE` | `twakeOrgStatus` | Organization status attribute |
650
657
  | `--twake-group-organization-deleted-value` | `DM_TWAKE_GROUP_ORGANIZATION_DELETED_VALUE` | `deleted` | Status of a deleted organization |
@@ -663,17 +670,17 @@ organization entry, and `core/ldap/onChange`; with `--rabbitmq-url`, it
663
670
  requires `core/rabbitmq` and reads the organization domain from
664
671
  `--twake-instance-organization-domain-attribute`.
665
672
 
666
- | CLI | Env | Default | Description |
667
- | -------------------------------------- | --------------------------------------- | -------------------------------------- | -------------------------------------------------- |
668
- | `--twake-space-base` | `DM_TWAKE_SPACE_BASE` | | Space branch of an organization, with `{org}` |
669
- | `--twake-space-class` | `DM_TWAKE_SPACE_CLASS` | `top,twakeSpace` | Object classes of a space |
670
- | `--twake-space-display-name-attribute` | `DM_TWAKE_SPACE_DISPLAY_NAME_ATTRIBUTE` | `twakeDisplayName` | Space name attribute |
671
- | `--twake-space-admin-attribute` | `DM_TWAKE_SPACE_ADMIN_ATTRIBUTE` | `twakeSpaceAdmin` | DNs holding the admin role |
672
- | `--twake-space-editor-attribute` | `DM_TWAKE_SPACE_EDITOR_ATTRIBUTE` | `twakeSpaceEditor` | DNs holding the editor role |
673
- | `--twake-space-viewer-attribute` | `DM_TWAKE_SPACE_VIEWER_ATTRIBUTE` | `twakeSpaceViewer` | DNs holding the viewer role |
674
- | `--twake-space-user-role-attribute` | `DM_TWAKE_SPACE_USER_ROLE_ATTRIBUTE` | | User attribute holding `<space id>:<role>`, if set |
675
- | `--twake-space-exchange` | `DM_TWAKE_SPACE_EXCHANGE` | `space` | Topic exchange of the space events |
676
- | `--twake-space-sync-queue` | `DM_TWAKE_SPACE_SYNC_QUEUE` | `twake.space.sync.requested.ldap-rest` | Queue of the space sync requests |
673
+ | CLI | Env | Default | Description |
674
+ | -------------------------------------- | --------------------------------------- | -------------------------------------- | ----------------------------------------------------- |
675
+ | `--twake-space-base` | `DM_TWAKE_SPACE_BASE` | | Space branch of an organization, holding `{org}` once |
676
+ | `--twake-space-class` | `DM_TWAKE_SPACE_CLASS` | `top,twakeSpace` | Object classes of a space |
677
+ | `--twake-space-display-name-attribute` | `DM_TWAKE_SPACE_DISPLAY_NAME_ATTRIBUTE` | `twakeDisplayName` | Space name attribute |
678
+ | `--twake-space-admin-attribute` | `DM_TWAKE_SPACE_ADMIN_ATTRIBUTE` | `twakeSpaceAdmin` | DNs holding the admin role |
679
+ | `--twake-space-editor-attribute` | `DM_TWAKE_SPACE_EDITOR_ATTRIBUTE` | `twakeSpaceEditor` | DNs holding the editor role |
680
+ | `--twake-space-viewer-attribute` | `DM_TWAKE_SPACE_VIEWER_ATTRIBUTE` | `twakeSpaceViewer` | DNs holding the viewer role |
681
+ | `--twake-space-user-role-attribute` | `DM_TWAKE_SPACE_USER_ROLE_ATTRIBUTE` | | User attribute holding `<space id>:<role>`, if set |
682
+ | `--twake-space-exchange` | `DM_TWAKE_SPACE_EXCHANGE` | `space` | Topic exchange of the space events |
683
+ | `--twake-space-sync-queue` | `DM_TWAKE_SPACE_SYNC_QUEUE` | `twake.space.sync.requested.ldap-rest` | Queue of the space sync requests |
677
684
 
678
685
  See [spaces](plugins/integrations/spaces.md).
679
686
 
@@ -722,7 +729,7 @@ see [Storage](plugins/utilities/storage.md).
722
729
 
723
730
  #### `core/configApi`
724
731
 
725
- Exposes API configuration at `/api/v1/config` for client applications. Returns available features, schemas, and endpoints. No configuration options.
732
+ Exposes API configuration at `/api/v1/config` for client applications. Returns available features, schemas, endpoints, and the server's `version` — read from the `package.json` that ships with it, so a client can tell an administrator the server is older than it supports. No configuration options.
726
733
 
727
734
  ## Configuration File
728
735
 
@@ -41,7 +41,17 @@ and `/readyz` names the loaded plugins that report: on an ingress exposing
41
41
  the server to the internet, keep the two paths to the cluster.
42
42
 
43
43
  The Helm chart probes both routes over HTTP (`livenessProbe` and
44
- `readinessProbe` in `values.yaml`).
44
+ `readinessProbe` in `values.yaml`). The published image also declares a
45
+ `HEALTHCHECK` calling `GET /healthz` from inside the container, on `DM_PORT`:
46
+ `docker ps` reports the container unhealthy when it stops answering, and a
47
+ `--restart` policy restarts it as it would after a crash, while an
48
+ orchestrator with probes of its own ignores the declaration. A port given
49
+ any other way (`--port` in a configuration file, a plugin) leaves the check
50
+ on 8081.
51
+
52
+ The image runs as its unprivileged `node` user: with `docker run`, a
53
+ `DM_PORT` below 1024 stops the server at startup, where the default 8081
54
+ changes nothing.
45
55
 
46
56
  ### What the shipped plugins report
47
57
 
@@ -110,6 +120,7 @@ the rate limiting and the request id, to keep to the cluster or the host.
110
120
  | `ldap_rest_ldap_pool_wait_seconds` | histogram | | Time to get a pooled connection, opening one included |
111
121
  | `ldap_rest_ldap_pool_connections` | gauge | `state` | Pooled connections, `busy` or `idle` |
112
122
  | `ldap_rest_ldap_pool_waiting` | gauge | | Operations waiting for a connection, the pool being full |
123
+ | `ldap_rest_ldap_pool_opening` | gauge | | Connections being opened, within `--ldap-pool-size` |
113
124
  | `ldap_rest_ldap_pool_max` | gauge | | `--ldap-pool-size` |
114
125
  | `ldap_rest_ldap_search_cache_total` | counter | `result` | Cacheable searches, `hit` or `miss`; nothing when the cache is off |
115
126
  | `ldap_rest_hook_failures_total` | counter | `hook`, `plugin` | Notification hooks (`*done`, `onLdap*`) that threw, which the server logs and goes on |
@@ -117,6 +128,10 @@ the rate limiting and the request id, to keep to the cluster or the host.
117
128
  | `ldap_rest_unhandled_rejections_total` | counter | | Promise rejections nothing handled; counted by the binary, a host skipping `exitOnCrash()` leaves it at zero |
118
129
  | `process_*`, `nodejs_*` | | | The process: CPU, memory, event loop lag, handles, GC |
119
130
 
131
+ A burst of requests that has to open connections no longer opens one per
132
+ request: a connection being opened takes its place in the pool, and the
133
+ requests over `--ldap-pool-size` wait for a free one.
134
+
120
135
  A search the caller pages is timed page by page, what the caller does
121
136
  between two pages left out; the readiness ping is not timed. The route
122
137
  label is the template a route was declared with, never the URL: a label
@@ -128,9 +143,9 @@ James's does, is a call like another. A plugin calling a service through
128
143
  `this.fetch` has its calls counted under its name; the OpenID Connect
129
144
  client's calls to its provider are made by its library, and not timed.
130
145
 
131
- The instruments come from `prom-client` 15, kept while the server supports
132
- Node 20, which its successor `@prometheus-io/client` dropped: the
133
- deprecation notice `npm install` prints is expected.
146
+ The instruments come from `prom-client` 15, which `npm install` reports as
147
+ deprecated: its successor `@prometheus-io/client` is the migration to make,
148
+ and the notice is expected until it is done.
134
149
 
135
150
  The port listens on every interface. What to alert on: `/readyz` for a
136
151
  replica out of rotation, `result="error"` in
@@ -137,8 +137,8 @@ for it.
137
137
  describes department groups and requires `twakeDepartmentLink`, which these
138
138
  groups do not have. Groups created through SCIM are written directly and
139
139
  are not checked against this schema.
140
- - `--twake-group-base`, `--twake-group-user-base`: required, each with
141
- `{org}`.
140
+ - `--twake-group-base`, `--twake-group-user-base`: required, each holding
141
+ `{org}` once.
142
142
  - `--twake-group-organization-dn`: the organization entry. Empty means no
143
143
  organization is checked.
144
144
  - `--twake-group-organization-status-attribute` (default `twakeOrgStatus`),
@@ -309,6 +309,16 @@ mapping contains an entry with `scim: "department"`; otherwise the server return
309
309
  `400 invalidFilter`. Same for PATCH: operations on unmapped paths produce
310
310
  `400 invalidPath`.
311
311
 
312
+ A User or Group body is checked against RFC 7643 before anything is written
313
+ and before any hook reads it: `userName`, `displayName` and `id` are
314
+ strings; a simple attribute takes a string, a number or a boolean, or a list
315
+ of them; a complex attribute takes an object whose sub-attributes are scalars
316
+ (or, for a reference such as `manager`, an object whose `value` is one); a
317
+ multi-valued complex attribute such as `emails` takes a list of objects whose
318
+ `value` is a scalar. Anything else is answered
319
+ `400 invalidValue`, as is a PATCH operation that is not an object or whose
320
+ `op` is not a string.
321
+
312
322
  A PATCH `add`, or a `remove` naming values, changes only the values it names:
313
323
  values the caller's read did not show, such as group members an
314
324
  `ldapsearchfilter` hides, stay. Two PATCHes changing different values of
@@ -369,7 +379,8 @@ via `escapeLdapFilter()` — no LDAP injection is possible.
369
379
  | `a eq "x" and b eq "y"` | `(&(a=x)(b=y))` |
370
380
  | `not (a eq "x")` | `(!(a=x))` |
371
381
 
372
- Unknown SCIM attributes raise `400 invalidFilter`.
382
+ Unknown SCIM attributes raise `400 invalidFilter`, as does a filter nested
383
+ more than 32 levels deep (a parenthesis and a `not (` each count one).
373
384
 
374
385
  ## Pagination
375
386
 
@@ -249,7 +249,7 @@ A space reads, strongest role first:
249
249
  The plugin loads [groups](groups.md), and reads its options for the
250
250
  organization's users, groups and organization entry.
251
251
 
252
- - `--twake-space-base`: required, with `{org}`.
252
+ - `--twake-space-base`: required, holding `{org}` once.
253
253
  - `--twake-space-class` (default `top,twakeSpace`): the classes of a space
254
254
  created.
255
255
  - `--twake-space-display-name-attribute` (default `twakeDisplayName`).
@@ -119,7 +119,10 @@ whenever the link changes. Mark the path `generated` so a client cannot set it.
119
119
 
120
120
  `5GB`, `500 MB`, `2KB` and `2048` all become a number of bytes, with decimal
121
121
  multipliers (1 GB = 10⁹ bytes) as mail servers count them. The same conversion
122
- applies to creations and to updates.
122
+ applies to creations and to updates. A size above 2⁵³ − 1 bytes is refused
123
+ (400), since its number of bytes would be rounded. A schema `number` or
124
+ `integer` refuses a whole number beyond 2⁵³ on its own, whatever the type;
125
+ see [what a value must look like](../../../client-development/schemas/README.md#what-a-value-must-look-like).
123
126
 
124
127
  ### Server-owned defaults
125
128
 
@@ -144,6 +147,12 @@ An `accountExpiry` attribute must not carry a date already past. The comparison
144
147
  is against the start of the current day, so scheduling something for today is
145
148
  allowed.
146
149
 
150
+ The date is an LDAP generalized time, read as written: the year keeps its
151
+ four digits (`0029123123` is a date of year 29, not 1929), and a leap second
152
+ (`20991231235960Z`) is the instant that follows the day. The zone is `Z` or a
153
+ difference of hours and minutes from UTC (`+00`, `+02`, `-0530`, `+14:00`); a
154
+ value past 23 hours or 59 minutes is answered 400 as invalid.
155
+
147
156
  ### Deletion and references
148
157
 
149
158
  ```json
@@ -451,6 +451,10 @@ Content-Type: application/json
451
451
  }
452
452
  ```
453
453
 
454
+ The identifier may also be the whole DN of the entry, in the entity's
455
+ branch: `"uid": "uid=Smith\\,John,ou=users,dc=example,dc=com"` creates the
456
+ entry `Smith,John`, whose DN spells the name with its escapes.
457
+
454
458
  ### Modify Entry
455
459
 
456
460
  ```bash
@@ -181,6 +181,22 @@ Site-specific attributes are added with `--ldap-raw-hidden-attribute`, which sta
181
181
  --ldap-raw-hidden-attribute twakeApiKey --ldap-raw-hidden-attribute employeeNumber
182
182
  ```
183
183
 
184
+ A hidden attribute is resolved to its directory type, so the type's OID and
185
+ every alias it declares are hidden too — asking for `2.5.4.35` or
186
+ `homePhone` does not serve `userPassword` or a configured
187
+ `homeTelephoneNumber`. A search filter that tests a hidden type, at any
188
+ depth and under any of its names, is refused with a 400: the search runs with
189
+ the service account's rights, so leaving it through would answer the same
190
+ question one match at a time. An assertion on a supertype of a hidden type is
191
+ refused as well, since the directory evaluates an attribute's subtypes. An
192
+ extensible match without an attribute (`(:octetStringMatch:=…)`) runs over
193
+ every attribute and is refused too while attributes are hidden.
194
+
195
+ When the directory schema cannot be read, an entry read or a search is
196
+ refused with a 503 rather than answered with only the configured names
197
+ compared, which could serve an alias or an OID. The root DSE still answers:
198
+ it carries no entry values and is how a client discovers the schema.
199
+
184
200
  Note that hiding an attribute here keeps it out of the API, not out of the directory: an account able to bind to LDAP directly still reads it. This is a guard against casual exposure through the browsing UI, not an access control — use [authz-per-branch](../auth/authz-per-branch.md) or directory ACLs for that.
185
201
 
186
202
  ## Security
@@ -189,7 +205,7 @@ Access is restricted in three independent layers:
189
205
 
190
206
  1. **Exposed bases** — any DN outside `--ldap-raw-base` is refused with a 403, whatever the caller's rights.
191
207
  2. **Authorization hooks** — every entry read, children listing and search goes through `server.ldap`, so the authorization plugins ([authz-per-branch](../auth/authz-per-branch.md), authz-dynamic, …) apply exactly as on the high-level APIs.
192
- 3. **Hidden attributes** — credential attributes, plus anything in `--ldap-raw-hidden-attribute`, are removed from every response, including searches. See [Hidden attributes](#hidden-attributes).
208
+ 3. **Hidden attributes** — credential attributes, plus anything in `--ldap-raw-hidden-attribute`, are removed from every response, including searches, under every name their type answers to; a filter that tests one is refused with a 400. See [Hidden attributes](#hidden-attributes).
193
209
 
194
210
  Two exceptions, both deliberate: the **root DSE** and the **schema** are read with the service account, not with the caller's rights. They are directory metadata and carry no user data; reading them with a restricted account would leave the UI unable to name and type attributes. Do not expose this plugin publicly if your directory publishes sensitive information in its root DSE.
195
211