@directus/api 20.0.0 → 21.0.0-rc.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 (330) hide show
  1. package/dist/app.js +5 -5
  2. package/dist/auth/drivers/ldap.js +5 -5
  3. package/dist/auth/drivers/local.js +4 -4
  4. package/dist/auth/drivers/oauth2.js +5 -5
  5. package/dist/auth/drivers/openid.js +3 -5
  6. package/dist/auth/drivers/saml.js +1 -1
  7. package/dist/auth.js +1 -1
  8. package/dist/cache.js +4 -1
  9. package/dist/cli/commands/bootstrap/index.js +10 -3
  10. package/dist/cli/commands/count/index.js +1 -1
  11. package/dist/cli/commands/database/install.js +1 -1
  12. package/dist/cli/commands/database/migrate.js +1 -1
  13. package/dist/cli/commands/init/index.js +9 -10
  14. package/dist/cli/commands/roles/create.js +1 -1
  15. package/dist/cli/commands/schema/apply.js +1 -1
  16. package/dist/cli/commands/schema/snapshot.js +1 -1
  17. package/dist/cli/commands/users/create.js +1 -1
  18. package/dist/cli/commands/users/passwd.js +1 -1
  19. package/dist/cli/load-extensions.js +1 -1
  20. package/dist/cli/utils/defaults.d.ts +4 -11
  21. package/dist/cli/utils/defaults.js +7 -1
  22. package/dist/constants.d.ts +1 -1
  23. package/dist/constants.js +2 -2
  24. package/dist/controllers/access.d.ts +2 -0
  25. package/dist/controllers/access.js +148 -0
  26. package/dist/controllers/assets.js +1 -1
  27. package/dist/controllers/auth.js +6 -17
  28. package/dist/controllers/files.js +1 -1
  29. package/dist/controllers/permissions.js +14 -2
  30. package/dist/controllers/policies.d.ts +2 -0
  31. package/dist/controllers/policies.js +169 -0
  32. package/dist/controllers/roles.js +22 -1
  33. package/dist/controllers/schema.js +1 -1
  34. package/dist/controllers/tus.js +11 -23
  35. package/dist/controllers/users.js +0 -55
  36. package/dist/database/get-ast-from-query/get-ast-from-query.d.ts +16 -0
  37. package/dist/database/get-ast-from-query/get-ast-from-query.js +82 -0
  38. package/dist/database/get-ast-from-query/lib/convert-wildcards.d.ts +13 -0
  39. package/dist/database/get-ast-from-query/lib/convert-wildcards.js +69 -0
  40. package/dist/database/get-ast-from-query/lib/parse-fields.d.ts +15 -0
  41. package/dist/database/get-ast-from-query/lib/parse-fields.js +190 -0
  42. package/dist/database/get-ast-from-query/utils/get-deep-query.d.ts +14 -0
  43. package/dist/database/get-ast-from-query/utils/get-deep-query.js +17 -0
  44. package/dist/database/get-ast-from-query/utils/get-related-collection.d.ts +2 -0
  45. package/dist/database/get-ast-from-query/utils/get-related-collection.js +13 -0
  46. package/dist/database/get-ast-from-query/utils/get-relation.d.ts +2 -0
  47. package/dist/database/get-ast-from-query/utils/get-relation.js +7 -0
  48. package/dist/database/helpers/fn/types.d.ts +2 -1
  49. package/dist/database/helpers/fn/types.js +1 -1
  50. package/dist/database/helpers/geometry/dialects/mssql.d.ts +1 -1
  51. package/dist/database/helpers/geometry/dialects/mssql.js +4 -2
  52. package/dist/database/helpers/geometry/dialects/mysql.js +1 -1
  53. package/dist/database/helpers/geometry/dialects/oracle.d.ts +1 -1
  54. package/dist/database/helpers/geometry/dialects/oracle.js +5 -3
  55. package/dist/database/helpers/geometry/types.d.ts +1 -1
  56. package/dist/database/helpers/geometry/types.js +4 -2
  57. package/dist/database/index.js +3 -2
  58. package/dist/database/migrations/20210518A-add-foreign-key-constraints.js +1 -1
  59. package/dist/database/migrations/20210519A-add-system-fk-triggers.js +1 -1
  60. package/dist/database/migrations/20210802A-replace-groups.js +1 -1
  61. package/dist/database/migrations/20230721A-require-shares-fields.js +1 -1
  62. package/dist/database/migrations/20240710A-permissions-policies.d.ts +3 -0
  63. package/dist/database/migrations/20240710A-permissions-policies.js +169 -0
  64. package/dist/database/migrations/run.js +1 -1
  65. package/dist/database/run-ast/lib/get-db-query.d.ts +4 -0
  66. package/dist/database/run-ast/lib/get-db-query.js +208 -0
  67. package/dist/database/run-ast/lib/parse-current-level.d.ts +7 -0
  68. package/dist/database/run-ast/lib/parse-current-level.js +41 -0
  69. package/dist/database/run-ast/run-ast.d.ts +7 -0
  70. package/dist/database/run-ast/run-ast.js +107 -0
  71. package/dist/database/{run-ast.d.ts → run-ast/types.d.ts} +3 -9
  72. package/dist/database/run-ast/types.js +1 -0
  73. package/dist/database/run-ast/utils/apply-case-when.d.ts +16 -0
  74. package/dist/database/run-ast/utils/apply-case-when.js +26 -0
  75. package/dist/database/run-ast/utils/apply-parent-filters.d.ts +3 -0
  76. package/dist/database/run-ast/utils/apply-parent-filters.js +55 -0
  77. package/dist/database/run-ast/utils/get-column-pre-processor.d.ts +10 -0
  78. package/dist/database/run-ast/utils/get-column-pre-processor.js +57 -0
  79. package/dist/database/run-ast/utils/get-field-alias.d.ts +2 -0
  80. package/dist/database/run-ast/utils/get-field-alias.js +4 -0
  81. package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.d.ts +5 -0
  82. package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.js +23 -0
  83. package/dist/database/run-ast/utils/merge-with-parent-items.d.ts +3 -0
  84. package/dist/database/run-ast/utils/merge-with-parent-items.js +87 -0
  85. package/dist/database/run-ast/utils/remove-temporary-fields.d.ts +3 -0
  86. package/dist/database/run-ast/utils/remove-temporary-fields.js +73 -0
  87. package/dist/emitter.js +1 -1
  88. package/dist/extensions/lib/get-shared-deps-mapping.js +1 -1
  89. package/dist/extensions/lib/installation/manager.js +1 -1
  90. package/dist/extensions/lib/sandbox/register/call-reference.js +1 -1
  91. package/dist/extensions/lib/sandbox/sdk/generators/log.js +1 -1
  92. package/dist/extensions/lib/sync-extensions.js +1 -1
  93. package/dist/extensions/manager.js +1 -1
  94. package/dist/flows.js +4 -5
  95. package/dist/{logger.js → logger/index.js} +2 -8
  96. package/dist/logger/redact-query.d.ts +1 -0
  97. package/dist/logger/redact-query.js +13 -0
  98. package/dist/mailer.js +1 -1
  99. package/dist/middleware/authenticate.js +2 -7
  100. package/dist/middleware/cache.js +2 -2
  101. package/dist/middleware/error-handler.js +1 -1
  102. package/dist/middleware/rate-limiter-global.js +1 -1
  103. package/dist/middleware/respond.js +2 -2
  104. package/dist/operations/log/index.js +1 -1
  105. package/dist/operations/mail/index.js +1 -1
  106. package/dist/permissions/cache.d.ts +2 -0
  107. package/dist/permissions/cache.js +23 -0
  108. package/dist/permissions/lib/fetch-permissions.d.ts +10 -0
  109. package/dist/permissions/lib/fetch-permissions.js +55 -0
  110. package/dist/permissions/lib/fetch-policies.d.ts +7 -0
  111. package/dist/permissions/lib/fetch-policies.js +28 -0
  112. package/dist/permissions/lib/fetch-roles-tree.d.ts +3 -0
  113. package/dist/permissions/lib/fetch-roles-tree.js +28 -0
  114. package/dist/{services/permissions → permissions}/lib/with-app-minimal-permissions.d.ts +1 -1
  115. package/dist/permissions/lib/with-app-minimal-permissions.js +10 -0
  116. package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.d.ts +7 -0
  117. package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js +56 -0
  118. package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.d.ts +3 -0
  119. package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js +16 -0
  120. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.d.ts +8 -0
  121. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +24 -0
  122. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.d.ts +9 -0
  123. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +31 -0
  124. package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.d.ts +16 -0
  125. package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js +27 -0
  126. package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +10 -0
  127. package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +23 -0
  128. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +5 -0
  129. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +7 -0
  130. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +5 -0
  131. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +10 -0
  132. package/dist/permissions/modules/fetch-global-access/types.d.ts +4 -0
  133. package/dist/permissions/modules/fetch-global-access/types.js +1 -0
  134. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +4 -0
  135. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +27 -0
  136. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.d.ts +12 -0
  137. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +32 -0
  138. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +4 -0
  139. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.js +29 -0
  140. package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.d.ts +4 -0
  141. package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.js +49 -0
  142. package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.d.ts +3 -0
  143. package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.js +56 -0
  144. package/dist/permissions/modules/process-ast/lib/field-map-from-ast.d.ts +4 -0
  145. package/dist/permissions/modules/process-ast/lib/field-map-from-ast.js +8 -0
  146. package/dist/permissions/modules/process-ast/lib/inject-cases.d.ts +9 -0
  147. package/dist/permissions/modules/process-ast/lib/inject-cases.js +93 -0
  148. package/dist/permissions/modules/process-ast/process-ast.d.ts +9 -0
  149. package/dist/permissions/modules/process-ast/process-ast.js +39 -0
  150. package/dist/permissions/modules/process-ast/types.d.ts +24 -0
  151. package/dist/permissions/modules/process-ast/types.js +1 -0
  152. package/dist/permissions/modules/process-ast/utils/collections-in-field-map.d.ts +2 -0
  153. package/dist/permissions/modules/process-ast/utils/collections-in-field-map.js +7 -0
  154. package/dist/permissions/modules/process-ast/utils/dedupe-access.d.ts +12 -0
  155. package/dist/permissions/modules/process-ast/utils/dedupe-access.js +30 -0
  156. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.d.ts +15 -0
  157. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +50 -0
  158. package/dist/permissions/modules/process-ast/utils/find-related-collection.d.ts +3 -0
  159. package/dist/permissions/modules/process-ast/utils/find-related-collection.js +9 -0
  160. package/dist/permissions/modules/process-ast/utils/flatten-filter.d.ts +3 -0
  161. package/dist/permissions/modules/process-ast/utils/flatten-filter.js +34 -0
  162. package/dist/permissions/modules/process-ast/utils/format-a2o-key.d.ts +1 -0
  163. package/dist/permissions/modules/process-ast/utils/format-a2o-key.js +3 -0
  164. package/dist/permissions/modules/process-ast/utils/get-info-for-path.d.ts +5 -0
  165. package/dist/permissions/modules/process-ast/utils/get-info-for-path.js +7 -0
  166. package/dist/permissions/modules/process-ast/utils/has-item-permissions.d.ts +2 -0
  167. package/dist/permissions/modules/process-ast/utils/has-item-permissions.js +3 -0
  168. package/dist/permissions/modules/process-ast/utils/stringify-query-path.d.ts +2 -0
  169. package/dist/permissions/modules/process-ast/utils/stringify-query-path.js +3 -0
  170. package/dist/permissions/modules/process-ast/utils/validate-path/create-error.d.ts +3 -0
  171. package/dist/permissions/modules/process-ast/utils/validate-path/create-error.js +16 -0
  172. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.d.ts +2 -0
  173. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.js +12 -0
  174. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.d.ts +2 -0
  175. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.js +28 -0
  176. package/dist/permissions/modules/process-payload/lib/is-field-nullable.d.ts +5 -0
  177. package/dist/permissions/modules/process-payload/lib/is-field-nullable.js +12 -0
  178. package/dist/permissions/modules/process-payload/process-payload.d.ts +13 -0
  179. package/dist/permissions/modules/process-payload/process-payload.js +77 -0
  180. package/dist/permissions/modules/validate-access/lib/validate-collection-access.d.ts +12 -0
  181. package/dist/permissions/modules/validate-access/lib/validate-collection-access.js +11 -0
  182. package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +9 -0
  183. package/dist/permissions/modules/validate-access/lib/validate-item-access.js +33 -0
  184. package/dist/permissions/modules/validate-access/validate-access.d.ts +14 -0
  185. package/dist/permissions/modules/validate-access/validate-access.js +28 -0
  186. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.d.ts +1 -0
  187. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.js +8 -0
  188. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.d.ts +5 -0
  189. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.js +10 -0
  190. package/dist/permissions/types.d.ts +6 -0
  191. package/dist/permissions/types.js +1 -0
  192. package/dist/permissions/utils/create-default-accountability.d.ts +2 -0
  193. package/dist/permissions/utils/create-default-accountability.js +11 -0
  194. package/dist/permissions/utils/extract-required-dynamic-variable-context.d.ts +8 -0
  195. package/dist/permissions/utils/extract-required-dynamic-variable-context.js +27 -0
  196. package/dist/permissions/utils/fetch-dynamic-variable-context.d.ts +9 -0
  197. package/dist/permissions/utils/fetch-dynamic-variable-context.js +43 -0
  198. package/dist/permissions/utils/filter-policies-by-ip.d.ts +2 -0
  199. package/dist/permissions/utils/filter-policies-by-ip.js +15 -0
  200. package/dist/permissions/utils/get-unaliased-field-key.d.ts +5 -0
  201. package/dist/permissions/utils/get-unaliased-field-key.js +17 -0
  202. package/dist/permissions/utils/process-permissions.d.ts +7 -0
  203. package/dist/permissions/utils/process-permissions.js +9 -0
  204. package/dist/permissions/utils/with-cache.d.ts +10 -0
  205. package/dist/permissions/utils/with-cache.js +25 -0
  206. package/dist/request/is-denied-ip.js +1 -1
  207. package/dist/server.js +1 -1
  208. package/dist/services/access.d.ts +10 -0
  209. package/dist/services/access.js +43 -0
  210. package/dist/services/activity.js +23 -11
  211. package/dist/services/assets.d.ts +2 -3
  212. package/dist/services/assets.js +11 -6
  213. package/dist/services/authentication.js +18 -18
  214. package/dist/services/collections.js +18 -17
  215. package/dist/services/fields.d.ts +0 -1
  216. package/dist/services/fields.js +53 -24
  217. package/dist/services/files/utils/get-metadata.js +1 -1
  218. package/dist/services/files.js +25 -15
  219. package/dist/services/graphql/index.d.ts +3 -3
  220. package/dist/services/graphql/index.js +126 -22
  221. package/dist/services/graphql/subscription.js +2 -4
  222. package/dist/services/graphql/utils/process-error.js +1 -1
  223. package/dist/services/graphql/utils/sanitize-gql-schema.js +1 -1
  224. package/dist/services/import-export.js +19 -5
  225. package/dist/services/index.d.ts +3 -2
  226. package/dist/services/index.js +3 -2
  227. package/dist/services/items.d.ts +3 -3
  228. package/dist/services/items.js +115 -44
  229. package/dist/services/mail/index.js +1 -1
  230. package/dist/services/meta.js +60 -23
  231. package/dist/services/notifications.js +15 -7
  232. package/dist/services/payload.d.ts +9 -10
  233. package/dist/services/payload.js +18 -3
  234. package/dist/services/{permissions/index.d.ts → permissions.d.ts} +5 -7
  235. package/dist/services/{permissions/index.js → permissions.js} +30 -54
  236. package/dist/services/policies.d.ts +12 -0
  237. package/dist/services/policies.js +87 -0
  238. package/dist/services/relations.d.ts +0 -6
  239. package/dist/services/relations.js +26 -29
  240. package/dist/services/roles.d.ts +4 -12
  241. package/dist/services/roles.js +57 -424
  242. package/dist/services/server.js +1 -1
  243. package/dist/services/shares.d.ts +0 -2
  244. package/dist/services/shares.js +13 -9
  245. package/dist/services/specifications.d.ts +2 -2
  246. package/dist/services/specifications.js +39 -27
  247. package/dist/services/tus/data-store.js +3 -1
  248. package/dist/services/users.d.ts +1 -5
  249. package/dist/services/users.js +79 -162
  250. package/dist/services/utils.js +11 -7
  251. package/dist/services/versions.d.ts +0 -2
  252. package/dist/services/versions.js +34 -10
  253. package/dist/services/webhooks.js +1 -1
  254. package/dist/telemetry/lib/get-report.js +2 -2
  255. package/dist/telemetry/lib/track.js +1 -1
  256. package/dist/telemetry/utils/check-user-limits.d.ts +5 -0
  257. package/dist/telemetry/utils/check-user-limits.js +19 -0
  258. package/dist/types/ast.d.ts +43 -1
  259. package/dist/types/items.d.ts +11 -0
  260. package/dist/utils/apply-diff.js +1 -1
  261. package/dist/utils/apply-query.d.ts +4 -3
  262. package/dist/utils/apply-query.js +37 -8
  263. package/dist/utils/delete-from-require-cache.js +1 -1
  264. package/dist/utils/fetch-user-count/fetch-access-lookup.d.ts +17 -0
  265. package/dist/utils/fetch-user-count/fetch-access-lookup.js +22 -0
  266. package/dist/utils/fetch-user-count/fetch-access-roles.d.ts +16 -0
  267. package/dist/utils/fetch-user-count/fetch-access-roles.js +37 -0
  268. package/dist/utils/fetch-user-count/fetch-active-users.d.ts +6 -0
  269. package/dist/utils/fetch-user-count/fetch-active-users.js +3 -0
  270. package/dist/utils/fetch-user-count/fetch-user-count.d.ts +12 -0
  271. package/dist/utils/fetch-user-count/fetch-user-count.js +57 -0
  272. package/dist/utils/fetch-user-count/get-user-count-query.d.ts +20 -0
  273. package/dist/utils/fetch-user-count/get-user-count-query.js +17 -0
  274. package/dist/utils/get-accountability-for-role.js +16 -25
  275. package/dist/utils/get-accountability-for-token.js +17 -16
  276. package/dist/utils/get-cache-key.d.ts +1 -1
  277. package/dist/utils/get-cache-key.js +12 -1
  278. package/dist/utils/get-column.d.ts +2 -1
  279. package/dist/utils/get-column.js +1 -0
  280. package/dist/utils/get-default-value.js +1 -1
  281. package/dist/utils/get-ip-from-req.js +1 -1
  282. package/dist/utils/get-schema.js +1 -1
  283. package/dist/utils/get-service.js +5 -1
  284. package/dist/utils/is-url-allowed.js +1 -1
  285. package/dist/utils/reduce-schema.d.ts +4 -6
  286. package/dist/utils/reduce-schema.js +16 -32
  287. package/dist/utils/sanitize-query.js +1 -1
  288. package/dist/utils/transaction.js +1 -1
  289. package/dist/utils/validate-env.js +1 -1
  290. package/dist/utils/validate-storage.js +1 -1
  291. package/dist/utils/validate-user-count-integrity.d.ts +13 -0
  292. package/dist/utils/validate-user-count-integrity.js +29 -0
  293. package/dist/websocket/authenticate.d.ts +0 -2
  294. package/dist/websocket/authenticate.js +0 -12
  295. package/dist/websocket/controllers/base.d.ts +1 -1
  296. package/dist/websocket/controllers/base.js +21 -17
  297. package/dist/websocket/controllers/graphql.js +2 -5
  298. package/dist/websocket/controllers/hooks.js +4 -0
  299. package/dist/websocket/controllers/rest.js +1 -3
  300. package/dist/websocket/errors.js +1 -1
  301. package/dist/websocket/handlers/subscribe.js +0 -2
  302. package/dist/websocket/utils/items.d.ts +1 -1
  303. package/package.json +28 -27
  304. package/dist/database/run-ast.js +0 -450
  305. package/dist/middleware/check-ip.d.ts +0 -2
  306. package/dist/middleware/check-ip.js +0 -37
  307. package/dist/middleware/get-permissions.d.ts +0 -3
  308. package/dist/middleware/get-permissions.js +0 -10
  309. package/dist/services/authorization.d.ts +0 -17
  310. package/dist/services/authorization.js +0 -456
  311. package/dist/services/permissions/lib/with-app-minimal-permissions.js +0 -13
  312. package/dist/telemetry/utils/check-increased-user-limits.d.ts +0 -7
  313. package/dist/telemetry/utils/check-increased-user-limits.js +0 -25
  314. package/dist/telemetry/utils/get-role-counts-by-roles.d.ts +0 -6
  315. package/dist/telemetry/utils/get-role-counts-by-roles.js +0 -27
  316. package/dist/telemetry/utils/get-role-counts-by-users.d.ts +0 -11
  317. package/dist/telemetry/utils/get-role-counts-by-users.js +0 -34
  318. package/dist/telemetry/utils/get-user-count.d.ts +0 -8
  319. package/dist/telemetry/utils/get-user-count.js +0 -33
  320. package/dist/telemetry/utils/get-user-counts-by-roles.d.ts +0 -7
  321. package/dist/telemetry/utils/get-user-counts-by-roles.js +0 -35
  322. package/dist/utils/get-ast-from-query.d.ts +0 -13
  323. package/dist/utils/get-ast-from-query.js +0 -297
  324. package/dist/utils/get-permissions.d.ts +0 -2
  325. package/dist/utils/get-permissions.js +0 -150
  326. package/dist/utils/merge-permissions-for-share.d.ts +0 -4
  327. package/dist/utils/merge-permissions-for-share.js +0 -109
  328. package/dist/utils/merge-permissions.d.ts +0 -3
  329. package/dist/utils/merge-permissions.js +0 -95
  330. /package/dist/{logger.d.ts → logger/index.d.ts} +0 -0
@@ -2,11 +2,11 @@ import { useEnv } from '@directus/env';
2
2
  import { version } from 'directus/version';
3
3
  import { getHelpers } from '../../database/helpers/index.js';
4
4
  import { getDatabase, getDatabaseClient } from '../../database/index.js';
5
+ import { fetchUserCount } from '../../utils/fetch-user-count/fetch-user-count.js';
5
6
  import { getExtensionCount } from '../utils/get-extension-count.js';
6
7
  import { getFieldCount } from '../utils/get-field-count.js';
7
8
  import { getFilesizeSum } from '../utils/get-filesize-sum.js';
8
9
  import { getItemCount } from '../utils/get-item-count.js';
9
- import { getUserCount } from '../utils/get-user-count.js';
10
10
  import { getUserItemCount } from '../utils/get-user-item-count.js';
11
11
  const basicCountTasks = [
12
12
  { collection: 'directus_dashboards' },
@@ -27,7 +27,7 @@ export const getReport = async () => {
27
27
  const helpers = getHelpers(db);
28
28
  const [basicCounts, userCounts, userItemCount, fieldsCounts, extensionsCounts, databaseSize, filesizes] = await Promise.all([
29
29
  getItemCount(db, basicCountTasks),
30
- getUserCount(db),
30
+ fetchUserCount({ knex: db }),
31
31
  getUserItemCount(db),
32
32
  getFieldCount(db),
33
33
  getExtensionCount(db),
@@ -1,6 +1,6 @@
1
1
  import { getNodeEnv } from '@directus/utils/node';
2
2
  import { setTimeout } from 'timers/promises';
3
- import { useLogger } from '../../logger.js';
3
+ import { useLogger } from '../../logger/index.js';
4
4
  import { getRandomWaitTime } from '../utils/get-random-wait-time.js';
5
5
  import { getReport } from './get-report.js';
6
6
  import { sendReport } from './send-report.js';
@@ -0,0 +1,5 @@
1
+ import { type UserCount } from '../../utils/fetch-user-count/fetch-user-count.js';
2
+ /**
3
+ * Ensure that user limits are not reached
4
+ */
5
+ export declare function checkUserLimits(userCounts: UserCount): Promise<void>;
@@ -0,0 +1,19 @@
1
+ import { useEnv } from '@directus/env';
2
+ import { LimitExceededError } from '@directus/errors';
3
+ import {} from '../../utils/fetch-user-count/fetch-user-count.js';
4
+ const env = useEnv();
5
+ /**
6
+ * Ensure that user limits are not reached
7
+ */
8
+ export async function checkUserLimits(userCounts) {
9
+ if (userCounts.admin > Number(env['USERS_ADMIN_ACCESS_LIMIT'])) {
10
+ throw new LimitExceededError({ category: 'Active Admin users' });
11
+ }
12
+ // Both app and admin users count against the app access limit
13
+ if (userCounts.app + userCounts.admin > Number(env['USERS_APP_ACCESS_LIMIT'])) {
14
+ throw new LimitExceededError({ category: 'Active App users' });
15
+ }
16
+ if (userCounts.api > Number(env['USERS_API_ACCESS_LIMIT'])) {
17
+ throw new LimitExceededError({ category: 'Active API users' });
18
+ }
19
+ }
@@ -1,4 +1,4 @@
1
- import type { Query, Relation } from '@directus/types';
1
+ import type { Filter, Query, Relation } from '@directus/types';
2
2
  export type M2ONode = {
3
3
  type: 'm2o';
4
4
  name: string;
@@ -8,6 +8,14 @@ export type M2ONode = {
8
8
  relation: Relation;
9
9
  parentKey: string;
10
10
  relatedKey: string;
11
+ /**
12
+ * Which permission cases have to be met on the current item for this field to return a value
13
+ */
14
+ whenCase: number[];
15
+ /**
16
+ * Permissions rules for the item access of the children of this item.
17
+ */
18
+ cases: Filter[];
11
19
  };
12
20
  export type A2MNode = {
13
21
  type: 'a2o';
@@ -24,6 +32,16 @@ export type A2MNode = {
24
32
  fieldKey: string;
25
33
  relation: Relation;
26
34
  parentKey: string;
35
+ /**
36
+ * Which permission cases have to be met on the current item for this field to return a value
37
+ */
38
+ whenCase: number[];
39
+ /**
40
+ * Permissions rules for the item access of the children of this item.
41
+ */
42
+ cases: {
43
+ [collection: string]: Filter[];
44
+ };
27
45
  };
28
46
  export type O2MNode = {
29
47
  type: 'o2m';
@@ -34,12 +52,24 @@ export type O2MNode = {
34
52
  relation: Relation;
35
53
  parentKey: string;
36
54
  relatedKey: string;
55
+ /**
56
+ * Which permission cases have to be met on the current item for this field to return a value
57
+ */
58
+ whenCase: number[];
59
+ /**
60
+ * Permissions rules for the item access of the children of this item.
61
+ */
62
+ cases: Filter[];
37
63
  };
38
64
  export type NestedCollectionNode = M2ONode | O2MNode | A2MNode;
39
65
  export type FieldNode = {
40
66
  type: 'field';
41
67
  name: string;
42
68
  fieldKey: string;
69
+ /**
70
+ * Which permission cases have to be met on the current item for this field to return a value
71
+ */
72
+ whenCase: number[];
43
73
  };
44
74
  export type FunctionFieldNode = {
45
75
  type: 'functionField';
@@ -47,10 +77,22 @@ export type FunctionFieldNode = {
47
77
  fieldKey: string;
48
78
  query: Query;
49
79
  relatedCollection: string;
80
+ /**
81
+ * Which permission cases have to be met on the current item for this field to return a value
82
+ */
83
+ whenCase: number[];
84
+ /**
85
+ * Permissions rules for the item access of the related collection of this item.
86
+ */
87
+ cases: Filter[];
50
88
  };
51
89
  export type AST = {
52
90
  type: 'root';
53
91
  name: string;
54
92
  children: (NestedCollectionNode | FieldNode | FunctionFieldNode)[];
55
93
  query: Query;
94
+ /**
95
+ * Permissions rules for the item access of the children of this item.
96
+ */
97
+ cases: Filter[];
56
98
  };
@@ -1,6 +1,7 @@
1
1
  import type { DirectusError } from '@directus/errors';
2
2
  import type { EventContext, PrimaryKey } from '@directus/types';
3
3
  import type { MutationTracker } from '../services/items.js';
4
+ import type { UserIntegrityCheckFlag } from '../utils/validate-user-count-integrity.js';
4
5
  export type MutationOptions = {
5
6
  /**
6
7
  * Callback function that's fired whenever a revision is made in the mutation
@@ -33,6 +34,16 @@ export type MutationOptions = {
33
34
  mutationTracker?: MutationTracker | undefined;
34
35
  preMutationError?: DirectusError | undefined;
35
36
  bypassAutoIncrementSequenceReset?: boolean;
37
+ /**
38
+ * Indicate that the top level mutation needs to perform a user integrity check before commiting the transaction
39
+ * This is a combination of flags
40
+ * @see UserIntegrityCheckFlag
41
+ */
42
+ userIntegrityCheckFlags?: UserIntegrityCheckFlag;
43
+ /**
44
+ * Callback function that is called whenever a mutation requires a user integrity check to be made
45
+ */
46
+ onRequireUserIntegrityCheck?: ((flags: UserIntegrityCheckFlag) => void) | undefined;
36
47
  };
37
48
  export type ActionEventParams = {
38
49
  event: string | string[];
@@ -4,7 +4,7 @@ import { flushCaches } from '../cache.js';
4
4
  import { getHelpers } from '../database/helpers/index.js';
5
5
  import getDatabase from '../database/index.js';
6
6
  import emitter from '../emitter.js';
7
- import { useLogger } from '../logger.js';
7
+ import { useLogger } from '../logger/index.js';
8
8
  import { CollectionsService } from '../services/collections.js';
9
9
  import { FieldsService } from '../services/fields.js';
10
10
  import { RelationsService } from '../services/relations.js';
@@ -5,7 +5,7 @@ export declare const generateAlias: (size?: number | undefined) => string;
5
5
  /**
6
6
  * Apply the Query to a given Knex query builder instance
7
7
  */
8
- export default function applyQuery(knex: Knex, collection: string, dbQuery: Knex.QueryBuilder, query: Query, schema: SchemaOverview, options?: {
8
+ export default function applyQuery(knex: Knex, collection: string, dbQuery: Knex.QueryBuilder, query: Query, schema: SchemaOverview, cases: Filter[], options?: {
9
9
  aliasMap?: AliasMap;
10
10
  isInnerQuery?: boolean;
11
11
  hasMultiRelationalSort?: boolean | undefined;
@@ -32,10 +32,11 @@ export declare function applySort(knex: Knex, schema: SchemaOverview, rootQuery:
32
32
  };
33
33
  export declare function applyLimit(knex: Knex, rootQuery: Knex.QueryBuilder, limit: any): void;
34
34
  export declare function applyOffset(knex: Knex, rootQuery: Knex.QueryBuilder, offset: any): void;
35
- export declare function applyFilter(knex: Knex, schema: SchemaOverview, rootQuery: Knex.QueryBuilder, rootFilter: Filter, collection: string, aliasMap: AliasMap): {
35
+ export declare function applyFilter(knex: Knex, schema: SchemaOverview, rootQuery: Knex.QueryBuilder, rootFilter: Filter, collection: string, aliasMap: AliasMap, cases: Filter[]): {
36
36
  query: Knex.QueryBuilder<any, any>;
37
37
  hasJoins: boolean;
38
38
  hasMultiRelationalFilter: boolean;
39
39
  };
40
- export declare function applySearch(knex: Knex, schema: SchemaOverview, dbQuery: Knex.QueryBuilder, searchQuery: string, collection: string): Promise<void>;
40
+ export declare function applySearch(knex: Knex, schema: SchemaOverview, dbQuery: Knex.QueryBuilder, searchQuery: string, collection: string): void;
41
41
  export declare function applyAggregate(schema: SchemaOverview, dbQuery: Knex.QueryBuilder, aggregate: Aggregate, collection: string, hasJoins: boolean): void;
42
+ export declare function joinFilterWithCases(filter: Filter | null | undefined, cases: Filter[]): Filter | null;
@@ -14,7 +14,7 @@ export const generateAlias = customAlphabet('abcdefghijklmnopqrstuvwxyz', 5);
14
14
  /**
15
15
  * Apply the Query to a given Knex query builder instance
16
16
  */
17
- export default function applyQuery(knex, collection, dbQuery, query, schema, options) {
17
+ export default function applyQuery(knex, collection, dbQuery, query, schema, cases, options) {
18
18
  const aliasMap = options?.aliasMap ?? Object.create(null);
19
19
  let hasJoins = false;
20
20
  let hasMultiRelationalFilter = false;
@@ -37,8 +37,14 @@ export default function applyQuery(knex, collection, dbQuery, query, schema, opt
37
37
  if (query.group) {
38
38
  dbQuery.groupBy(query.group.map((column) => getColumn(knex, collection, column, false, schema)));
39
39
  }
40
- if (query.filter) {
41
- const filterResult = applyFilter(knex, schema, dbQuery, query.filter, collection, aliasMap);
40
+ // `cases` are the permissions cases that are required for the current data set. We're
41
+ // dynamically adding those into the filters that the user provided to enforce the permission
42
+ // rules. You should be able to read an item if one or more of the cases matches. The actual case
43
+ // is reused in the column selection case/when to dynamically return or nullify the field values
44
+ // you're actually allowed to read
45
+ const filter = joinFilterWithCases(query.filter, cases);
46
+ if (filter) {
47
+ const filterResult = applyFilter(knex, schema, dbQuery, filter, collection, aliasMap, cases);
42
48
  if (!hasJoins) {
43
49
  hasJoins = filterResult.hasJoins;
44
50
  }
@@ -226,7 +232,7 @@ export function applyOffset(knex, rootQuery, offset) {
226
232
  getHelpers(knex).schema.applyOffset(rootQuery, offset);
227
233
  }
228
234
  }
229
- export function applyFilter(knex, schema, rootQuery, rootFilter, collection, aliasMap) {
235
+ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, aliasMap, cases) {
230
236
  const helpers = getHelpers(knex);
231
237
  const relations = schema.relations;
232
238
  let hasJoins = false;
@@ -235,12 +241,23 @@ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, ali
235
241
  addWhereClauses(knex, rootQuery, rootFilter, collection);
236
242
  return { query: rootQuery, hasJoins, hasMultiRelationalFilter };
237
243
  function addJoins(dbQuery, filter, collection) {
238
- for (const [key, value] of Object.entries(filter)) {
244
+ // eslint-disable-next-line prefer-const
245
+ for (let [key, value] of Object.entries(filter)) {
239
246
  if (key === '_or' || key === '_and') {
240
247
  // If the _or array contains an empty object (full permissions), we should short-circuit and ignore all other
241
248
  // permission checks, as {} already matches full permissions.
242
249
  if (key === '_or' && value.some((subFilter) => Object.keys(subFilter).length === 0)) {
243
- continue;
250
+ // But only do so, if the value is not equal to `cases` (since then this is not permission related at all)
251
+ // or the length of value is 1, ie. only the empty filter.
252
+ // If the length is more than one it means that some items (and fields) might now be available, so
253
+ // the joins are required for the case/when construction.
254
+ if (value !== cases || value.length === 1) {
255
+ continue;
256
+ }
257
+ else {
258
+ // Otherwise we can at least filter out all empty filters that would not add joins anyway
259
+ value = value.filter((subFilter) => Object.keys(subFilter).length > 0);
260
+ }
244
261
  }
245
262
  value.forEach((subFilter) => {
246
263
  addJoins(dbQuery, subFilter, collection);
@@ -311,7 +328,7 @@ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, ali
311
328
  .select({ [field]: column })
312
329
  .from(collection)
313
330
  .whereNotNull(column);
314
- applyQuery(knex, relation.collection, subQueryKnex, { filter }, schema);
331
+ applyQuery(knex, relation.collection, subQueryKnex, { filter }, schema, cases);
315
332
  };
316
333
  const childKey = Object.keys(value)?.[0];
317
334
  if (childKey === '_none') {
@@ -551,7 +568,7 @@ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, ali
551
568
  }
552
569
  }
553
570
  }
554
- export async function applySearch(knex, schema, dbQuery, searchQuery, collection) {
571
+ export function applySearch(knex, schema, dbQuery, searchQuery, collection) {
555
572
  const { number: numberHelper } = getHelpers(knex);
556
573
  const fields = Object.entries(schema.collections[collection].fields);
557
574
  dbQuery.andWhere(function () {
@@ -627,6 +644,18 @@ export function applyAggregate(schema, dbQuery, aggregate, collection, hasJoins)
627
644
  }
628
645
  }
629
646
  }
647
+ export function joinFilterWithCases(filter, cases) {
648
+ if (cases.length > 0 && !filter) {
649
+ return { _or: cases };
650
+ }
651
+ else if (filter && cases.length === 0) {
652
+ return filter ?? null;
653
+ }
654
+ else if (filter && cases.length > 0) {
655
+ return { _and: [filter, { _or: cases }] };
656
+ }
657
+ return null;
658
+ }
630
659
  function getFilterPath(key, value) {
631
660
  const path = [key];
632
661
  const childKey = Object.keys(value)[0];
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from 'node:module';
2
- import { useLogger } from '../logger.js';
2
+ import { useLogger } from '../logger/index.js';
3
3
  const require = createRequire(import.meta.url);
4
4
  export function deleteFromRequireCache(modulePath) {
5
5
  const logger = useLogger();
@@ -0,0 +1,17 @@
1
+ import type { PrimaryKey } from '@directus/types';
2
+ import type { Knex } from 'knex';
3
+ export interface AccessLookup {
4
+ role: string | null;
5
+ user: string | null;
6
+ app_access: boolean | number;
7
+ admin_access: boolean | number;
8
+ }
9
+ export interface FetchAccessLookupOptions {
10
+ excludeAccessRows?: PrimaryKey[];
11
+ excludePolicies?: PrimaryKey[];
12
+ excludeUsers?: PrimaryKey[];
13
+ excludeRoles?: PrimaryKey[];
14
+ adminOnly?: boolean;
15
+ knex: Knex;
16
+ }
17
+ export declare function fetchAccessLookup(options: FetchAccessLookupOptions): Promise<AccessLookup[]>;
@@ -0,0 +1,22 @@
1
+ export async function fetchAccessLookup(options) {
2
+ let query = options.knex
3
+ .select('directus_access.role', 'directus_access.user', 'directus_policies.app_access', 'directus_policies.admin_access')
4
+ .from('directus_access')
5
+ .leftJoin('directus_policies', 'directus_access.policy', 'directus_policies.id');
6
+ if (options.excludeAccessRows && options.excludeAccessRows.length > 0) {
7
+ query = query.whereNotIn('directus_access.id', options.excludeAccessRows);
8
+ }
9
+ if (options.excludePolicies && options.excludePolicies.length > 0) {
10
+ query = query.whereNotIn('directus_access.policy', options.excludePolicies);
11
+ }
12
+ if (options.excludeUsers && options.excludeUsers.length > 0) {
13
+ query = query.where((q) => q.whereNotIn('directus_access.user', options.excludeUsers).orWhereNull('directus_access.user'));
14
+ }
15
+ if (options.excludeRoles && options.excludeRoles.length > 0) {
16
+ query = query.where((q) => q.whereNotIn('directus_access.role', options.excludeRoles).orWhereNull('directus_access.role'));
17
+ }
18
+ if (options.adminOnly) {
19
+ query = query.where('directus_policies.admin_access', 1);
20
+ }
21
+ return query;
22
+ }
@@ -0,0 +1,16 @@
1
+ import type { PrimaryKey } from '@directus/types';
2
+ import type { Knex } from 'knex';
3
+ export interface FetchAccessRolesOptions {
4
+ adminRoles: Set<string>;
5
+ appRoles: Set<string>;
6
+ excludeRoles?: PrimaryKey[];
7
+ }
8
+ /**
9
+ * Return a set of roles that allow app or admin access, if itself or any of its parents do
10
+ */
11
+ export declare function fetchAccessRoles(options: FetchAccessRolesOptions, context: {
12
+ knex: Knex;
13
+ }): Promise<{
14
+ adminRoles: Set<string>;
15
+ appRoles: Set<string>;
16
+ }>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Return a set of roles that allow app or admin access, if itself or any of its parents do
3
+ */
4
+ export async function fetchAccessRoles(options, context) {
5
+ // Only fetch the roles that have a parent, as otherwise those roles should already be included in at least one of the input set
6
+ const allChildRoles = await context.knex
7
+ .select('id', 'parent')
8
+ .from('directus_roles')
9
+ .whereNotNull('parent')
10
+ .whereNotIn('id', options.excludeRoles ?? []);
11
+ const adminRoles = new Set(options.adminRoles);
12
+ const appRoles = new Set(options.appRoles);
13
+ const remainingRoles = new Set(allChildRoles);
14
+ let hasChanged = remainingRoles.size > 0;
15
+ // This loop accounts for the undefined order in which the roles are returned, as there is the possibility
16
+ // of a role parent not being in the set of roles yet, so we need to iterate over the roles multiple times
17
+ // until no further roles are added to the sets
18
+ while (hasChanged) {
19
+ hasChanged = false;
20
+ for (const role of remainingRoles) {
21
+ if (adminRoles.has(role.parent)) {
22
+ adminRoles.add(role.id);
23
+ remainingRoles.delete(role);
24
+ hasChanged = true;
25
+ }
26
+ if (appRoles.has(role.parent)) {
27
+ appRoles.add(role.id);
28
+ remainingRoles.delete(role);
29
+ hasChanged = true;
30
+ }
31
+ }
32
+ }
33
+ return {
34
+ adminRoles,
35
+ appRoles,
36
+ };
37
+ }
@@ -0,0 +1,6 @@
1
+ import type { Knex } from 'knex';
2
+ export interface ActiveUser {
3
+ id: string;
4
+ role: string | null;
5
+ }
6
+ export declare function fetchActiveUsers(knex: Knex): Promise<ActiveUser[]>;
@@ -0,0 +1,3 @@
1
+ export async function fetchActiveUsers(knex) {
2
+ return await knex.select('id', 'role').from('directus_users').where('status', 'active');
3
+ }
@@ -0,0 +1,12 @@
1
+ import { type FetchAccessLookupOptions } from './fetch-access-lookup.js';
2
+ export interface FetchUserCountOptions extends FetchAccessLookupOptions {
3
+ }
4
+ export interface UserCount {
5
+ admin: number;
6
+ app: number;
7
+ api: number;
8
+ }
9
+ /**
10
+ * Returns counts of all active users in the system grouped by admin, app, and api access
11
+ */
12
+ export declare function fetchUserCount(options: FetchUserCountOptions): Promise<UserCount>;
@@ -0,0 +1,57 @@
1
+ import { toBoolean } from '@directus/utils';
2
+ import { fetchAccessLookup } from './fetch-access-lookup.js';
3
+ import { fetchAccessRoles } from './fetch-access-roles.js';
4
+ import { getUserCountQuery } from './get-user-count-query.js';
5
+ /**
6
+ * Returns counts of all active users in the system grouped by admin, app, and api access
7
+ */
8
+ export async function fetchUserCount(options) {
9
+ const accessRows = await fetchAccessLookup(options);
10
+ const adminRoles = new Set(accessRows.filter((row) => toBoolean(row.admin_access) && row.role !== null).map((row) => row.role));
11
+ const appRoles = new Set(accessRows
12
+ .filter((row) => !toBoolean(row.admin_access) && toBoolean(row.app_access) && row.role !== null)
13
+ .map((row) => row.role));
14
+ // All users that are directly granted rights through a connected policy
15
+ const adminUsers = new Set(accessRows.filter((row) => toBoolean(row.admin_access) && row.user !== null).map((row) => row.user));
16
+ // Some roles might be granted access rights through nesting, so determine all roles that grant admin or app access,
17
+ // including nested roles
18
+ const { adminRoles: allAdminRoles, appRoles: allAppRoles } = await fetchAccessRoles({
19
+ adminRoles,
20
+ appRoles,
21
+ ...options,
22
+ }, { knex: options.knex });
23
+ // All users that are granted admin rights through a role, but not directly
24
+ const adminCountQuery = getUserCountQuery(options.knex, {
25
+ includeRoles: Array.from(allAdminRoles),
26
+ excludeIds: [...adminUsers, ...(options.excludeUsers ?? [])],
27
+ });
28
+ if (options.adminOnly) {
29
+ // Shortcut for only counting admin users
30
+ const adminResult = await adminCountQuery;
31
+ return {
32
+ admin: Number(adminResult?.['count'] ?? 0) + adminUsers.size,
33
+ app: 0,
34
+ api: 0,
35
+ };
36
+ }
37
+ const appUsers = new Set(accessRows
38
+ .filter((row) => !toBoolean(row.admin_access) && toBoolean(row.app_access) && row.user !== null)
39
+ .map((row) => row.user));
40
+ // All users that are granted app rights through a role, but not directly, and that aren't admin users
41
+ const appCountQuery = getUserCountQuery(options.knex, {
42
+ includeRoles: Array.from(allAppRoles),
43
+ excludeRoles: Array.from(allAdminRoles),
44
+ excludeIds: [...appUsers, ...adminUsers, ...(options.excludeUsers ?? [])],
45
+ });
46
+ const allCountQuery = getUserCountQuery(options.knex, {
47
+ excludeIds: options.excludeUsers ?? [],
48
+ });
49
+ const [adminResult, appResult, allResult] = await Promise.all([adminCountQuery, appCountQuery, allCountQuery]);
50
+ const adminCount = Number(adminResult?.['count'] ?? 0) + adminUsers.size;
51
+ const appCount = Number(appResult?.['count'] ?? 0) + appUsers.size;
52
+ return {
53
+ admin: adminCount,
54
+ app: appCount,
55
+ api: Number(allResult?.['count'] ?? 0) - adminCount - appCount,
56
+ };
57
+ }
@@ -0,0 +1,20 @@
1
+ import type { PrimaryKey } from '@directus/types';
2
+ import type { Knex } from 'knex';
3
+ export interface GetUserCountOptions {
4
+ excludeIds?: PrimaryKey[];
5
+ excludeRoles?: PrimaryKey[];
6
+ includeRoles?: PrimaryKey[];
7
+ }
8
+ export declare function getUserCountQuery(knex: Knex, options: GetUserCountOptions): Promise<{
9
+ count: number;
10
+ }> | Knex.QueryBuilder<any, {
11
+ _base: {};
12
+ _hasSelection: true;
13
+ _keys: never;
14
+ _aliases: {};
15
+ _single: false;
16
+ _intersectProps: {
17
+ count?: string | number;
18
+ };
19
+ _unionProps: undefined;
20
+ }>;
@@ -0,0 +1,17 @@
1
+ export function getUserCountQuery(knex, options) {
2
+ // Safety check for an empty list of includeRoles, which would otherwise return all users
3
+ if (options.includeRoles && options.includeRoles.length === 0) {
4
+ return Promise.resolve({ count: 0 });
5
+ }
6
+ let query = knex('directus_users').count({ count: '*' }).as('count').where('status', 'active');
7
+ if (options.excludeIds && options.excludeIds.length > 0) {
8
+ query = query.whereNotIn('id', options.excludeIds);
9
+ }
10
+ if (options.excludeRoles && options.excludeRoles.length > 0) {
11
+ query = query.whereNotIn('role', options.excludeRoles);
12
+ }
13
+ if (options.includeRoles && options.includeRoles.length > 0) {
14
+ query = query.whereIn('role', options.includeRoles);
15
+ }
16
+ return query.first();
17
+ }
@@ -1,40 +1,31 @@
1
- import { getPermissions } from './get-permissions.js';
1
+ import { fetchRolesTree } from '../permissions/lib/fetch-roles-tree.js';
2
+ import { fetchGlobalAccess } from '../permissions/modules/fetch-global-access/fetch-global-access.js';
3
+ import { createDefaultAccountability } from '../permissions/utils/create-default-accountability.js';
2
4
  export async function getAccountabilityForRole(role, context) {
3
- let generatedAccountability = context.accountability;
5
+ let generatedAccountability;
4
6
  if (role === null) {
5
- generatedAccountability = {
6
- role: null,
7
- user: null,
8
- admin: false,
9
- app: false,
10
- };
11
- generatedAccountability.permissions = await getPermissions(generatedAccountability, context.schema);
7
+ generatedAccountability = createDefaultAccountability();
12
8
  }
13
9
  else if (role === 'system') {
14
- generatedAccountability = {
15
- user: null,
16
- role: null,
10
+ generatedAccountability = createDefaultAccountability({
17
11
  admin: true,
18
12
  app: true,
19
- permissions: [],
20
- };
13
+ });
21
14
  }
22
15
  else {
23
- const roleInfo = await context.database
24
- .select(['app_access', 'admin_access'])
25
- .from('directus_roles')
26
- .where({ id: role })
27
- .first();
28
- if (!roleInfo) {
16
+ const roles = await fetchRolesTree(role, context.database);
17
+ // The roles tree should always include the passed role. If it doesn't, it's because it
18
+ // couldn't be read from the database and therefore doesn't exist
19
+ if (roles.length === 0) {
29
20
  throw new Error(`Configured role "${role}" isn't a valid role ID or doesn't exist.`);
30
21
  }
31
- generatedAccountability = {
22
+ const globalAccess = await fetchGlobalAccess({ user: null, roles, ip: context.accountability?.ip ?? null }, context.database);
23
+ generatedAccountability = createDefaultAccountability({
32
24
  role,
25
+ roles,
33
26
  user: null,
34
- admin: roleInfo.admin_access === 1 || roleInfo.admin_access === '1' || roleInfo.admin_access === true,
35
- app: roleInfo.app_access === 1 || roleInfo.app_access === '1' || roleInfo.app_access === true,
36
- };
37
- generatedAccountability.permissions = await getPermissions(generatedAccountability, context.schema);
27
+ ...globalAccess,
28
+ });
38
29
  }
39
30
  return generatedAccountability;
40
31
  }