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
@@ -16,7 +16,23 @@ type Identity = {
16
16
  };
17
17
  /** Signatures an identity carries over, see {@link Identity} */
18
18
  export type Signatures = Pick<Identity, 'htmlSignature' | 'textSignature'>;
19
+ export declare const QuotaParams: import("@sinclair/typebox").TObject<{
20
+ user: import("@sinclair/typebox").TString;
21
+ }>;
19
22
  export default class James extends TwakePlugin {
23
+ static optionDefinitions: {
24
+ james_webadmin_url: import("../../config/options").StringOption;
25
+ james_webadmin_token: import("../../config/options").StringOption;
26
+ james_signature_template: import("../../config/options").StringOption;
27
+ james_concurrency: import("../../config/options").NumberOption;
28
+ james_init_delay: import("../../config/options").NumberOption;
29
+ james_retry_attempts: import("../../config/options").NumberOption;
30
+ james_retry_delay: import("../../config/options").NumberOption;
31
+ james_mailing_list_branch: import("../../config/options").ArrayOption;
32
+ james_mailbox_type_attribute: import("../../config/options").StringOption;
33
+ james_sync_forwards: import("../../config/options").BooleanOption;
34
+ james_team_mailbox_any_domain: import("../../config/options").BooleanOption;
35
+ };
20
36
  name: string;
21
37
  roles: Role[];
22
38
  dependencies: {
@@ -30,6 +46,7 @@ export default class James extends TwakePlugin {
30
46
  private forwardAttr;
31
47
  private initDelay;
32
48
  constructor(server: DM);
49
+ private get settings();
33
50
  /**
34
51
  * Register API routes
35
52
  */
@@ -0,0 +1,21 @@
1
+ /**
2
+ * James plugin options, see `docs/plugin-development/options.md`.
3
+ */
4
+ import type { Config } from '../../config/args';
5
+ import { type OptionsOf } from '../../config/options';
6
+ export declare const jamesOptions: {
7
+ james_webadmin_url: import("../../config/options").StringOption;
8
+ james_webadmin_token: import("../../config/options").StringOption;
9
+ james_signature_template: import("../../config/options").StringOption;
10
+ james_concurrency: import("../../config/options").NumberOption;
11
+ james_init_delay: import("../../config/options").NumberOption;
12
+ james_retry_attempts: import("../../config/options").NumberOption;
13
+ james_retry_delay: import("../../config/options").NumberOption;
14
+ james_mailing_list_branch: import("../../config/options").ArrayOption;
15
+ james_mailbox_type_attribute: import("../../config/options").StringOption;
16
+ james_sync_forwards: import("../../config/options").BooleanOption;
17
+ james_team_mailbox_any_domain: import("../../config/options").BooleanOption;
18
+ };
19
+ export type JamesOptions = OptionsOf<typeof jamesOptions>;
20
+ /** The checked values for a caller that holds a Config instead of the plugin */
21
+ export declare function jamesOptionsOf(config: Config): JamesOptions;
@@ -23,6 +23,18 @@ interface Rule {
23
23
  }
24
24
  export declare function parseRules(source: string): Rule[];
25
25
  export default class TwakeLifecycleEvents extends DmPlugin {
26
+ static optionDefinitions: {
27
+ twake_lifecycle_role_attribute: import("../../config/options").StringOption;
28
+ twake_lifecycle_lock_attribute: import("../../config/options").StringOption;
29
+ twake_lifecycle_lock_value: import("../../config/options").StringOption;
30
+ twake_lifecycle_deleted_attribute: import("../../config/options").StringOption;
31
+ twake_lifecycle_deleted_value: import("../../config/options").StringOption;
32
+ twake_lifecycle_deleted_at_attribute: import("../../config/options").StringOption;
33
+ twake_lifecycle_deleted_at_format: import("../../config/options").StringOption;
34
+ twake_lifecycle_reason_attribute: import("../../config/options").StringOption;
35
+ twake_lifecycle_rules: import("../../config/options").StringOption;
36
+ twake_lifecycle_member_attribute: import("../../config/options").StringOption;
37
+ };
26
38
  name: string;
27
39
  roles: Role[];
28
40
  dependencies: {
@@ -1,14 +1,3 @@
1
- /**
2
- * @module plugins/twake/migrationProxy
3
- *
4
- * Keeps the migrated-users list of the Twake Mail migration proxy in step
5
- * with the accounts of the directory: an account created during the
6
- * migration has no mailbox on the old system, so the proxy has to route it
7
- * to the new one from its first login, and the addresses of a migrated
8
- * account have to follow it.
9
- *
10
- * See `docs/usage/plugins/integrations/mail-migration.md`.
11
- */
12
1
  import type { Express } from 'express';
13
2
  import TwakePlugin from '../../abstract/twakePlugin';
14
3
  import { type Role } from '../../abstract/plugin';
@@ -27,7 +16,18 @@ import type { HealthStatus } from '../../lib/health';
27
16
  * type: boolean
28
17
  * description: Whether the proxy routes the user to the new backend
29
18
  */
19
+ export declare const MailMigrationParams: import("@sinclair/typebox").TObject<{
20
+ user: import("@sinclair/typebox").TString;
21
+ }>;
30
22
  export default class MigrationProxy extends TwakePlugin {
23
+ static optionDefinitions: {
24
+ migration_proxy_url: import("../../config/options").StringOption;
25
+ migration_proxy_token: import("../../config/options").StringOption;
26
+ migration_proxy_branch: import("../../config/options").ArrayOption;
27
+ migration_proxy_concurrency: import("../../config/options").NumberOption;
28
+ migration_proxy_retry_attempts: import("../../config/options").NumberOption;
29
+ migration_proxy_retry_delay: import("../../config/options").NumberOption;
30
+ };
31
31
  name: string;
32
32
  roles: Role[];
33
33
  dependencies: {
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Twake Mail migration proxy options, see `docs/plugin-development/options.md`.
3
+ */
4
+ import type { Config } from '../../config/args';
5
+ import { type OptionsOf } from '../../config/options';
6
+ export declare const migrationProxyOptions: {
7
+ migration_proxy_url: import("../../config/options").StringOption;
8
+ migration_proxy_token: import("../../config/options").StringOption;
9
+ migration_proxy_branch: import("../../config/options").ArrayOption;
10
+ migration_proxy_concurrency: import("../../config/options").NumberOption;
11
+ migration_proxy_retry_attempts: import("../../config/options").NumberOption;
12
+ migration_proxy_retry_delay: import("../../config/options").NumberOption;
13
+ };
14
+ export type MigrationProxyOptions = OptionsOf<typeof migrationProxyOptions>;
15
+ /** The checked values for a caller that holds a Config instead of the plugin */
16
+ export declare function migrationProxyOptionsOf(config: Config): MigrationProxyOptions;
@@ -18,7 +18,35 @@ export type Space = {
18
18
  role: SpaceRole;
19
19
  }[];
20
20
  };
21
+ export declare const SpaceOrganizationParams: import("@sinclair/typebox").TObject<{
22
+ id: import("@sinclair/typebox").TString;
23
+ }>;
24
+ export declare const SpaceParams: import("@sinclair/typebox").TObject<{
25
+ id: import("@sinclair/typebox").TString;
26
+ spaceId: import("@sinclair/typebox").TString;
27
+ }>;
28
+ export declare const SpaceMemberParams: import("@sinclair/typebox").TObject<{
29
+ id: import("@sinclair/typebox").TString;
30
+ spaceId: import("@sinclair/typebox").TString;
31
+ userId: import("@sinclair/typebox").TString;
32
+ }>;
33
+ export declare const SpaceGroupParams: import("@sinclair/typebox").TObject<{
34
+ id: import("@sinclair/typebox").TString;
35
+ spaceId: import("@sinclair/typebox").TString;
36
+ groupId: import("@sinclair/typebox").TString;
37
+ }>;
21
38
  export default class TwakeSpaces extends DmPlugin {
39
+ static optionDefinitions: {
40
+ twake_space_base: import("../../config/options").StringOption;
41
+ twake_space_class: import("../../config/options").ArrayOption;
42
+ twake_space_display_name_attribute: import("../../config/options").StringOption;
43
+ twake_space_admin_attribute: import("../../config/options").StringOption;
44
+ twake_space_editor_attribute: import("../../config/options").StringOption;
45
+ twake_space_viewer_attribute: import("../../config/options").StringOption;
46
+ twake_space_user_role_attribute: import("../../config/options").StringOption;
47
+ twake_space_exchange: import("../../config/options").StringOption;
48
+ twake_space_sync_queue: import("../../config/options").StringOption;
49
+ };
22
50
  name: string;
23
51
  roles: Role[];
24
52
  dependencies: Record<string, string>;
@@ -32,6 +60,8 @@ export default class TwakeSpaces extends DmPlugin {
32
60
  private readonly exchange;
33
61
  private readonly domainAttribute;
34
62
  private readonly orgPattern?;
63
+ private readonly groupSettings;
64
+ private readonly instanceSettings;
35
65
  private readonly domains;
36
66
  /**
37
67
  * Changes are followed one at a time, each from the directory as it is
@@ -1,22 +1,24 @@
1
- /**
2
- * @module plugins/twake/tombstone
3
- *
4
- * Turns the delete of an account into a tombstone: the entry stays, flagged
5
- * as deleted, dated, locked, with the reason and without the attributes the
6
- * deployment wants gone. The address it holds cannot be taken again until the
7
- * tombstone is erased.
8
- *
9
- * SCIM treats a tombstone as gone. Erasing removes the entry and its group
10
- * memberships, once the deletion is old enough or when forced, and announces
11
- * nothing: the deletion already did.
12
- *
13
- * See `docs/usage/plugins/integrations/tombstone.md`.
14
- */
15
1
  import type { Express, Request } from 'express';
16
2
  import DmPlugin, { type Role } from '../../abstract/plugin';
17
3
  import type { DM } from '../../bin';
18
4
  import type { Hooks } from '../../hooks';
5
+ export declare const EraseTombstoneBody: import("@sinclair/typebox").TObject<{
6
+ dn: import("@sinclair/typebox").TString;
7
+ force: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
8
+ }>;
9
+ export declare const EraseTombstoneResponse: import("@sinclair/typebox").TObject<{
10
+ success: import("@sinclair/typebox").TBoolean;
11
+ }>;
19
12
  export default class TwakeTombstone extends DmPlugin {
13
+ static optionDefinitions: {
14
+ twake_tombstone_dn: import("../../config/options").ArrayOption;
15
+ twake_tombstone_default_reason: import("../../config/options").StringOption;
16
+ twake_tombstone_reason_header: import("../../config/options").StringOption;
17
+ twake_tombstone_reasons: import("../../config/options").ArrayOption;
18
+ twake_tombstone_clear_attributes: import("../../config/options").ArrayOption;
19
+ twake_tombstone_erase_min_age: import("../../config/options").NumberOption;
20
+ twake_tombstone_group_bases: import("../../config/options").ArrayOption;
21
+ };
20
22
  name: string;
21
23
  roles: Role[];
22
24
  private readonly attrs;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Twake groups options, see `docs/plugin-development/options.md`.
3
+ */
4
+ import type { Config } from '../../config/args';
5
+ import { type OptionsOf } from '../../config/options';
6
+ export declare const twakeGroupsOptions: {
7
+ twake_group_base: import("../../config/options").StringOption;
8
+ twake_group_user_base: import("../../config/options").StringOption;
9
+ twake_group_organization_dn: import("../../config/options").StringOption;
10
+ twake_group_organization_status_attribute: import("../../config/options").StringOption;
11
+ twake_group_organization_deleted_value: import("../../config/options").StringOption;
12
+ twake_group_display_name_attribute: import("../../config/options").StringOption;
13
+ twake_group_color_attribute: import("../../config/options").StringOption;
14
+ twake_group_created_at_attribute: import("../../config/options").StringOption;
15
+ twake_group_max_page_limit: import("../../config/options").NumberOption;
16
+ twake_group_member_fields: import("../../config/options").JsonOption<{}>;
17
+ };
18
+ export type TwakeGroupsOptions = OptionsOf<typeof twakeGroupsOptions>;
19
+ /** The checked values for a caller that holds a Config instead of the plugin */
20
+ export declare function twakeGroupsOptionsOf(config: Config): TwakeGroupsOptions;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Twake instances options, see `docs/plugin-development/options.md`.
3
+ */
4
+ import type { Config } from '../../config/args';
5
+ import { type OptionsOf } from '../../config/options';
6
+ export declare const twakeInstancesOptions: {
7
+ twake_instance_dn: import("../../config/options").ArrayOption;
8
+ twake_instance_skip_attribute: import("../../config/options").StringOption;
9
+ twake_instance_skip_value: import("../../config/options").StringOption;
10
+ twake_instance_provider: import("../../config/options").StringOption;
11
+ twake_instance_id: import("../../config/options").StringOption;
12
+ twake_instance_fqdn_attribute: import("../../config/options").StringOption;
13
+ twake_instance_sent_attribute: import("../../config/options").StringOption;
14
+ twake_instance_locale: import("../../config/options").StringOption;
15
+ twake_instance_timeout: import("../../config/options").NumberOption;
16
+ twake_instance_cloudery_url: import("../../config/options").StringOption;
17
+ twake_instance_cloudery_token: import("../../config/options").StringOption;
18
+ twake_instance_cloudery_offer: import("../../config/options").StringOption;
19
+ twake_instance_cloudery_domain: import("../../config/options").StringOption;
20
+ twake_instance_cloudery_organization_offer: import("../../config/options").StringOption;
21
+ twake_instance_cozy_url: import("../../config/options").StringOption;
22
+ twake_instance_cozy_user: import("../../config/options").StringOption;
23
+ twake_instance_cozy_passphrase: import("../../config/options").StringOption;
24
+ twake_instance_cozy_domain: import("../../config/options").StringOption;
25
+ twake_instance_cozy_context: import("../../config/options").StringOption;
26
+ twake_instance_cozy_apps: import("../../config/options").StringOption;
27
+ twake_instance_cozy_org_id: import("../../config/options").StringOption;
28
+ twake_instance_cozy_org_domain: import("../../config/options").StringOption;
29
+ twake_instance_auth_exchange: import("../../config/options").StringOption;
30
+ twake_instance_b2b_exchange: import("../../config/options").StringOption;
31
+ twake_instance_user_created_key: import("../../config/options").StringOption;
32
+ twake_instance_organization_created_key: import("../../config/options").StringOption;
33
+ twake_instance_workplace_created_key: import("../../config/options").StringOption;
34
+ twake_instance_queue: import("../../config/options").StringOption;
35
+ twake_instance_organization_base: import("../../config/options").StringOption;
36
+ twake_instance_organization_id_attribute: import("../../config/options").StringOption;
37
+ twake_instance_organization_domain_attribute: import("../../config/options").StringOption;
38
+ twake_instance_organization_name_attribute: import("../../config/options").StringOption;
39
+ twake_instance_organization_fqdn_attribute: import("../../config/options").StringOption;
40
+ };
41
+ export type TwakeInstancesOptions = OptionsOf<typeof twakeInstancesOptions>;
42
+ /** The checked values for a caller that holds a Config instead of the plugin */
43
+ export declare function twakeInstancesOptionsOf(config: Config): TwakeInstancesOptions;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Account lifecycle options, shared by the Twake lifecycle plugins. See
3
+ * `docs/plugin-development/options.md`.
4
+ */
5
+ import type { Config } from '../../config/args';
6
+ import { type OptionsOf } from '../../config/options';
7
+ export declare const twakeLifecycleOptions: {
8
+ twake_lifecycle_role_attribute: import("../../config/options").StringOption;
9
+ twake_lifecycle_lock_attribute: import("../../config/options").StringOption;
10
+ twake_lifecycle_lock_value: import("../../config/options").StringOption;
11
+ twake_lifecycle_deleted_attribute: import("../../config/options").StringOption;
12
+ twake_lifecycle_deleted_value: import("../../config/options").StringOption;
13
+ twake_lifecycle_deleted_at_attribute: import("../../config/options").StringOption;
14
+ twake_lifecycle_deleted_at_format: import("../../config/options").StringOption;
15
+ twake_lifecycle_reason_attribute: import("../../config/options").StringOption;
16
+ twake_lifecycle_rules: import("../../config/options").StringOption;
17
+ twake_lifecycle_member_attribute: import("../../config/options").StringOption;
18
+ };
19
+ export type TwakeLifecycleOptions = OptionsOf<typeof twakeLifecycleOptions>;
20
+ /** The checked values for a caller that holds a Config instead of the plugin */
21
+ export declare function twakeLifecycleOptionsOf(config: Config): TwakeLifecycleOptions;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Twake spaces options, see `docs/plugin-development/options.md`.
3
+ */
4
+ import type { Config } from '../../config/args';
5
+ import { type OptionsOf } from '../../config/options';
6
+ export declare const twakeSpacesOptions: {
7
+ twake_space_base: import("../../config/options").StringOption;
8
+ twake_space_class: import("../../config/options").ArrayOption;
9
+ twake_space_display_name_attribute: import("../../config/options").StringOption;
10
+ twake_space_admin_attribute: import("../../config/options").StringOption;
11
+ twake_space_editor_attribute: import("../../config/options").StringOption;
12
+ twake_space_viewer_attribute: import("../../config/options").StringOption;
13
+ twake_space_user_role_attribute: import("../../config/options").StringOption;
14
+ twake_space_exchange: import("../../config/options").StringOption;
15
+ twake_space_sync_queue: import("../../config/options").StringOption;
16
+ };
17
+ export type TwakeSpacesOptions = OptionsOf<typeof twakeSpacesOptions>;
18
+ /** The checked values for a caller that holds a Config instead of the plugin */
19
+ export declare function twakeSpacesOptionsOf(config: Config): TwakeSpacesOptions;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Twake tombstone options, see `docs/plugin-development/options.md`.
3
+ */
4
+ import type { Config } from '../../config/args';
5
+ import { type OptionsOf } from '../../config/options';
6
+ export declare const twakeTombstoneOptions: {
7
+ twake_tombstone_dn: import("../../config/options").ArrayOption;
8
+ twake_tombstone_default_reason: import("../../config/options").StringOption;
9
+ twake_tombstone_reason_header: import("../../config/options").StringOption;
10
+ twake_tombstone_reasons: import("../../config/options").ArrayOption;
11
+ twake_tombstone_clear_attributes: import("../../config/options").ArrayOption;
12
+ twake_tombstone_erase_min_age: import("../../config/options").NumberOption;
13
+ twake_tombstone_group_bases: import("../../config/options").ArrayOption;
14
+ };
15
+ export type TwakeTombstoneOptions = OptionsOf<typeof twakeTombstoneOptions>;
16
+ /** The checked values for a caller that holds a Config instead of the plugin */
17
+ export declare function twakeTombstoneOptionsOf(config: Config): TwakeTombstoneOptions;
package/docs/README.md CHANGED
@@ -20,6 +20,7 @@ Create and extend LDAP-Rest with custom plugins.
20
20
 
21
21
  - **[Development Guide](plugin-development/README.md)** - Architecture and plugin creation
22
22
  - **[Hooks](plugin-development/hooks.md)** - LDAP hooks system
23
+ - **[Routes](plugin-development/routes.md)** - Declaring routes and their input schemas
23
24
  - **[Testing](plugin-development/testing.md)** - Testing with embedded LDAP
24
25
  - **[Dependencies](plugin-development/dependencies.md)** - Plugin dependencies matrix
25
26
 
@@ -156,6 +156,7 @@ curl -H "Accept: application/json" \
156
156
  {
157
157
  "apiPrefix": "/api",
158
158
  "ldapBase": "dc=example,dc=com",
159
+ "version": "1.2.1",
159
160
  "features": {
160
161
  "flatResources": [
161
162
  {
@@ -242,6 +243,10 @@ curl -H "Accept: application/json" \
242
243
 
243
244
  - `apiPrefix`: API URL prefix (default: `/api`)
244
245
  - `ldapBase`: LDAP base DN
246
+ - `version`: the server's version, as its `package.json` declares it. A client
247
+ compares it with the range it supports and tells an administrator when the
248
+ server is older. Optional: an installation that moved that file leaves it
249
+ out, and a client must then work without it
245
250
  - `features.ldapFlatGeneric.flatResources`: Array of flat LDAP resources (users, positions, etc.)
246
251
  - `features.ldapGroups`: Group management configuration (if enabled)
247
252
  - `features.ldapOrganizations`: Organization tree configuration (if enabled)
@@ -1078,7 +1083,7 @@ curl -X POST \
1078
1083
  ```
1079
1084
 
1080
1085
  A member that does not exist answers 400 naming it, and none of the members
1081
- is added (unless `--group-allow-unexistent-members` is set). Group creation
1086
+ is added (unless `--groups-allow-unexistent-members` is set). Group creation
1082
1087
  checks its `member` list the same way:
1083
1088
 
1084
1089
  ```json
@@ -1335,8 +1340,9 @@ no `limit` was given —
1335
1340
 
1336
1341
  #### 500 Internal Server Error
1337
1342
 
1338
- The failure is in the server's logs, not in the response; the message names
1339
- it only when the server runs with `--debug`:
1343
+ Every 5xx answers this body, whether the route threw or reported the failure
1344
+ through the server's own helper. The failure is in the server's logs, not in
1345
+ the response; the message names it only when the server runs with `--debug`:
1340
1346
 
1341
1347
  ```json
1342
1348
  {
@@ -66,7 +66,7 @@ A complete schema consists of two main sections: entity metadata and attribute d
66
66
  },
67
67
  "mail": {
68
68
  "type": "string",
69
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
69
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$",
70
70
  "required": false,
71
71
  "role": "primaryEmail"
72
72
  },
@@ -74,7 +74,7 @@ A complete schema consists of two main sections: entity metadata and attribute d
74
74
  "type": "array",
75
75
  "items": {
76
76
  "type": "string",
77
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
77
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
78
78
  },
79
79
  "required": false,
80
80
  "role": "emailAliases"
@@ -197,7 +197,7 @@ Simple text values.
197
197
  },
198
198
  "mail": {
199
199
  "type": "string",
200
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
200
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$",
201
201
  "required": false,
202
202
  "role": "primaryEmail"
203
203
  },
@@ -237,7 +237,7 @@ Multi-valued attributes containing multiple items of the same type.
237
237
  "type": "array",
238
238
  "items": {
239
239
  "type": "string",
240
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
240
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
241
241
  },
242
242
  "required": false,
243
243
  "role": "emailAliases"
@@ -338,19 +338,20 @@ Complete reference for all attribute properties.
338
338
 
339
339
  ### Attribute Properties Table
340
340
 
341
- | Property | Type | Applies To | Description | Example |
342
- | ---------- | --------------- | ------------------- | ------------------------------------------------------------------------------------- | ----------------------------------- |
343
- | `type` | string | All | `"string"`, `"array"`, `"pointer"`, `"number"`, `"integer"`, `"boolean"`, `"date"` | `"string"` |
344
- | `required` | boolean | All | Whether the attribute must be present | `true` |
345
- | `fixed` | boolean | All | Whether the attribute value cannot be changed after creation | `true` |
346
- | `role` | string/string[] | String | Semantic role, or roles, for special handling (see [Semantic Roles](#semantic-roles)) | `"identifier"` |
347
- | `test` | string/regex | String, Array items | Regular expression for validation | `"^[a-zA-Z0-9._-]{1,255}$"` |
348
- | `branch` | string[] | Pointer | LDAP branches where referenced entries exist | `["ou=users,dc=example,dc=com"]` |
349
- | `ui` | object | Pointer | UI configuration for pointer selection (future feature) | `{}` |
350
- | `hint` | text | String, Array items | What a valid value looks like, shown under the field and quoted in refusals | `"Expected pattern 999 9999"` |
351
- | `group` | text | All | Form section the attribute is shown in | `"Mailbox Settings"` |
352
- | `default` | any | All | Default value for new entries | `["top", "person"]` |
353
- | `items` | object | Array | Schema for array element validation | `{"type": "string", "test": "..."}` |
341
+ | Property | Type | Applies To | Description | Example |
342
+ | ----------- | --------------- | ------------------- | ------------------------------------------------------------------------------------- | ----------------------------------- |
343
+ | `type` | string | All | `"string"`, `"array"`, `"pointer"`, `"number"`, `"integer"`, `"boolean"`, `"date"` | `"string"` |
344
+ | `required` | boolean | All | Whether the attribute must be present | `true` |
345
+ | `fixed` | boolean | All | Whether the attribute value cannot be changed after creation | `true` |
346
+ | `role` | string/string[] | String | Semantic role, or roles, for special handling (see [Semantic Roles](#semantic-roles)) | `"identifier"` |
347
+ | `test` | string/regex | String, Array items | Regular expression for validation | `"^[a-zA-Z0-9._-]{1,255}$"` |
348
+ | `maxLength` | number | String, Array items | Longest value accepted, in characters; a longer one is refused before `test` (4096) | `255` |
349
+ | `branch` | string[] | Pointer | LDAP branches where referenced entries exist | `["ou=users,dc=example,dc=com"]` |
350
+ | `ui` | object | Pointer | UI configuration for pointer selection (future feature) | `{}` |
351
+ | `hint` | text | String, Array items | What a valid value looks like, shown under the field and quoted in refusals | `"Expected pattern 999 9999"` |
352
+ | `group` | text | All | Form section the attribute is shown in | `"Mailbox Settings"` |
353
+ | `default` | any | All | Default value for new entries | `["top", "person"]` |
354
+ | `items` | object | Array | Schema for array element validation | `{"type": "string", "test": "..."}` |
354
355
 
355
356
  A _text_ is a plain string or a map keyed by language tag,
356
357
  `{ "en": "Mailbox Settings", "fr": "Paramètres de messagerie" }`, picked as
@@ -378,16 +379,40 @@ answers, one value where the attribute holds one, can be sent back as is;
378
379
  two values or more for a single-valued attribute are refused. Each element
379
380
  of an `array` follows `items.type`, text without `items`. A `number` or
380
381
  `integer` is a JSON number or its text (`42`, `"42"`), a `boolean` a JSON
381
- boolean or the directory's `TRUE` / `FALSE`; anything else for them, and
382
+ boolean or the directory's `TRUE` / `FALSE`. A JSON number is judged on the
383
+ text written for it, what the directory is sent: `1e21` and `1e-7` are
384
+ written `1e+21` and `1e-7`, so they are refused like that text, and a whole
385
+ number beyond 2⁵³ is refused whatever the type, as the JSON parser has
386
+ already lost its digits. Anything else for them, and
382
387
  anything but text for a `string`, `date` or `pointer`, is refused; an
383
388
  attribute with `normalize` takes any text, read after validation. In a
384
389
  `replace`, `null` or `[]` clears an attribute; in an `add` or on a creation
385
- they are refused as no value. A `delete` naming the attribute, or naming it
386
- with no value, removes it, unless it is `required`; naming values removes
387
- those, every value it holds included, as the server does not read what
388
- remains, and `''` names no value. An empty string sent as a value is
389
- checked against `test`, refused for a pointer, and missing for a `required`
390
- attribute. Every refusal answers 400 with the attribute's name.
390
+ they are refused as no value when the schema declares the attribute. A
391
+ `null` under a name the schema does not declare — and, for an entity without
392
+ a schema, under any name — is dropped from a creation and from an `add`, as
393
+ if the key were left out, and clears the attribute in a `replace`. A
394
+ `delete` naming the attribute, or naming it with no value, removes it,
395
+ unless it is `required`; naming values removes those, every value it holds
396
+ included, as the server does not read what remains, and `''` names no
397
+ value. An empty string sent as a value is checked against `test`, refused
398
+ for a pointer, and missing for a `required` attribute. Every refusal answers
399
+ 400 with the attribute's name.
400
+
401
+ #### What an attribute may be called
402
+
403
+ A name is read as the directory reads it: case and the `;binary` option do
404
+ not change the attribute it names, so `Cn`, `CN` and `cn;binary` all name
405
+ `cn`. Each key the schema declares is written under the schema's spelling —
406
+ `Cn` becomes `cn`, `L;binary` becomes `l;binary` — before any check, so that
407
+ a `required` attribute counts as present wherever it was sent, and a `fixed`
408
+ one is written once, at its default. The attribute's type is read the same
409
+ way when a value is converted, so a boolean sent as `pwdReset;binary` is
410
+ written `TRUE`.
411
+
412
+ Two keys of an entry or of one part of a change naming one attribute are
413
+ refused; the option does not make them two, so `l` and `l;binary` are
414
+ refused together. A name the schema does not declare is left as sent, and a
415
+ strict schema refuses it.
391
416
 
392
417
  Every entity route applies these rules; `test/lib/entitySchema.contract.test.ts`
393
418
  asserts them on a flat entity, a group and an organization alike. In the
@@ -451,7 +476,10 @@ Assigns semantic meaning to attributes for special handling by the application.
451
476
 
452
477
  #### `test` (Optional)
453
478
 
454
- Regular expression pattern for validating attribute values.
479
+ Regular expression pattern for validating attribute values. The value is
480
+ judged only up to its attribute's length: see
481
+ [`maxLength`](#maxlength-optional) below, which a deployment writing its own
482
+ pattern should read.
455
483
 
456
484
  ```json
457
485
  {
@@ -461,7 +489,7 @@ Regular expression pattern for validating attribute values.
461
489
  },
462
490
  "mail": {
463
491
  "type": "string",
464
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
492
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
465
493
  }
466
494
  }
467
495
  ```
@@ -474,7 +502,38 @@ Regular expression pattern for validating attribute values.
474
502
  "type": "array",
475
503
  "items": {
476
504
  "type": "string",
477
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
505
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
506
+ }
507
+ }
508
+ }
509
+ ```
510
+
511
+ #### `maxLength` (Optional)
512
+
513
+ Longest value, in UTF-16 code units (what `.length` counts, so an emoji or an
514
+ accented letter outside the basic plane counts as two), accepted before `test`
515
+ runs on it. A value past
516
+ it is refused with 400, and the pattern never reads it — whether or not it
517
+ would have matched. On an array, `maxLength` on the attribute governs every
518
+ item; one declared under `items` is read only when the attribute declares
519
+ none. It is a positive whole number of characters: the server refuses to load
520
+ a schema where it is not.
521
+
522
+ A pattern is written for values of its attribute, not for a payload of any
523
+ size: handed several kilobytes, a pattern whose parts overlap walks the value
524
+ in quadratic time, on the event loop that answers every other request
525
+ meanwhile. The limit keeps the pattern within what it was written for. It is
526
+ **4096** unless the schema says otherwise; an attribute holding longer values
527
+ — a description, a public key — declares its own.
528
+
529
+ ```json
530
+ {
531
+ "sshPublicKey": {
532
+ "type": "array",
533
+ "items": {
534
+ "type": "string",
535
+ "maxLength": 16384,
536
+ "test": "^[^\\r\\n]*$"
478
537
  }
479
538
  }
480
539
  }
@@ -594,7 +653,7 @@ Email-related roles help applications handle email addresses consistently across
594
653
  {
595
654
  "mail": {
596
655
  "type": "string",
597
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
656
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$",
598
657
  "required": false,
599
658
  "role": "primaryEmail"
600
659
  }
@@ -609,7 +668,7 @@ Email-related roles help applications handle email addresses consistently across
609
668
  "type": "array",
610
669
  "items": {
611
670
  "type": "string",
612
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
671
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
613
672
  },
614
673
  "required": false,
615
674
  "role": "emailAliases"
@@ -686,9 +745,14 @@ Common validation patterns:
686
745
  **Email:**
687
746
 
688
747
  ```regex
689
- ^[^@\s]+@[^@\s]+\.[^@\s]+$
748
+ ^[^@\s]+@[^@\s.]+(\.[^@\s.]+)+$
690
749
  ```
691
750
 
751
+ The domain part holds no dot of its own: where it did, the pattern tried every
752
+ dot as the last separator and read the value again each time — seconds for a
753
+ value of a few kilobytes. The result is that a trailing dot (`a@b.c.`) is
754
+ refused, where the older form accepted it.
755
+
692
756
  **DN (Distinguished Name):**
693
757
 
694
758
  ```regex
@@ -1045,7 +1109,7 @@ Start with the schema that most closely matches your directory type:
1045
1109
  "attributes": {
1046
1110
  "mail": {
1047
1111
  "type": "string",
1048
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
1112
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$",
1049
1113
  "required": false,
1050
1114
  "role": "primaryEmail"
1051
1115
  },
@@ -1238,7 +1302,7 @@ Use `test` patterns to enforce data quality:
1238
1302
  },
1239
1303
  "mail": {
1240
1304
  "type": "string",
1241
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
1305
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
1242
1306
  }
1243
1307
  }
1244
1308
  ```