@directus/api 21.0.0 → 22.1.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 (299) hide show
  1. package/dist/app.js +4 -4
  2. package/dist/auth/drivers/ldap.js +4 -4
  3. package/dist/auth/drivers/local.js +4 -4
  4. package/dist/auth/drivers/oauth2.js +4 -4
  5. package/dist/auth/drivers/openid.js +2 -4
  6. package/dist/cache.js +3 -0
  7. package/dist/cli/commands/bootstrap/index.js +8 -2
  8. package/dist/cli/commands/init/index.js +9 -10
  9. package/dist/cli/commands/init/questions.d.ts +7 -6
  10. package/dist/cli/commands/init/questions.js +2 -2
  11. package/dist/cli/utils/create-env/index.d.ts +2 -2
  12. package/dist/cli/utils/create-env/index.js +3 -1
  13. package/dist/cli/utils/defaults.d.ts +4 -11
  14. package/dist/cli/utils/defaults.js +7 -1
  15. package/dist/cli/utils/drivers.js +1 -1
  16. package/dist/constants.d.ts +1 -1
  17. package/dist/controllers/access.d.ts +2 -0
  18. package/dist/controllers/access.js +148 -0
  19. package/dist/controllers/auth.js +5 -16
  20. package/dist/controllers/permissions.js +14 -2
  21. package/dist/controllers/policies.d.ts +2 -0
  22. package/dist/controllers/policies.js +169 -0
  23. package/dist/controllers/roles.js +22 -1
  24. package/dist/controllers/tus.js +14 -26
  25. package/dist/controllers/users.js +0 -55
  26. package/dist/database/get-ast-from-query/get-ast-from-query.d.ts +16 -0
  27. package/dist/database/get-ast-from-query/get-ast-from-query.js +82 -0
  28. package/dist/database/get-ast-from-query/lib/convert-wildcards.d.ts +13 -0
  29. package/dist/database/get-ast-from-query/lib/convert-wildcards.js +69 -0
  30. package/dist/database/get-ast-from-query/lib/parse-fields.d.ts +15 -0
  31. package/dist/database/get-ast-from-query/lib/parse-fields.js +200 -0
  32. package/dist/database/get-ast-from-query/utils/get-deep-query.d.ts +14 -0
  33. package/dist/database/get-ast-from-query/utils/get-deep-query.js +17 -0
  34. package/dist/database/get-ast-from-query/utils/get-related-collection.d.ts +2 -0
  35. package/dist/database/get-ast-from-query/utils/get-related-collection.js +13 -0
  36. package/dist/database/get-ast-from-query/utils/get-relation.d.ts +2 -0
  37. package/dist/database/get-ast-from-query/utils/get-relation.js +7 -0
  38. package/dist/database/helpers/fn/types.d.ts +2 -1
  39. package/dist/database/helpers/fn/types.js +1 -1
  40. package/dist/database/helpers/geometry/dialects/mssql.d.ts +1 -1
  41. package/dist/database/helpers/geometry/dialects/mssql.js +4 -2
  42. package/dist/database/helpers/geometry/dialects/mysql.js +1 -1
  43. package/dist/database/helpers/geometry/dialects/oracle.d.ts +1 -1
  44. package/dist/database/helpers/geometry/dialects/oracle.js +5 -3
  45. package/dist/database/helpers/geometry/types.d.ts +1 -1
  46. package/dist/database/helpers/geometry/types.js +4 -2
  47. package/dist/database/helpers/index.d.ts +3 -3
  48. package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +2 -1
  49. package/dist/database/helpers/schema/dialects/cockroachdb.js +4 -0
  50. package/dist/database/helpers/schema/dialects/mssql.d.ts +2 -1
  51. package/dist/database/helpers/schema/dialects/mssql.js +4 -0
  52. package/dist/database/helpers/schema/dialects/oracle.d.ts +2 -1
  53. package/dist/database/helpers/schema/dialects/oracle.js +4 -0
  54. package/dist/database/helpers/schema/dialects/postgres.d.ts +2 -1
  55. package/dist/database/helpers/schema/dialects/postgres.js +4 -0
  56. package/dist/database/helpers/schema/types.d.ts +5 -0
  57. package/dist/database/helpers/schema/types.js +3 -0
  58. package/dist/database/helpers/schema/utils/preprocess-bindings.d.ts +8 -0
  59. package/dist/database/helpers/schema/utils/preprocess-bindings.js +30 -0
  60. package/dist/database/index.js +6 -1
  61. package/dist/database/migrations/20210519A-add-system-fk-triggers.js +3 -2
  62. package/dist/database/migrations/20230721A-require-shares-fields.js +3 -5
  63. package/dist/database/migrations/20240716A-update-files-date-fields.js +3 -7
  64. package/dist/{utils/merge-permissions.d.ts → database/migrations/20240806A-permissions-policies.d.ts} +4 -1
  65. package/dist/database/migrations/20240806A-permissions-policies.js +352 -0
  66. package/dist/database/run-ast/lib/get-db-query.d.ts +4 -0
  67. package/dist/database/run-ast/lib/get-db-query.js +218 -0
  68. package/dist/database/run-ast/lib/parse-current-level.d.ts +7 -0
  69. package/dist/database/run-ast/lib/parse-current-level.js +41 -0
  70. package/dist/database/run-ast/run-ast.d.ts +7 -0
  71. package/dist/database/run-ast/run-ast.js +107 -0
  72. package/dist/database/{run-ast.d.ts → run-ast/types.d.ts} +3 -9
  73. package/dist/database/run-ast/types.js +1 -0
  74. package/dist/database/run-ast/utils/apply-case-when.d.ts +16 -0
  75. package/dist/database/run-ast/utils/apply-case-when.js +27 -0
  76. package/dist/database/run-ast/utils/apply-parent-filters.d.ts +3 -0
  77. package/dist/database/run-ast/utils/apply-parent-filters.js +55 -0
  78. package/dist/database/run-ast/utils/get-column-pre-processor.d.ts +10 -0
  79. package/dist/database/run-ast/utils/get-column-pre-processor.js +57 -0
  80. package/dist/database/run-ast/utils/get-field-alias.d.ts +2 -0
  81. package/dist/database/run-ast/utils/get-field-alias.js +4 -0
  82. package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.d.ts +5 -0
  83. package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.js +23 -0
  84. package/dist/database/run-ast/utils/merge-with-parent-items.d.ts +3 -0
  85. package/dist/database/run-ast/utils/merge-with-parent-items.js +87 -0
  86. package/dist/database/run-ast/utils/remove-temporary-fields.d.ts +3 -0
  87. package/dist/database/run-ast/utils/remove-temporary-fields.js +73 -0
  88. package/dist/database/run-ast/utils/with-preprocess-bindings.d.ts +2 -0
  89. package/dist/database/run-ast/utils/with-preprocess-bindings.js +14 -0
  90. package/dist/flows.js +3 -4
  91. package/dist/middleware/authenticate.js +2 -7
  92. package/dist/middleware/cache.js +1 -1
  93. package/dist/middleware/respond.js +1 -1
  94. package/dist/permissions/cache.d.ts +2 -0
  95. package/dist/permissions/cache.js +23 -0
  96. package/dist/permissions/lib/fetch-permissions.d.ts +11 -0
  97. package/dist/permissions/lib/fetch-permissions.js +56 -0
  98. package/dist/permissions/lib/fetch-policies.d.ts +14 -0
  99. package/dist/permissions/lib/fetch-policies.js +43 -0
  100. package/dist/permissions/lib/fetch-roles-tree.d.ts +3 -0
  101. package/dist/permissions/lib/fetch-roles-tree.js +28 -0
  102. package/dist/{services/permissions → permissions}/lib/with-app-minimal-permissions.d.ts +1 -1
  103. package/dist/permissions/lib/with-app-minimal-permissions.js +10 -0
  104. package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.d.ts +7 -0
  105. package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js +56 -0
  106. package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.d.ts +3 -0
  107. package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js +16 -0
  108. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.d.ts +8 -0
  109. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +24 -0
  110. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.d.ts +9 -0
  111. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +31 -0
  112. package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.d.ts +16 -0
  113. package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js +27 -0
  114. package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +10 -0
  115. package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +23 -0
  116. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +5 -0
  117. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +7 -0
  118. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +5 -0
  119. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +10 -0
  120. package/dist/permissions/modules/fetch-global-access/types.d.ts +4 -0
  121. package/dist/permissions/modules/fetch-global-access/types.js +1 -0
  122. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +4 -0
  123. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +27 -0
  124. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.d.ts +12 -0
  125. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +32 -0
  126. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +4 -0
  127. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.js +29 -0
  128. package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.d.ts +4 -0
  129. package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.js +49 -0
  130. package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.d.ts +3 -0
  131. package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.js +56 -0
  132. package/dist/permissions/modules/process-ast/lib/field-map-from-ast.d.ts +4 -0
  133. package/dist/permissions/modules/process-ast/lib/field-map-from-ast.js +8 -0
  134. package/dist/permissions/modules/process-ast/lib/inject-cases.d.ts +9 -0
  135. package/dist/permissions/modules/process-ast/lib/inject-cases.js +93 -0
  136. package/dist/permissions/modules/process-ast/process-ast.d.ts +9 -0
  137. package/dist/permissions/modules/process-ast/process-ast.js +39 -0
  138. package/dist/permissions/modules/process-ast/types.d.ts +18 -0
  139. package/dist/permissions/modules/process-ast/types.js +1 -0
  140. package/dist/permissions/modules/process-ast/utils/collections-in-field-map.d.ts +2 -0
  141. package/dist/permissions/modules/process-ast/utils/collections-in-field-map.js +7 -0
  142. package/dist/permissions/modules/process-ast/utils/dedupe-access.d.ts +12 -0
  143. package/dist/permissions/modules/process-ast/utils/dedupe-access.js +30 -0
  144. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.d.ts +15 -0
  145. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +60 -0
  146. package/dist/permissions/modules/process-ast/utils/find-related-collection.d.ts +3 -0
  147. package/dist/permissions/modules/process-ast/utils/find-related-collection.js +9 -0
  148. package/dist/permissions/modules/process-ast/utils/flatten-filter.d.ts +3 -0
  149. package/dist/permissions/modules/process-ast/utils/flatten-filter.js +34 -0
  150. package/dist/permissions/modules/process-ast/utils/format-a2o-key.d.ts +1 -0
  151. package/dist/permissions/modules/process-ast/utils/format-a2o-key.js +3 -0
  152. package/dist/permissions/modules/process-ast/utils/get-info-for-path.d.ts +5 -0
  153. package/dist/permissions/modules/process-ast/utils/get-info-for-path.js +7 -0
  154. package/dist/permissions/modules/process-ast/utils/has-item-permissions.d.ts +2 -0
  155. package/dist/permissions/modules/process-ast/utils/has-item-permissions.js +3 -0
  156. package/dist/permissions/modules/process-ast/utils/stringify-query-path.d.ts +2 -0
  157. package/dist/permissions/modules/process-ast/utils/stringify-query-path.js +3 -0
  158. package/dist/permissions/modules/process-ast/utils/validate-path/create-error.d.ts +3 -0
  159. package/dist/permissions/modules/process-ast/utils/validate-path/create-error.js +16 -0
  160. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.d.ts +2 -0
  161. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.js +12 -0
  162. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.d.ts +2 -0
  163. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.js +28 -0
  164. package/dist/permissions/modules/process-payload/lib/is-field-nullable.d.ts +5 -0
  165. package/dist/permissions/modules/process-payload/lib/is-field-nullable.js +12 -0
  166. package/dist/permissions/modules/process-payload/process-payload.d.ts +13 -0
  167. package/dist/permissions/modules/process-payload/process-payload.js +77 -0
  168. package/dist/permissions/modules/validate-access/lib/validate-collection-access.d.ts +12 -0
  169. package/dist/permissions/modules/validate-access/lib/validate-collection-access.js +11 -0
  170. package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +9 -0
  171. package/dist/permissions/modules/validate-access/lib/validate-item-access.js +33 -0
  172. package/dist/permissions/modules/validate-access/validate-access.d.ts +14 -0
  173. package/dist/permissions/modules/validate-access/validate-access.js +28 -0
  174. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.d.ts +1 -0
  175. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.js +8 -0
  176. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.d.ts +5 -0
  177. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.js +10 -0
  178. package/dist/permissions/types.d.ts +6 -0
  179. package/dist/permissions/types.js +1 -0
  180. package/dist/permissions/utils/create-default-accountability.d.ts +2 -0
  181. package/dist/permissions/utils/create-default-accountability.js +11 -0
  182. package/dist/permissions/utils/extract-required-dynamic-variable-context.d.ts +8 -0
  183. package/dist/permissions/utils/extract-required-dynamic-variable-context.js +27 -0
  184. package/dist/permissions/utils/fetch-dynamic-variable-context.d.ts +9 -0
  185. package/dist/permissions/utils/fetch-dynamic-variable-context.js +43 -0
  186. package/dist/permissions/utils/filter-policies-by-ip.d.ts +2 -0
  187. package/dist/permissions/utils/filter-policies-by-ip.js +15 -0
  188. package/dist/permissions/utils/get-unaliased-field-key.d.ts +5 -0
  189. package/dist/permissions/utils/get-unaliased-field-key.js +17 -0
  190. package/dist/permissions/utils/process-permissions.d.ts +7 -0
  191. package/dist/permissions/utils/process-permissions.js +9 -0
  192. package/dist/permissions/utils/with-cache.d.ts +10 -0
  193. package/dist/permissions/utils/with-cache.js +25 -0
  194. package/dist/server.js +17 -4
  195. package/dist/services/access.d.ts +10 -0
  196. package/dist/services/access.js +43 -0
  197. package/dist/services/activity.js +22 -10
  198. package/dist/services/assets.d.ts +2 -3
  199. package/dist/services/assets.js +10 -5
  200. package/dist/services/authentication.js +18 -18
  201. package/dist/services/collections.js +18 -17
  202. package/dist/services/fields.d.ts +0 -1
  203. package/dist/services/fields.js +54 -25
  204. package/dist/services/files.js +10 -3
  205. package/dist/services/graphql/index.d.ts +3 -3
  206. package/dist/services/graphql/index.js +126 -22
  207. package/dist/services/graphql/subscription.js +2 -4
  208. package/dist/services/import-export.d.ts +3 -1
  209. package/dist/services/import-export.js +67 -9
  210. package/dist/services/index.d.ts +3 -2
  211. package/dist/services/index.js +3 -2
  212. package/dist/services/items.js +115 -44
  213. package/dist/services/meta.js +60 -23
  214. package/dist/services/notifications.js +14 -6
  215. package/dist/services/payload.d.ts +9 -10
  216. package/dist/services/payload.js +18 -3
  217. package/dist/services/{permissions/index.d.ts → permissions.d.ts} +5 -7
  218. package/dist/services/{permissions/index.js → permissions.js} +30 -54
  219. package/dist/services/policies.d.ts +12 -0
  220. package/dist/services/policies.js +87 -0
  221. package/dist/services/relations.d.ts +0 -6
  222. package/dist/services/relations.js +27 -30
  223. package/dist/services/roles.d.ts +4 -12
  224. package/dist/services/roles.js +57 -424
  225. package/dist/services/shares.d.ts +0 -2
  226. package/dist/services/shares.js +12 -8
  227. package/dist/services/specifications.d.ts +2 -2
  228. package/dist/services/specifications.js +39 -27
  229. package/dist/services/users.d.ts +1 -5
  230. package/dist/services/users.js +78 -161
  231. package/dist/services/utils.js +11 -7
  232. package/dist/services/versions.d.ts +0 -2
  233. package/dist/services/versions.js +34 -10
  234. package/dist/telemetry/lib/get-report.js +2 -2
  235. package/dist/telemetry/utils/check-user-limits.d.ts +5 -0
  236. package/dist/telemetry/utils/check-user-limits.js +19 -0
  237. package/dist/types/ast.d.ts +43 -1
  238. package/dist/types/database.d.ts +1 -1
  239. package/dist/types/items.d.ts +11 -0
  240. package/dist/utils/apply-query.d.ts +11 -7
  241. package/dist/utils/apply-query.js +69 -11
  242. package/dist/utils/fetch-user-count/fetch-access-lookup.d.ts +19 -0
  243. package/dist/utils/fetch-user-count/fetch-access-lookup.js +23 -0
  244. package/dist/utils/fetch-user-count/fetch-access-roles.d.ts +16 -0
  245. package/dist/utils/fetch-user-count/fetch-access-roles.js +37 -0
  246. package/dist/utils/fetch-user-count/fetch-active-users.d.ts +6 -0
  247. package/dist/utils/fetch-user-count/fetch-active-users.js +3 -0
  248. package/dist/utils/fetch-user-count/fetch-user-count.d.ts +12 -0
  249. package/dist/utils/fetch-user-count/fetch-user-count.js +64 -0
  250. package/dist/utils/fetch-user-count/get-user-count-query.d.ts +20 -0
  251. package/dist/utils/fetch-user-count/get-user-count-query.js +17 -0
  252. package/dist/utils/get-accountability-for-role.js +16 -25
  253. package/dist/utils/get-accountability-for-token.js +17 -16
  254. package/dist/utils/get-address.d.ts +5 -0
  255. package/dist/utils/get-address.js +13 -0
  256. package/dist/utils/get-cache-key.d.ts +1 -1
  257. package/dist/utils/get-cache-key.js +12 -1
  258. package/dist/utils/get-column.d.ts +2 -1
  259. package/dist/utils/get-column.js +1 -0
  260. package/dist/utils/get-service.js +5 -1
  261. package/dist/utils/reduce-schema.d.ts +4 -6
  262. package/dist/utils/reduce-schema.js +16 -32
  263. package/dist/utils/sanitize-schema.d.ts +1 -1
  264. package/dist/utils/transaction.js +28 -11
  265. package/dist/utils/validate-user-count-integrity.d.ts +13 -0
  266. package/dist/utils/validate-user-count-integrity.js +29 -0
  267. package/dist/websocket/authenticate.d.ts +0 -2
  268. package/dist/websocket/authenticate.js +0 -12
  269. package/dist/websocket/controllers/graphql.js +3 -7
  270. package/dist/websocket/controllers/hooks.js +4 -0
  271. package/dist/websocket/controllers/rest.js +2 -5
  272. package/dist/websocket/handlers/subscribe.js +0 -2
  273. package/dist/websocket/utils/items.d.ts +1 -1
  274. package/package.json +31 -30
  275. package/dist/database/run-ast.js +0 -458
  276. package/dist/middleware/check-ip.d.ts +0 -2
  277. package/dist/middleware/check-ip.js +0 -37
  278. package/dist/middleware/get-permissions.d.ts +0 -3
  279. package/dist/middleware/get-permissions.js +0 -10
  280. package/dist/services/authorization.d.ts +0 -17
  281. package/dist/services/authorization.js +0 -456
  282. package/dist/services/permissions/lib/with-app-minimal-permissions.js +0 -13
  283. package/dist/telemetry/utils/check-increased-user-limits.d.ts +0 -7
  284. package/dist/telemetry/utils/check-increased-user-limits.js +0 -25
  285. package/dist/telemetry/utils/get-role-counts-by-roles.d.ts +0 -6
  286. package/dist/telemetry/utils/get-role-counts-by-roles.js +0 -27
  287. package/dist/telemetry/utils/get-role-counts-by-users.d.ts +0 -11
  288. package/dist/telemetry/utils/get-role-counts-by-users.js +0 -34
  289. package/dist/telemetry/utils/get-user-count.d.ts +0 -8
  290. package/dist/telemetry/utils/get-user-count.js +0 -33
  291. package/dist/telemetry/utils/get-user-counts-by-roles.d.ts +0 -7
  292. package/dist/telemetry/utils/get-user-counts-by-roles.js +0 -35
  293. package/dist/utils/get-ast-from-query.d.ts +0 -13
  294. package/dist/utils/get-ast-from-query.js +0 -297
  295. package/dist/utils/get-permissions.d.ts +0 -2
  296. package/dist/utils/get-permissions.js +0 -150
  297. package/dist/utils/merge-permissions-for-share.d.ts +0 -4
  298. package/dist/utils/merge-permissions-for-share.js +0 -109
  299. package/dist/utils/merge-permissions.js +0 -95
@@ -1,458 +0,0 @@
1
- import { useEnv } from '@directus/env';
2
- import { toArray } from '@directus/utils';
3
- import { clone, cloneDeep, isNil, merge, pick, uniq } from 'lodash-es';
4
- import { PayloadService } from '../services/payload.js';
5
- import { applyFunctionToColumnName } from '../utils/apply-function-to-column-name.js';
6
- import applyQuery, { applyLimit, applySort, generateAlias } from '../utils/apply-query.js';
7
- import { getCollectionFromAlias } from '../utils/get-collection-from-alias.js';
8
- import { getColumn } from '../utils/get-column.js';
9
- import { parseFilterKey } from '../utils/parse-filter-key.js';
10
- import { getHelpers } from './helpers/index.js';
11
- import getDatabase from './index.js';
12
- /**
13
- * Execute a given AST using Knex. Returns array of items based on requested AST.
14
- */
15
- export default async function runAST(originalAST, schema, options) {
16
- const ast = cloneDeep(originalAST);
17
- const knex = options?.knex || getDatabase();
18
- if (ast.type === 'a2o') {
19
- const results = {};
20
- for (const collection of ast.names) {
21
- results[collection] = await run(collection, ast.children[collection], ast.query[collection]);
22
- }
23
- return results;
24
- }
25
- else {
26
- return await run(ast.name, ast.children, options?.query || ast.query);
27
- }
28
- async function run(collection, children, query) {
29
- const env = useEnv();
30
- // Retrieve the database columns to select in the current AST
31
- const { fieldNodes, primaryKeyField, nestedCollectionNodes } = await parseCurrentLevel(schema, collection, children, query);
32
- // The actual knex query builder instance. This is a promise that resolves with the raw items from the db
33
- const dbQuery = await getDBQuery(schema, knex, collection, fieldNodes, query);
34
- const rawItems = await dbQuery;
35
- if (!rawItems)
36
- return null;
37
- // Run the items through the special transforms
38
- const payloadService = new PayloadService(collection, { knex, schema });
39
- let items = await payloadService.processValues('read', rawItems, query.alias ?? {});
40
- if (!items || (Array.isArray(items) && items.length === 0))
41
- return items;
42
- // Apply the `_in` filters to the nested collection batches
43
- const nestedNodes = applyParentFilters(schema, nestedCollectionNodes, items);
44
- for (const nestedNode of nestedNodes) {
45
- let nestedItems = [];
46
- if (nestedNode.type === 'o2m') {
47
- let hasMore = true;
48
- let batchCount = 0;
49
- while (hasMore) {
50
- const node = merge({}, nestedNode, {
51
- query: {
52
- limit: env['RELATIONAL_BATCH_SIZE'],
53
- offset: batchCount * env['RELATIONAL_BATCH_SIZE'],
54
- page: null,
55
- },
56
- });
57
- nestedItems = (await runAST(node, schema, { knex, nested: true }));
58
- if (nestedItems) {
59
- items = mergeWithParentItems(schema, nestedItems, items, nestedNode);
60
- }
61
- if (!nestedItems || nestedItems.length < env['RELATIONAL_BATCH_SIZE']) {
62
- hasMore = false;
63
- }
64
- batchCount++;
65
- }
66
- }
67
- else {
68
- const node = merge({}, nestedNode, {
69
- query: { limit: -1 },
70
- });
71
- nestedItems = (await runAST(node, schema, { knex, nested: true }));
72
- if (nestedItems) {
73
- // Merge all fetched nested records with the parent items
74
- items = mergeWithParentItems(schema, nestedItems, items, nestedNode);
75
- }
76
- }
77
- }
78
- // During the fetching of data, we have to inject a couple of required fields for the child nesting
79
- // to work (primary / foreign keys) even if they're not explicitly requested. After all fetching
80
- // and nesting is done, we parse through the output structure, and filter out all non-requested
81
- // fields
82
- if (options?.nested !== true && options?.stripNonRequested !== false) {
83
- items = removeTemporaryFields(schema, items, originalAST, primaryKeyField);
84
- }
85
- return items;
86
- }
87
- }
88
- async function parseCurrentLevel(schema, collection, children, query) {
89
- const primaryKeyField = schema.collections[collection].primary;
90
- const columnsInCollection = Object.keys(schema.collections[collection].fields);
91
- const columnsToSelectInternal = [];
92
- const nestedCollectionNodes = [];
93
- for (const child of children) {
94
- if (child.type === 'field' || child.type === 'functionField') {
95
- const { fieldName } = parseFilterKey(child.name);
96
- if (columnsInCollection.includes(fieldName)) {
97
- columnsToSelectInternal.push(child.fieldKey);
98
- }
99
- continue;
100
- }
101
- if (!child.relation)
102
- continue;
103
- if (child.type === 'm2o') {
104
- columnsToSelectInternal.push(child.relation.field);
105
- }
106
- if (child.type === 'a2o') {
107
- columnsToSelectInternal.push(child.relation.field);
108
- columnsToSelectInternal.push(child.relation.meta.one_collection_field);
109
- }
110
- nestedCollectionNodes.push(child);
111
- }
112
- const isAggregate = (query.group || (query.aggregate && Object.keys(query.aggregate).length > 0)) ?? false;
113
- /** Always fetch primary key in case there's a nested relation that needs it. Aggregate payloads
114
- * can't have nested relational fields
115
- */
116
- if (isAggregate === false && columnsToSelectInternal.includes(primaryKeyField) === false) {
117
- columnsToSelectInternal.push(primaryKeyField);
118
- }
119
- /** Make sure select list has unique values */
120
- const columnsToSelect = [...new Set(columnsToSelectInternal)];
121
- const fieldNodes = columnsToSelect.map((column) => children.find((childNode) => (childNode.type === 'field' || childNode.type === 'functionField') && childNode.fieldKey === column) ?? {
122
- type: 'field',
123
- name: column,
124
- fieldKey: column,
125
- });
126
- return { fieldNodes, nestedCollectionNodes, primaryKeyField };
127
- }
128
- function getColumnPreprocessor(knex, schema, table) {
129
- const helpers = getHelpers(knex);
130
- return function (fieldNode) {
131
- let alias = undefined;
132
- if (fieldNode.name !== fieldNode.fieldKey) {
133
- alias = fieldNode.fieldKey;
134
- }
135
- let field;
136
- if (fieldNode.type === 'field' || fieldNode.type === 'functionField') {
137
- const { fieldName } = parseFilterKey(fieldNode.name);
138
- field = schema.collections[table].fields[fieldName];
139
- }
140
- else {
141
- field = schema.collections[fieldNode.relation.collection].fields[fieldNode.relation.field];
142
- }
143
- if (field?.type?.startsWith('geometry')) {
144
- return helpers.st.asText(table, field.field);
145
- }
146
- if (fieldNode.type === 'functionField') {
147
- return getColumn(knex, table, fieldNode.name, alias, schema, { query: fieldNode.query });
148
- }
149
- return getColumn(knex, table, fieldNode.name, alias, schema);
150
- };
151
- }
152
- async function getDBQuery(schema, knex, table, fieldNodes, query) {
153
- const env = useEnv();
154
- const preProcess = getColumnPreprocessor(knex, schema, table);
155
- const queryCopy = clone(query);
156
- const helpers = getHelpers(knex);
157
- queryCopy.limit = typeof queryCopy.limit === 'number' ? queryCopy.limit : Number(env['QUERY_LIMIT_DEFAULT']);
158
- // Queries with aggregates and groupBy will not have duplicate result
159
- if (queryCopy.aggregate || queryCopy.group) {
160
- const flatQuery = knex.select(fieldNodes.map(preProcess)).from(table);
161
- return await applyQuery(knex, table, flatQuery, queryCopy, schema).query;
162
- }
163
- const primaryKey = schema.collections[table].primary;
164
- const aliasMap = Object.create(null);
165
- let dbQuery = knex.from(table);
166
- let sortRecords;
167
- const innerQuerySortRecords = [];
168
- let hasMultiRelationalSort;
169
- if (queryCopy.sort) {
170
- const sortResult = applySort(knex, schema, dbQuery, queryCopy, table, aliasMap, true);
171
- if (sortResult) {
172
- sortRecords = sortResult.sortRecords;
173
- hasMultiRelationalSort = sortResult.hasMultiRelationalSort;
174
- }
175
- }
176
- const { hasMultiRelationalFilter } = applyQuery(knex, table, dbQuery, queryCopy, schema, {
177
- aliasMap,
178
- isInnerQuery: true,
179
- hasMultiRelationalSort,
180
- });
181
- const needsInnerQuery = hasMultiRelationalSort || hasMultiRelationalFilter;
182
- if (needsInnerQuery) {
183
- dbQuery.select(`${table}.${primaryKey}`).distinct();
184
- }
185
- else {
186
- dbQuery.select(fieldNodes.map(preProcess));
187
- }
188
- if (sortRecords) {
189
- // Clears the order if any, eg: from MSSQL offset
190
- dbQuery.clear('order');
191
- if (needsInnerQuery) {
192
- let orderByString = '';
193
- const orderByFields = [];
194
- sortRecords.map((sortRecord) => {
195
- if (orderByString.length !== 0) {
196
- orderByString += ', ';
197
- }
198
- const sortAlias = `sort_${generateAlias()}`;
199
- if (sortRecord.column.includes('.')) {
200
- const [alias, field] = sortRecord.column.split('.');
201
- const originalCollectionName = getCollectionFromAlias(alias, aliasMap);
202
- dbQuery.select(getColumn(knex, alias, field, sortAlias, schema, { originalCollectionName }));
203
- orderByString += `?? ${sortRecord.order}`;
204
- orderByFields.push(getColumn(knex, alias, field, false, schema, { originalCollectionName }));
205
- }
206
- else {
207
- dbQuery.select(getColumn(knex, table, sortRecord.column, sortAlias, schema));
208
- orderByString += `?? ${sortRecord.order}`;
209
- orderByFields.push(getColumn(knex, table, sortRecord.column, false, schema));
210
- }
211
- innerQuerySortRecords.push({ alias: sortAlias, order: sortRecord.order });
212
- });
213
- if (hasMultiRelationalSort) {
214
- dbQuery = helpers.schema.applyMultiRelationalSort(knex, dbQuery, table, primaryKey, orderByString, orderByFields);
215
- // Start order by with directus_row_number. The directus_row_number is derived from a window function that
216
- // is ordered by the sort fields within every primary key partition. That ensures that the result with the
217
- // row number = 1 is the top-most row of every partition, according to the selected sort fields.
218
- // Since the only relevant result is the first row of this partition, adding the directus_row_number to the
219
- // order by here ensures that all rows with a directus_row_number = 1 show up first in the inner query result,
220
- // and are correctly truncated by the limit, but not earlier.
221
- orderByString = `?? asc, ${orderByString}`;
222
- orderByFields.unshift(knex.ref('directus_row_number'));
223
- }
224
- dbQuery.orderByRaw(orderByString, orderByFields);
225
- }
226
- else {
227
- sortRecords.map((sortRecord) => {
228
- if (sortRecord.column.includes('.')) {
229
- const [alias, field] = sortRecord.column.split('.');
230
- sortRecord.column = getColumn(knex, alias, field, false, schema, {
231
- originalCollectionName: getCollectionFromAlias(alias, aliasMap),
232
- });
233
- }
234
- else {
235
- sortRecord.column = getColumn(knex, table, sortRecord.column, false, schema);
236
- }
237
- });
238
- dbQuery.orderBy(sortRecords);
239
- }
240
- }
241
- if (!needsInnerQuery)
242
- return dbQuery;
243
- const wrapperQuery = knex
244
- .select(fieldNodes.map(preProcess))
245
- .from(table)
246
- .innerJoin(knex.raw('??', dbQuery.as('inner')), `${table}.${primaryKey}`, `inner.${primaryKey}`);
247
- if (sortRecords) {
248
- innerQuerySortRecords.map((innerQuerySortRecord) => {
249
- wrapperQuery.orderBy(`inner.${innerQuerySortRecord.alias}`, innerQuerySortRecord.order);
250
- });
251
- if (hasMultiRelationalSort) {
252
- wrapperQuery.where('inner.directus_row_number', '=', 1);
253
- applyLimit(knex, wrapperQuery, queryCopy.limit);
254
- }
255
- }
256
- return wrapperQuery;
257
- }
258
- function applyParentFilters(schema, nestedCollectionNodes, parentItem) {
259
- const parentItems = toArray(parentItem);
260
- for (const nestedNode of nestedCollectionNodes) {
261
- if (!nestedNode.relation)
262
- continue;
263
- if (nestedNode.type === 'm2o') {
264
- const foreignField = schema.collections[nestedNode.relation.related_collection].primary;
265
- const foreignIds = uniq(parentItems.map((res) => res[nestedNode.relation.field])).filter((id) => !isNil(id));
266
- merge(nestedNode, { query: { filter: { [foreignField]: { _in: foreignIds } } } });
267
- }
268
- else if (nestedNode.type === 'o2m') {
269
- const relatedM2OisFetched = !!nestedNode.children.find((child) => {
270
- return child.type === 'field' && child.name === nestedNode.relation.field;
271
- });
272
- if (relatedM2OisFetched === false) {
273
- nestedNode.children.push({
274
- type: 'field',
275
- name: nestedNode.relation.field,
276
- fieldKey: nestedNode.relation.field,
277
- });
278
- }
279
- if (nestedNode.relation.meta?.sort_field) {
280
- nestedNode.children.push({
281
- type: 'field',
282
- name: nestedNode.relation.meta.sort_field,
283
- fieldKey: nestedNode.relation.meta.sort_field,
284
- });
285
- }
286
- const foreignField = nestedNode.relation.field;
287
- const foreignIds = uniq(parentItems.map((res) => res[nestedNode.parentKey])).filter((id) => !isNil(id));
288
- merge(nestedNode, { query: { filter: { [foreignField]: { _in: foreignIds } } } });
289
- }
290
- else if (nestedNode.type === 'a2o') {
291
- const keysPerCollection = {};
292
- for (const parentItem of parentItems) {
293
- const collection = parentItem[nestedNode.relation.meta.one_collection_field];
294
- if (!keysPerCollection[collection])
295
- keysPerCollection[collection] = [];
296
- keysPerCollection[collection].push(parentItem[nestedNode.relation.field]);
297
- }
298
- for (const relatedCollection of nestedNode.names) {
299
- const foreignField = nestedNode.relatedKey[relatedCollection];
300
- const foreignIds = uniq(keysPerCollection[relatedCollection]);
301
- merge(nestedNode, {
302
- query: { [relatedCollection]: { filter: { [foreignField]: { _in: foreignIds } }, limit: foreignIds.length } },
303
- });
304
- }
305
- }
306
- }
307
- return nestedCollectionNodes;
308
- }
309
- function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode) {
310
- const env = useEnv();
311
- const nestedItems = toArray(nestedItem);
312
- const parentItems = clone(toArray(parentItem));
313
- if (nestedNode.type === 'm2o') {
314
- for (const parentItem of parentItems) {
315
- const itemChild = nestedItems.find((nestedItem) => {
316
- return (nestedItem[schema.collections[nestedNode.relation.related_collection].primary] ==
317
- parentItem[nestedNode.relation.field]);
318
- });
319
- parentItem[nestedNode.fieldKey] = itemChild || null;
320
- }
321
- }
322
- else if (nestedNode.type === 'o2m') {
323
- for (const parentItem of parentItems) {
324
- if (!parentItem[nestedNode.fieldKey])
325
- parentItem[nestedNode.fieldKey] = [];
326
- const itemChildren = nestedItems.filter((nestedItem) => {
327
- if (nestedItem === null)
328
- return false;
329
- if (Array.isArray(nestedItem[nestedNode.relation.field]))
330
- return true;
331
- return (nestedItem[nestedNode.relation.field] ==
332
- parentItem[schema.collections[nestedNode.relation.related_collection].primary] ||
333
- nestedItem[nestedNode.relation.field]?.[schema.collections[nestedNode.relation.related_collection].primary] == parentItem[schema.collections[nestedNode.relation.related_collection].primary]);
334
- });
335
- parentItem[nestedNode.fieldKey].push(...itemChildren);
336
- const limit = nestedNode.query.limit ?? Number(env['QUERY_LIMIT_DEFAULT']);
337
- if (nestedNode.query.page && nestedNode.query.page > 1) {
338
- parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(limit * (nestedNode.query.page - 1));
339
- }
340
- if (nestedNode.query.offset && nestedNode.query.offset >= 0) {
341
- parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(nestedNode.query.offset);
342
- }
343
- if (limit !== -1) {
344
- parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(0, limit);
345
- }
346
- parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].sort((a, b) => {
347
- // This is pre-filled in get-ast-from-query
348
- const sortField = nestedNode.query.sort[0];
349
- let column = sortField;
350
- let order = 'asc';
351
- if (sortField.startsWith('-')) {
352
- column = sortField.substring(1);
353
- order = 'desc';
354
- }
355
- if (a[column] === b[column])
356
- return 0;
357
- if (a[column] === null)
358
- return 1;
359
- if (b[column] === null)
360
- return -1;
361
- if (order === 'asc') {
362
- return a[column] < b[column] ? -1 : 1;
363
- }
364
- else {
365
- return a[column] < b[column] ? 1 : -1;
366
- }
367
- });
368
- }
369
- }
370
- else if (nestedNode.type === 'a2o') {
371
- for (const parentItem of parentItems) {
372
- if (!nestedNode.relation.meta?.one_collection_field) {
373
- parentItem[nestedNode.fieldKey] = null;
374
- continue;
375
- }
376
- const relatedCollection = parentItem[nestedNode.relation.meta.one_collection_field];
377
- if (!nestedItem[relatedCollection]) {
378
- parentItem[nestedNode.fieldKey] = null;
379
- continue;
380
- }
381
- const itemChild = nestedItem[relatedCollection].find((nestedItem) => {
382
- return nestedItem[nestedNode.relatedKey[relatedCollection]] == parentItem[nestedNode.fieldKey];
383
- });
384
- parentItem[nestedNode.fieldKey] = itemChild || null;
385
- }
386
- }
387
- return Array.isArray(parentItem) ? parentItems : parentItems[0];
388
- }
389
- function removeTemporaryFields(schema, rawItem, ast, primaryKeyField, parentItem) {
390
- const rawItems = cloneDeep(toArray(rawItem));
391
- const items = [];
392
- if (ast.type === 'a2o') {
393
- const fields = {};
394
- const nestedCollectionNodes = {};
395
- for (const relatedCollection of ast.names) {
396
- if (!fields[relatedCollection])
397
- fields[relatedCollection] = [];
398
- if (!nestedCollectionNodes[relatedCollection])
399
- nestedCollectionNodes[relatedCollection] = [];
400
- for (const child of ast.children[relatedCollection]) {
401
- if (child.type === 'field' || child.type === 'functionField') {
402
- fields[relatedCollection].push(child.name);
403
- }
404
- else {
405
- fields[relatedCollection].push(child.fieldKey);
406
- nestedCollectionNodes[relatedCollection].push(child);
407
- }
408
- }
409
- }
410
- for (const rawItem of rawItems) {
411
- const relatedCollection = parentItem?.[ast.relation.meta.one_collection_field];
412
- if (rawItem === null || rawItem === undefined)
413
- return rawItem;
414
- let item = rawItem;
415
- for (const nestedNode of nestedCollectionNodes[relatedCollection]) {
416
- item[nestedNode.fieldKey] = removeTemporaryFields(schema, item[nestedNode.fieldKey], nestedNode, schema.collections[nestedNode.relation.collection].primary, item);
417
- }
418
- const fieldsWithFunctionsApplied = fields[relatedCollection].map((field) => applyFunctionToColumnName(field));
419
- item =
420
- fields[relatedCollection].length > 0 ? pick(rawItem, fieldsWithFunctionsApplied) : rawItem[primaryKeyField];
421
- items.push(item);
422
- }
423
- }
424
- else {
425
- const fields = [];
426
- const nestedCollectionNodes = [];
427
- for (const child of ast.children) {
428
- fields.push(child.fieldKey);
429
- if (child.type !== 'field' && child.type !== 'functionField') {
430
- nestedCollectionNodes.push(child);
431
- }
432
- }
433
- // Make sure any requested aggregate fields are included
434
- if (ast.query?.aggregate) {
435
- for (const [operation, aggregateFields] of Object.entries(ast.query.aggregate)) {
436
- if (!fields)
437
- continue;
438
- if (operation === 'count' && aggregateFields.includes('*'))
439
- fields.push('count');
440
- fields.push(...aggregateFields.map((field) => `${operation}.${field}`));
441
- }
442
- }
443
- for (const rawItem of rawItems) {
444
- if (rawItem === null || rawItem === undefined)
445
- return rawItem;
446
- let item = rawItem;
447
- for (const nestedNode of nestedCollectionNodes) {
448
- item[nestedNode.fieldKey] = removeTemporaryFields(schema, item[nestedNode.fieldKey], nestedNode, nestedNode.type === 'm2o'
449
- ? schema.collections[nestedNode.relation.related_collection].primary
450
- : schema.collections[nestedNode.relation.collection].primary, item);
451
- }
452
- const fieldsWithFunctionsApplied = fields.map((field) => applyFunctionToColumnName(field));
453
- item = fields.length > 0 ? pick(rawItem, fieldsWithFunctionsApplied) : rawItem[primaryKeyField];
454
- items.push(item);
455
- }
456
- }
457
- return Array.isArray(rawItem) ? items : items[0];
458
- }
@@ -1,2 +0,0 @@
1
- import type { RequestHandler } from 'express';
2
- export declare const checkIP: RequestHandler;
@@ -1,37 +0,0 @@
1
- import { InvalidIpError } from '@directus/errors';
2
- import getDatabase from '../database/index.js';
3
- import { useLogger } from '../logger/index.js';
4
- import asyncHandler from '../utils/async-handler.js';
5
- import { ipInNetworks } from '../utils/ip-in-networks.js';
6
- export const checkIP = asyncHandler(async (req, _res, next) => {
7
- const database = getDatabase();
8
- const logger = useLogger();
9
- const { role: roleId, ip } = req.accountability;
10
- const query = database.select('ip_access').from('directus_roles');
11
- if (roleId) {
12
- query.where({ id: roleId });
13
- }
14
- else {
15
- query.whereNull('id');
16
- }
17
- const role = await query.first();
18
- if (!role?.ip_access)
19
- return next();
20
- const ipAllowList = role.ip_access.split(',').filter((ip) => ip);
21
- if (ipAllowList.length > 0) {
22
- if (!ip)
23
- throw new InvalidIpError();
24
- let allowed;
25
- try {
26
- allowed = ipInNetworks(ip, ipAllowList);
27
- }
28
- catch (error) {
29
- logger.warn(`Invalid IP access configuration for role "${roleId}"`);
30
- logger.warn(error);
31
- throw new InvalidIpError();
32
- }
33
- if (!allowed)
34
- throw new InvalidIpError();
35
- }
36
- return next();
37
- });
@@ -1,3 +0,0 @@
1
- import type { RequestHandler } from 'express';
2
- declare const getPermissions: RequestHandler;
3
- export default getPermissions;
@@ -1,10 +0,0 @@
1
- import asyncHandler from '../utils/async-handler.js';
2
- import { getPermissions as getPermissionsUtil } from '../utils/get-permissions.js';
3
- const getPermissions = asyncHandler(async (req, _res, next) => {
4
- if (!req.accountability) {
5
- throw new Error('getPermissions middleware needs to be called after authenticate');
6
- }
7
- req.accountability.permissions = await getPermissionsUtil(req.accountability, req.schema);
8
- return next();
9
- });
10
- export default getPermissions;
@@ -1,17 +0,0 @@
1
- import type { Accountability, Item, PermissionsAction, PrimaryKey, SchemaOverview } from '@directus/types';
2
- import type { Knex } from 'knex';
3
- import type { AST, AbstractServiceOptions } from '../types/index.js';
4
- import { PayloadService } from './payload.js';
5
- export declare class AuthorizationService {
6
- knex: Knex;
7
- accountability: Accountability | null;
8
- payloadService: PayloadService;
9
- schema: SchemaOverview;
10
- constructor(options: AbstractServiceOptions);
11
- processAST(ast: AST, action?: PermissionsAction): Promise<AST>;
12
- /**
13
- * Checks if the provided payload matches the configured permissions, and adds the presets to the payload.
14
- */
15
- validatePayload(action: PermissionsAction, collection: string, data: Partial<Item>): Partial<Item>;
16
- checkAccess(action: PermissionsAction, collection: string, pk?: PrimaryKey | PrimaryKey[]): Promise<void>;
17
- }