@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
@@ -1,14 +1,17 @@
1
1
  import { useEnv } from '@directus/env';
2
2
  import formatTitle from '@directus/format-title';
3
3
  import { spec } from '@directus/specs';
4
+ import { isSystemCollection } from '@directus/system-data';
4
5
  import { version } from 'directus/version';
5
6
  import { cloneDeep, mergeWith } from 'lodash-es';
6
7
  import { OAS_REQUIRED_SCHEMAS } from '../constants.js';
7
8
  import getDatabase from '../database/index.js';
9
+ import { fetchPermissions } from '../permissions/lib/fetch-permissions.js';
10
+ import { fetchPolicies } from '../permissions/lib/fetch-policies.js';
11
+ import { fetchAllowedFieldMap } from '../permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js';
8
12
  import { getRelationType } from '../utils/get-relation-type.js';
9
13
  import { reduceSchema } from '../utils/reduce-schema.js';
10
14
  import { GraphQLService } from './graphql/index.js';
11
- import { isSystemCollection } from '@directus/system-data';
12
15
  const env = useEnv();
13
16
  export class SpecificationService {
14
17
  accountability;
@@ -16,29 +19,38 @@ export class SpecificationService {
16
19
  schema;
17
20
  oas;
18
21
  graphql;
19
- constructor({ accountability, knex, schema }) {
20
- this.accountability = accountability || null;
21
- this.knex = knex || getDatabase();
22
- this.schema = schema;
23
- this.oas = new OASSpecsService({ knex, schema, accountability });
24
- this.graphql = new GraphQLSpecsService({ knex, schema, accountability });
22
+ constructor(options) {
23
+ this.accountability = options.accountability || null;
24
+ this.knex = options.knex || getDatabase();
25
+ this.schema = options.schema;
26
+ this.oas = new OASSpecsService(options);
27
+ this.graphql = new GraphQLSpecsService(options);
25
28
  }
26
29
  }
27
30
  class OASSpecsService {
28
31
  accountability;
29
32
  knex;
30
33
  schema;
31
- constructor({ knex, schema, accountability }) {
32
- this.accountability = accountability || null;
33
- this.knex = knex || getDatabase();
34
- this.schema =
35
- this.accountability?.admin === true ? schema : reduceSchema(schema, accountability?.permissions || null);
34
+ constructor(options) {
35
+ this.accountability = options.accountability || null;
36
+ this.knex = options.knex || getDatabase();
37
+ this.schema = options.schema;
36
38
  }
37
39
  async generate(host) {
38
- const permissions = this.accountability?.permissions ?? [];
39
- const tags = await this.generateTags();
40
+ let schema = this.schema;
41
+ let permissions = [];
42
+ if (this.accountability && this.accountability.admin !== true) {
43
+ const allowedFields = await fetchAllowedFieldMap({
44
+ accountability: this.accountability,
45
+ action: 'read',
46
+ }, { schema, knex: this.knex });
47
+ schema = reduceSchema(schema, allowedFields);
48
+ const policies = await fetchPolicies(this.accountability, { schema, knex: this.knex });
49
+ permissions = await fetchPermissions({ action: 'read', policies, accountability: this.accountability }, { schema, knex: this.knex });
50
+ }
51
+ const tags = await this.generateTags(schema);
40
52
  const paths = await this.generatePaths(permissions, tags);
41
- const components = await this.generateComponents(tags);
53
+ const components = await this.generateComponents(schema, tags);
42
54
  const isDefaultPublicUrl = env['PUBLIC_URL'] === '/';
43
55
  const url = isDefaultPublicUrl && host ? host : env['PUBLIC_URL'];
44
56
  const spec = {
@@ -62,9 +74,9 @@ class OASSpecsService {
62
74
  spec.components = components;
63
75
  return spec;
64
76
  }
65
- async generateTags() {
77
+ async generateTags(schema) {
66
78
  const systemTags = cloneDeep(spec.tags);
67
- const collections = Object.values(this.schema.collections);
79
+ const collections = Object.values(schema.collections);
68
80
  const tags = [];
69
81
  for (const systemTag of systemTags) {
70
82
  // Check if necessary authentication level is given
@@ -246,7 +258,7 @@ class OASSpecsService {
246
258
  }
247
259
  return paths;
248
260
  }
249
- async generateComponents(tags) {
261
+ async generateComponents(schema, tags) {
250
262
  if (!tags)
251
263
  return;
252
264
  let components = cloneDeep(spec.components);
@@ -264,7 +276,7 @@ class OASSpecsService {
264
276
  };
265
277
  }
266
278
  }
267
- const collections = Object.values(this.schema.collections);
279
+ const collections = Object.values(schema.collections);
268
280
  for (const collection of collections) {
269
281
  const tag = tags.find((tag) => tag['x-collection'] === collection.collection);
270
282
  if (!tag)
@@ -277,7 +289,7 @@ class OASSpecsService {
277
289
  schemaComponent['x-collection'] = collection.collection;
278
290
  for (const field of fieldsInCollection) {
279
291
  schemaComponent.properties[field.field] =
280
- cloneDeep(spec.components.schemas[tag.name].properties[field.field]) || this.generateField(collection.collection, field, tags);
292
+ cloneDeep(spec.components.schemas[tag.name].properties[field.field]) || this.generateField(schema, collection.collection, field, tags);
281
293
  }
282
294
  components.schemas[tag.name] = schemaComponent;
283
295
  }
@@ -288,7 +300,7 @@ class OASSpecsService {
288
300
  'x-collection': collection.collection,
289
301
  };
290
302
  for (const field of fieldsInCollection) {
291
- schemaComponent.properties[field.field] = this.generateField(collection.collection, field, tags);
303
+ schemaComponent.properties[field.field] = this.generateField(schema, collection.collection, field, tags);
292
304
  }
293
305
  components.schemas[tag.name] = schemaComponent;
294
306
  }
@@ -311,13 +323,13 @@ class OASSpecsService {
311
323
  return 'read';
312
324
  }
313
325
  }
314
- generateField(collection, field, tags) {
326
+ generateField(schema, collection, field, tags) {
315
327
  let propertyObject = {};
316
328
  propertyObject.nullable = field.nullable;
317
329
  if (field.note) {
318
330
  propertyObject.description = field.note;
319
331
  }
320
- const relation = this.schema.relations.find((relation) => (relation.collection === collection && relation.field === field.field) ||
332
+ const relation = schema.relations.find((relation) => (relation.collection === collection && relation.field === field.field) ||
321
333
  (relation.related_collection === collection && relation.meta?.one_field === field.field));
322
334
  if (!relation) {
323
335
  propertyObject = {
@@ -335,10 +347,10 @@ class OASSpecsService {
335
347
  const relatedTag = tags.find((tag) => tag['x-collection'] === relation.related_collection);
336
348
  if (!relatedTag ||
337
349
  !relation.related_collection ||
338
- relation.related_collection in this.schema.collections === false) {
350
+ relation.related_collection in schema.collections === false) {
339
351
  return propertyObject;
340
352
  }
341
- const relatedCollection = this.schema.collections[relation.related_collection];
353
+ const relatedCollection = schema.collections[relation.related_collection];
342
354
  const relatedPrimaryKeyField = relatedCollection.fields[relatedCollection.primary];
343
355
  propertyObject.oneOf = [
344
356
  {
@@ -351,10 +363,10 @@ class OASSpecsService {
351
363
  }
352
364
  else if (relationType === 'o2m') {
353
365
  const relatedTag = tags.find((tag) => tag['x-collection'] === relation.collection);
354
- if (!relatedTag || !relation.related_collection || relation.collection in this.schema.collections === false) {
366
+ if (!relatedTag || !relation.related_collection || relation.collection in schema.collections === false) {
355
367
  return propertyObject;
356
368
  }
357
- const relatedCollection = this.schema.collections[relation.collection];
369
+ const relatedCollection = schema.collections[relation.collection];
358
370
  const relatedPrimaryKeyField = relatedCollection.fields[relatedCollection.primary];
359
371
  if (!relatedTag || !relatedPrimaryKeyField)
360
372
  return propertyObject;
@@ -4,7 +4,7 @@ import { extname } from 'node:path';
4
4
  import stream from 'node:stream';
5
5
  import { DataStore, ERRORS, Upload } from '@tus/utils';
6
6
  import { ItemsService } from '../items.js';
7
- import { useLogger } from '../../logger.js';
7
+ import { useLogger } from '../../logger/index.js';
8
8
  import getDatabase from '../../database/index.js';
9
9
  import { omit } from 'lodash-es';
10
10
  export class TusDataStore extends DataStore {
@@ -109,6 +109,7 @@ export class TusDataStore extends DataStore {
109
109
  }
110
110
  }
111
111
  async write(readable, tus_id, offset) {
112
+ const logger = useLogger();
112
113
  const fileData = await this.getFileById(tus_id);
113
114
  const filePath = fileData.filename_disk;
114
115
  const sudoService = new ItemsService('directus_files', {
@@ -146,6 +147,7 @@ export class TusDataStore extends DataStore {
146
147
  return newOffset;
147
148
  }
148
149
  catch (err) {
150
+ logger.error(err, 'Error writing chunk for upload "%s" at offset %d', tus_id, offset);
149
151
  if ('status_code' in err && err.status_code === 500) {
150
152
  throw err;
151
153
  }
@@ -13,11 +13,6 @@ export declare class UsersService extends ItemsService {
13
13
  * directus_settings.auth_password_policy
14
14
  */
15
15
  private checkPasswordPolicy;
16
- private checkRemainingAdminExistence;
17
- /**
18
- * Make sure there's at least one active admin user when updating user status
19
- */
20
- private checkRemainingActiveAdmin;
21
16
  /**
22
17
  * Get basic information of user identified by email
23
18
  */
@@ -52,4 +47,5 @@ export declare class UsersService extends ItemsService {
52
47
  verifyRegistration(token: string): Promise<string>;
53
48
  requestPasswordReset(email: string, url: string | null, subject?: string | null): Promise<void>;
54
49
  resetPassword(token: string, password: string): Promise<void>;
50
+ private clearCaches;
55
51
  }
@@ -1,23 +1,22 @@
1
1
  import { useEnv } from '@directus/env';
2
- import { ForbiddenError, InvalidPayloadError, RecordNotUniqueError, UnprocessableContentError } from '@directus/errors';
3
- import { getSimpleHash, toArray, toBoolean, validatePayload } from '@directus/utils';
2
+ import { ForbiddenError, InvalidPayloadError, RecordNotUniqueError } from '@directus/errors';
3
+ import { getSimpleHash, toArray, validatePayload } from '@directus/utils';
4
4
  import { FailedValidationError, joiValidationErrorItemToErrorExtensions } from '@directus/validation';
5
5
  import Joi from 'joi';
6
6
  import jwt from 'jsonwebtoken';
7
- import { isEmpty, mergeWith } from 'lodash-es';
7
+ import { isEmpty } from 'lodash-es';
8
8
  import { performance } from 'perf_hooks';
9
+ import { clearSystemCache } from '../cache.js';
9
10
  import getDatabase from '../database/index.js';
10
- import { useLogger } from '../logger.js';
11
- import { checkIncreasedUserLimits } from '../telemetry/utils/check-increased-user-limits.js';
12
- import { getRoleCountsByRoles } from '../telemetry/utils/get-role-counts-by-roles.js';
13
- import { getRoleCountsByUsers } from '../telemetry/utils/get-role-counts-by-users.js';
14
- import {} from '../telemetry/utils/get-user-count.js';
15
- import { shouldCheckUserLimits } from '../telemetry/utils/should-check-user-limits.js';
11
+ import { useLogger } from '../logger/index.js';
12
+ import { validateRemainingAdminUsers } from '../permissions/modules/validate-remaining-admin/validate-remaining-admin-users.js';
13
+ import { createDefaultAccountability } from '../permissions/utils/create-default-accountability.js';
16
14
  import { getSecret } from '../utils/get-secret.js';
17
15
  import isUrlAllowed from '../utils/is-url-allowed.js';
18
16
  import { verifyJWT } from '../utils/jwt.js';
19
17
  import { stall } from '../utils/stall.js';
20
18
  import { Url } from '../utils/url.js';
19
+ import { UserIntegrityCheckFlag } from '../utils/validate-user-count-integrity.js';
21
20
  import { ItemsService } from './items.js';
22
21
  import { MailService } from './mail/index.js';
23
22
  import { SettingsService } from './settings.js';
@@ -88,42 +87,11 @@ export class UsersService extends ItemsService {
88
87
  }
89
88
  }
90
89
  }
91
- async checkRemainingAdminExistence(excludeKeys) {
92
- // Make sure there's at least one admin user left after this deletion is done
93
- const otherAdminUsers = await this.knex
94
- .count('*', { as: 'count' })
95
- .from('directus_users')
96
- .whereNotIn('directus_users.id', excludeKeys)
97
- .andWhere({ 'directus_roles.admin_access': true })
98
- .leftJoin('directus_roles', 'directus_users.role', 'directus_roles.id')
99
- .first();
100
- const otherAdminUsersCount = +(otherAdminUsers?.count || 0);
101
- if (otherAdminUsersCount === 0) {
102
- throw new UnprocessableContentError({ reason: `You can't remove the last admin user from the role` });
103
- }
104
- }
105
- /**
106
- * Make sure there's at least one active admin user when updating user status
107
- */
108
- async checkRemainingActiveAdmin(excludeKeys) {
109
- const otherAdminUsers = await this.knex
110
- .count('*', { as: 'count' })
111
- .from('directus_users')
112
- .whereNotIn('directus_users.id', excludeKeys)
113
- .andWhere({ 'directus_roles.admin_access': true })
114
- .andWhere({ 'directus_users.status': 'active' })
115
- .leftJoin('directus_roles', 'directus_users.role', 'directus_roles.id')
116
- .first();
117
- const otherAdminUsersCount = +(otherAdminUsers?.count || 0);
118
- if (otherAdminUsersCount === 0) {
119
- throw new UnprocessableContentError({ reason: `You can't change the active status of the last admin user` });
120
- }
121
- }
122
90
  /**
123
91
  * Get basic information of user identified by email
124
92
  */
125
93
  async getUserByEmail(email) {
126
- return await this.knex
94
+ return this.knex
127
95
  .select('id', 'role', 'status', 'password', 'email')
128
96
  .from('directus_users')
129
97
  .whereRaw(`LOWER(??) = ?`, ['email', email.toLowerCase()])
@@ -158,51 +126,34 @@ export class UsersService extends ItemsService {
158
126
  /**
159
127
  * Create a new user
160
128
  */
161
- async createOne(data, opts) {
129
+ async createOne(data, opts = {}) {
162
130
  try {
163
- if (data['email']) {
131
+ if ('email' in data) {
164
132
  this.validateEmail(data['email']);
165
133
  await this.checkUniqueEmails([data['email']]);
166
134
  }
167
- if (data['password']) {
135
+ if ('password' in data) {
168
136
  await this.checkPasswordPolicy([data['password']]);
169
137
  }
170
- if (shouldCheckUserLimits() && data['role']) {
171
- const increasedCounts = {
172
- admin: 0,
173
- app: 0,
174
- api: 0,
175
- };
176
- if (typeof data['role'] === 'object') {
177
- if ('admin_access' in data['role'] && data['role']['admin_access'] === true) {
178
- increasedCounts.admin++;
179
- }
180
- else if ('app_access' in data['role'] && data['role']['app_access'] === true) {
181
- increasedCounts.app++;
182
- }
183
- else {
184
- increasedCounts.api++;
185
- }
186
- }
187
- else {
188
- const existingRoleCounts = await getRoleCountsByRoles(this.knex, [data['role']]);
189
- mergeWith(increasedCounts, existingRoleCounts, (x, y) => x + y);
190
- }
191
- await checkIncreasedUserLimits(this.knex, increasedCounts);
192
- }
193
138
  }
194
139
  catch (err) {
195
- (opts || (opts = {})).preMutationError = err;
140
+ opts.preMutationError = err;
141
+ }
142
+ if (!('status' in data) || data['status'] === 'active') {
143
+ // Creating a user only requires checking user limits if the user is active, no need to care about the role
144
+ opts.userIntegrityCheckFlags =
145
+ (opts.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None) | UserIntegrityCheckFlag.UserLimits;
146
+ opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
196
147
  }
197
148
  return await super.createOne(data, opts);
198
149
  }
199
150
  /**
200
151
  * Create multiple new users
201
152
  */
202
- async createMany(data, opts) {
203
- const emails = data['map']((payload) => payload['email']).filter((email) => email);
204
- const passwords = data['map']((payload) => payload['password']).filter((password) => password);
205
- const roles = data['map']((payload) => payload['role']).filter((role) => role);
153
+ async createMany(data, opts = {}) {
154
+ const emails = data.map((payload) => payload['email']).filter((email) => email);
155
+ const passwords = data.map((payload) => payload['password']).filter((password) => password);
156
+ const someActive = data.some((payload) => !('status' in payload) || payload['status'] === 'active');
206
157
  try {
207
158
  if (emails.length) {
208
159
  this.validateEmail(emails);
@@ -211,96 +162,30 @@ export class UsersService extends ItemsService {
211
162
  if (passwords.length) {
212
163
  await this.checkPasswordPolicy(passwords);
213
164
  }
214
- if (shouldCheckUserLimits() && roles.length) {
215
- const increasedCounts = {
216
- admin: 0,
217
- app: 0,
218
- api: 0,
219
- };
220
- const existingRoles = [];
221
- for (const role of roles) {
222
- if (typeof role === 'object') {
223
- if ('admin_access' in role && role['admin_access'] === true) {
224
- increasedCounts.admin++;
225
- }
226
- else if ('app_access' in role && role['app_access'] === true) {
227
- increasedCounts.app++;
228
- }
229
- else {
230
- increasedCounts.api++;
231
- }
232
- }
233
- else {
234
- existingRoles.push(role);
235
- }
236
- }
237
- const existingRoleCounts = await getRoleCountsByRoles(this.knex, existingRoles);
238
- mergeWith(increasedCounts, existingRoleCounts, (x, y) => x + y);
239
- await checkIncreasedUserLimits(this.knex, increasedCounts);
240
- }
241
165
  }
242
166
  catch (err) {
243
- (opts || (opts = {})).preMutationError = err;
167
+ opts.preMutationError = err;
168
+ }
169
+ if (someActive) {
170
+ // Creating users only requires checking user limits if the users are active, no need to care about the role
171
+ opts.userIntegrityCheckFlags =
172
+ (opts.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None) | UserIntegrityCheckFlag.UserLimits;
173
+ opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
244
174
  }
245
- return await super.createMany(data, opts);
175
+ // Use generic ItemsService to avoid calling `UserService.createOne` to avoid additional work of validating emails,
176
+ // as this requires one query per email if done in `createOne`
177
+ const itemsService = new ItemsService(this.collection, {
178
+ schema: this.schema,
179
+ accountability: this.accountability,
180
+ knex: this.knex,
181
+ });
182
+ return await itemsService.createMany(data, opts);
246
183
  }
247
184
  /**
248
185
  * Update many users by primary key
249
186
  */
250
- async updateMany(keys, data, opts) {
187
+ async updateMany(keys, data, opts = {}) {
251
188
  try {
252
- const needsUserLimitCheck = shouldCheckUserLimits();
253
- if (data['role']) {
254
- /*
255
- * data['role'] has the following cases:
256
- * - a string with existing role id
257
- * - an object with existing role id for GraphQL mutations
258
- * - an object with data for new role
259
- */
260
- const role = data['role']?.id ?? data['role'];
261
- let newRole;
262
- if (typeof role === 'string') {
263
- newRole = await this.knex
264
- .select('admin_access', 'app_access')
265
- .from('directus_roles')
266
- .where('id', role)
267
- .first();
268
- }
269
- else {
270
- newRole = role;
271
- }
272
- if (!newRole?.admin_access) {
273
- await this.checkRemainingAdminExistence(keys);
274
- }
275
- if (needsUserLimitCheck && newRole) {
276
- const existingCounts = await getRoleCountsByUsers(this.knex, keys);
277
- const increasedCounts = {
278
- admin: 0,
279
- app: 0,
280
- api: 0,
281
- };
282
- if (toBoolean(newRole.admin_access)) {
283
- increasedCounts.admin = keys.length - existingCounts.admin;
284
- }
285
- else if (toBoolean(newRole.app_access)) {
286
- increasedCounts.app = keys.length - existingCounts.app;
287
- }
288
- else {
289
- increasedCounts.api = keys.length - existingCounts.api;
290
- }
291
- await checkIncreasedUserLimits(this.knex, increasedCounts);
292
- }
293
- }
294
- if (needsUserLimitCheck && data['role'] === null) {
295
- await checkIncreasedUserLimits(this.knex, { admin: 0, app: 0, api: 1 });
296
- }
297
- if (data['status'] !== undefined && data['status'] !== 'active') {
298
- await this.checkRemainingActiveAdmin(keys);
299
- }
300
- if (needsUserLimitCheck && data['status'] === 'active') {
301
- const increasedCounts = await getRoleCountsByUsers(this.knex, keys, { inactiveUsers: true });
302
- await checkIncreasedUserLimits(this.knex, increasedCounts);
303
- }
304
189
  if (data['email']) {
305
190
  if (keys.length > 1) {
306
191
  throw new RecordNotUniqueError({
@@ -331,19 +216,45 @@ export class UsersService extends ItemsService {
331
216
  }
332
217
  }
333
218
  catch (err) {
334
- (opts || (opts = {})).preMutationError = err;
219
+ opts.preMutationError = err;
220
+ }
221
+ if ('role' in data) {
222
+ opts.userIntegrityCheckFlags = UserIntegrityCheckFlag.All;
335
223
  }
336
- return await super.updateMany(keys, data, opts);
224
+ if ('status' in data) {
225
+ if (data['status'] === 'active') {
226
+ // User are being activated, no need to check if there are enough admins
227
+ opts.userIntegrityCheckFlags =
228
+ (opts.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None) | UserIntegrityCheckFlag.UserLimits;
229
+ }
230
+ else {
231
+ opts.userIntegrityCheckFlags = UserIntegrityCheckFlag.All;
232
+ }
233
+ }
234
+ if (opts.userIntegrityCheckFlags) {
235
+ opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
236
+ }
237
+ const result = await super.updateMany(keys, data, opts);
238
+ // Only clear the caches if the role has been updated
239
+ if ('role' in data) {
240
+ await this.clearCaches(opts);
241
+ }
242
+ return result;
337
243
  }
338
244
  /**
339
245
  * Delete multiple users by primary key
340
246
  */
341
- async deleteMany(keys, opts) {
342
- try {
343
- await this.checkRemainingAdminExistence(keys);
247
+ async deleteMany(keys, opts = {}) {
248
+ if (opts?.onRequireUserIntegrityCheck) {
249
+ opts.onRequireUserIntegrityCheck(opts?.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None);
344
250
  }
345
- catch (err) {
346
- (opts || (opts = {})).preMutationError = err;
251
+ else {
252
+ try {
253
+ await validateRemainingAdminUsers({ excludeUsers: keys }, { knex: this.knex, schema: this.schema });
254
+ }
255
+ catch (err) {
256
+ opts.preMutationError = err;
257
+ }
347
258
  }
348
259
  // Manual constraint, see https://github.com/directus/directus/pull/19912
349
260
  await this.knex('directus_notifications').update({ sender: null }).whereIn('sender', keys);
@@ -566,10 +477,16 @@ export class UsersService extends ItemsService {
566
477
  knex: this.knex,
567
478
  schema: this.schema,
568
479
  accountability: {
569
- ...(this.accountability ?? { role: null }),
480
+ ...(this.accountability ?? createDefaultAccountability()),
570
481
  admin: true, // We need to skip permissions checks for the update call below
571
482
  },
572
483
  });
573
484
  await service.updateOne(user.id, { password, status: 'active' }, opts);
574
485
  }
486
+ async clearCaches(opts) {
487
+ await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
488
+ if (this.cache && opts?.autoPurgeCache !== false) {
489
+ await this.cache.clear();
490
+ }
491
+ }
575
492
  }
@@ -3,6 +3,8 @@ import { systemCollectionRows } from '@directus/system-data';
3
3
  import { clearSystemCache, getCache } from '../cache.js';
4
4
  import getDatabase from '../database/index.js';
5
5
  import emitter from '../emitter.js';
6
+ import { fetchAllowedFields } from '../permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js';
7
+ import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
6
8
  import { shouldClearCache } from '../utils/should-clear-cache.js';
7
9
  export class UtilsService {
8
10
  knex;
@@ -20,14 +22,16 @@ export class UtilsService {
20
22
  if (!sortField) {
21
23
  throw new InvalidPayloadError({ reason: `Collection "${collection}" doesn't have a sort field` });
22
24
  }
23
- if (this.accountability?.admin !== true) {
24
- const permissions = this.accountability?.permissions?.find((permission) => {
25
- return permission.collection === collection && permission.action === 'update';
25
+ if (this.accountability && this.accountability.admin !== true) {
26
+ await validateAccess({
27
+ accountability: this.accountability,
28
+ action: 'update',
29
+ collection,
30
+ }, {
31
+ schema: this.schema,
32
+ knex: this.knex,
26
33
  });
27
- if (!permissions) {
28
- throw new ForbiddenError();
29
- }
30
- const allowedFields = permissions.fields ?? [];
34
+ const allowedFields = await fetchAllowedFields({ collection, action: 'update', accountability: this.accountability }, { schema: this.schema, knex: this.knex });
31
35
  if (allowedFields[0] !== '*' && allowedFields.includes(sortField) === false) {
32
36
  throw new ForbiddenError();
33
37
  }
@@ -1,9 +1,7 @@
1
1
  import type { Item, PrimaryKey, Query } from '@directus/types';
2
2
  import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
3
- import { AuthorizationService } from './authorization.js';
4
3
  import { ItemsService } from './items.js';
5
4
  export declare class VersionsService extends ItemsService {
6
- authorizationService: AuthorizationService;
7
5
  constructor(options: AbstractServiceOptions);
8
6
  private validateCreateData;
9
7
  getMainItem(collection: string, item: PrimaryKey, query?: Query): Promise<Item>;
@@ -6,21 +6,15 @@ import objectHash from 'object-hash';
6
6
  import { getCache } from '../cache.js';
7
7
  import getDatabase from '../database/index.js';
8
8
  import emitter from '../emitter.js';
9
+ import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
9
10
  import { shouldClearCache } from '../utils/should-clear-cache.js';
10
11
  import { ActivityService } from './activity.js';
11
- import { AuthorizationService } from './authorization.js';
12
12
  import { ItemsService } from './items.js';
13
13
  import { PayloadService } from './payload.js';
14
14
  import { RevisionsService } from './revisions.js';
15
15
  export class VersionsService extends ItemsService {
16
- authorizationService;
17
16
  constructor(options) {
18
17
  super('directus_versions', options);
19
- this.authorizationService = new AuthorizationService({
20
- accountability: this.accountability,
21
- knex: this.knex,
22
- schema: this.schema,
23
- });
24
18
  }
25
19
  async validateCreateData(data) {
26
20
  if (!data['key'])
@@ -55,11 +49,31 @@ export class VersionsService extends ItemsService {
55
49
  });
56
50
  }
57
51
  // will throw an error if the accountability does not have permission to read the item
58
- await this.authorizationService.checkAccess('read', data['collection'], data['item']);
52
+ if (this.accountability) {
53
+ await validateAccess({
54
+ accountability: this.accountability,
55
+ action: 'read',
56
+ collection: data['collection'],
57
+ primaryKeys: [data['item']],
58
+ }, {
59
+ schema: this.schema,
60
+ knex: this.knex,
61
+ });
62
+ }
59
63
  }
60
64
  async getMainItem(collection, item, query) {
61
65
  // will throw an error if the accountability does not have permission to read the item
62
- await this.authorizationService.checkAccess('read', collection, item);
66
+ if (this.accountability) {
67
+ await validateAccess({
68
+ accountability: this.accountability,
69
+ action: 'read',
70
+ collection,
71
+ primaryKeys: [item],
72
+ }, {
73
+ schema: this.schema,
74
+ knex: this.knex,
75
+ });
76
+ }
63
77
  const itemsService = new ItemsService(collection, {
64
78
  knex: this.knex,
65
79
  accountability: this.accountability,
@@ -200,7 +214,17 @@ export class VersionsService extends ItemsService {
200
214
  async promote(version, mainHash, fields) {
201
215
  const { id, collection, item } = (await this.readOne(version));
202
216
  // will throw an error if the accountability does not have permission to update the item
203
- await this.authorizationService.checkAccess('update', collection, item);
217
+ if (this.accountability) {
218
+ await validateAccess({
219
+ accountability: this.accountability,
220
+ action: 'update',
221
+ collection,
222
+ primaryKeys: [item],
223
+ }, {
224
+ schema: this.schema,
225
+ knex: this.knex,
226
+ });
227
+ }
204
228
  const { outdated } = await this.verifyHash(collection, item, mainHash);
205
229
  if (outdated) {
206
230
  throw new UnprocessableContentError({
@@ -1,6 +1,6 @@
1
1
  import { ErrorCode, createError } from '@directus/errors';
2
2
  import { useBus } from '../bus/index.js';
3
- import { useLogger } from '../logger.js';
3
+ import { useLogger } from '../logger/index.js';
4
4
  import { ItemsService } from './items.js';
5
5
  const logger = useLogger();
6
6
  export class WebhooksService extends ItemsService {