@directus/api 19.3.1 → 20.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 (285) 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/utils/defaults.d.ts +4 -11
  10. package/dist/cli/utils/defaults.js +7 -1
  11. package/dist/constants.d.ts +1 -1
  12. package/dist/controllers/access.d.ts +2 -0
  13. package/dist/controllers/access.js +148 -0
  14. package/dist/controllers/auth.js +5 -16
  15. package/dist/controllers/permissions.js +14 -2
  16. package/dist/controllers/policies.d.ts +2 -0
  17. package/dist/controllers/policies.js +169 -0
  18. package/dist/controllers/roles.js +22 -1
  19. package/dist/controllers/users.js +0 -55
  20. package/dist/database/errors/dialects/mysql.js +23 -23
  21. package/dist/database/get-ast-from-query/get-ast-from-query.d.ts +16 -0
  22. package/dist/database/get-ast-from-query/get-ast-from-query.js +82 -0
  23. package/dist/database/get-ast-from-query/lib/convert-wildcards.d.ts +13 -0
  24. package/dist/database/get-ast-from-query/lib/convert-wildcards.js +69 -0
  25. package/dist/database/get-ast-from-query/lib/parse-fields.d.ts +15 -0
  26. package/dist/database/get-ast-from-query/lib/parse-fields.js +190 -0
  27. package/dist/database/get-ast-from-query/utils/get-deep-query.d.ts +14 -0
  28. package/dist/database/get-ast-from-query/utils/get-deep-query.js +17 -0
  29. package/dist/database/get-ast-from-query/utils/get-related-collection.d.ts +2 -0
  30. package/dist/database/get-ast-from-query/utils/get-related-collection.js +13 -0
  31. package/dist/database/get-ast-from-query/utils/get-relation.d.ts +2 -0
  32. package/dist/database/get-ast-from-query/utils/get-relation.js +7 -0
  33. package/dist/database/helpers/fn/types.d.ts +2 -1
  34. package/dist/database/helpers/fn/types.js +1 -1
  35. package/dist/database/helpers/geometry/dialects/mssql.d.ts +1 -1
  36. package/dist/database/helpers/geometry/dialects/mssql.js +4 -2
  37. package/dist/database/helpers/geometry/dialects/mysql.js +1 -1
  38. package/dist/database/helpers/geometry/dialects/oracle.d.ts +1 -1
  39. package/dist/database/helpers/geometry/dialects/oracle.js +5 -3
  40. package/dist/database/helpers/geometry/types.d.ts +1 -1
  41. package/dist/database/helpers/geometry/types.js +4 -2
  42. package/dist/database/index.js +2 -1
  43. package/dist/database/migrations/20240619A-permissions-policies.d.ts +3 -0
  44. package/dist/database/migrations/20240619A-permissions-policies.js +163 -0
  45. package/dist/database/run-ast/lib/get-db-query.d.ts +4 -0
  46. package/dist/database/run-ast/lib/get-db-query.js +194 -0
  47. package/dist/database/run-ast/lib/parse-current-level.d.ts +7 -0
  48. package/dist/database/run-ast/lib/parse-current-level.js +41 -0
  49. package/dist/database/run-ast/run-ast.d.ts +7 -0
  50. package/dist/database/run-ast/run-ast.js +107 -0
  51. package/dist/database/{run-ast.d.ts → run-ast/types.d.ts} +3 -9
  52. package/dist/database/run-ast/types.js +1 -0
  53. package/dist/database/run-ast/utils/apply-case-when.d.ts +16 -0
  54. package/dist/database/run-ast/utils/apply-case-when.js +26 -0
  55. package/dist/database/run-ast/utils/apply-parent-filters.d.ts +3 -0
  56. package/dist/database/run-ast/utils/apply-parent-filters.js +55 -0
  57. package/dist/database/run-ast/utils/get-column-pre-processor.d.ts +10 -0
  58. package/dist/database/run-ast/utils/get-column-pre-processor.js +57 -0
  59. package/dist/database/run-ast/utils/get-field-alias.d.ts +2 -0
  60. package/dist/database/run-ast/utils/get-field-alias.js +4 -0
  61. package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.d.ts +5 -0
  62. package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.js +23 -0
  63. package/dist/database/run-ast/utils/merge-with-parent-items.d.ts +3 -0
  64. package/dist/database/run-ast/utils/merge-with-parent-items.js +87 -0
  65. package/dist/database/run-ast/utils/remove-temporary-fields.d.ts +3 -0
  66. package/dist/database/run-ast/utils/remove-temporary-fields.js +73 -0
  67. package/dist/extensions/lib/sandbox/generate-api-extensions-sandbox-entrypoint.d.ts +1 -1
  68. package/dist/flows.js +3 -4
  69. package/dist/middleware/authenticate.js +2 -7
  70. package/dist/middleware/cache.js +1 -1
  71. package/dist/middleware/cors.js +4 -4
  72. package/dist/middleware/respond.js +1 -1
  73. package/dist/permissions/cache.d.ts +2 -0
  74. package/dist/permissions/cache.js +23 -0
  75. package/dist/permissions/lib/fetch-permissions.d.ts +10 -0
  76. package/dist/permissions/lib/fetch-permissions.js +55 -0
  77. package/dist/permissions/lib/fetch-policies.d.ts +7 -0
  78. package/dist/permissions/lib/fetch-policies.js +28 -0
  79. package/dist/permissions/lib/fetch-roles-tree.d.ts +3 -0
  80. package/dist/permissions/lib/fetch-roles-tree.js +28 -0
  81. package/dist/{services/permissions → permissions}/lib/with-app-minimal-permissions.d.ts +1 -1
  82. package/dist/permissions/lib/with-app-minimal-permissions.js +10 -0
  83. package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.d.ts +7 -0
  84. package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js +56 -0
  85. package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.d.ts +3 -0
  86. package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js +16 -0
  87. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.d.ts +8 -0
  88. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +24 -0
  89. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.d.ts +9 -0
  90. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +31 -0
  91. package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.d.ts +16 -0
  92. package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js +27 -0
  93. package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +10 -0
  94. package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +23 -0
  95. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +5 -0
  96. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +7 -0
  97. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +5 -0
  98. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +10 -0
  99. package/dist/permissions/modules/fetch-global-access/types.d.ts +4 -0
  100. package/dist/permissions/modules/fetch-global-access/types.js +1 -0
  101. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +4 -0
  102. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +27 -0
  103. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.d.ts +12 -0
  104. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +32 -0
  105. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +4 -0
  106. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.js +29 -0
  107. package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.d.ts +4 -0
  108. package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.js +49 -0
  109. package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.d.ts +3 -0
  110. package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.js +56 -0
  111. package/dist/permissions/modules/process-ast/lib/field-map-from-ast.d.ts +4 -0
  112. package/dist/permissions/modules/process-ast/lib/field-map-from-ast.js +8 -0
  113. package/dist/permissions/modules/process-ast/lib/inject-cases.d.ts +9 -0
  114. package/dist/permissions/modules/process-ast/lib/inject-cases.js +93 -0
  115. package/dist/permissions/modules/process-ast/process-ast.d.ts +9 -0
  116. package/dist/permissions/modules/process-ast/process-ast.js +39 -0
  117. package/dist/permissions/modules/process-ast/types.d.ts +24 -0
  118. package/dist/permissions/modules/process-ast/types.js +1 -0
  119. package/dist/permissions/modules/process-ast/utils/collections-in-field-map.d.ts +2 -0
  120. package/dist/permissions/modules/process-ast/utils/collections-in-field-map.js +7 -0
  121. package/dist/permissions/modules/process-ast/utils/dedupe-access.d.ts +12 -0
  122. package/dist/permissions/modules/process-ast/utils/dedupe-access.js +30 -0
  123. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.d.ts +15 -0
  124. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +50 -0
  125. package/dist/permissions/modules/process-ast/utils/find-related-collection.d.ts +3 -0
  126. package/dist/permissions/modules/process-ast/utils/find-related-collection.js +9 -0
  127. package/dist/permissions/modules/process-ast/utils/flatten-filter.d.ts +3 -0
  128. package/dist/permissions/modules/process-ast/utils/flatten-filter.js +24 -0
  129. package/dist/permissions/modules/process-ast/utils/format-a2o-key.d.ts +1 -0
  130. package/dist/permissions/modules/process-ast/utils/format-a2o-key.js +3 -0
  131. package/dist/permissions/modules/process-ast/utils/get-info-for-path.d.ts +5 -0
  132. package/dist/permissions/modules/process-ast/utils/get-info-for-path.js +7 -0
  133. package/dist/permissions/modules/process-ast/utils/has-item-permissions.d.ts +2 -0
  134. package/dist/permissions/modules/process-ast/utils/has-item-permissions.js +3 -0
  135. package/dist/permissions/modules/process-ast/utils/stringify-query-path.d.ts +2 -0
  136. package/dist/permissions/modules/process-ast/utils/stringify-query-path.js +3 -0
  137. package/dist/permissions/modules/process-ast/utils/validate-path/create-error.d.ts +3 -0
  138. package/dist/permissions/modules/process-ast/utils/validate-path/create-error.js +16 -0
  139. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.d.ts +2 -0
  140. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.js +12 -0
  141. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.d.ts +2 -0
  142. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.js +28 -0
  143. package/dist/permissions/modules/process-payload/lib/is-field-nullable.d.ts +5 -0
  144. package/dist/permissions/modules/process-payload/lib/is-field-nullable.js +12 -0
  145. package/dist/permissions/modules/process-payload/process-payload.d.ts +13 -0
  146. package/dist/permissions/modules/process-payload/process-payload.js +77 -0
  147. package/dist/permissions/modules/validate-access/lib/validate-collection-access.d.ts +12 -0
  148. package/dist/permissions/modules/validate-access/lib/validate-collection-access.js +11 -0
  149. package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +9 -0
  150. package/dist/permissions/modules/validate-access/lib/validate-item-access.js +33 -0
  151. package/dist/permissions/modules/validate-access/validate-access.d.ts +14 -0
  152. package/dist/permissions/modules/validate-access/validate-access.js +28 -0
  153. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.d.ts +1 -0
  154. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.js +8 -0
  155. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.d.ts +5 -0
  156. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.js +10 -0
  157. package/dist/permissions/types.d.ts +6 -0
  158. package/dist/permissions/types.js +1 -0
  159. package/dist/permissions/utils/create-default-accountability.d.ts +2 -0
  160. package/dist/permissions/utils/create-default-accountability.js +11 -0
  161. package/dist/permissions/utils/extract-required-dynamic-variable-context.d.ts +8 -0
  162. package/dist/permissions/utils/extract-required-dynamic-variable-context.js +27 -0
  163. package/dist/permissions/utils/fetch-dynamic-variable-context.d.ts +9 -0
  164. package/dist/permissions/utils/fetch-dynamic-variable-context.js +43 -0
  165. package/dist/permissions/utils/filter-policies-by-ip.d.ts +2 -0
  166. package/dist/permissions/utils/filter-policies-by-ip.js +15 -0
  167. package/dist/permissions/utils/get-unaliased-field-key.d.ts +5 -0
  168. package/dist/permissions/utils/get-unaliased-field-key.js +17 -0
  169. package/dist/permissions/utils/process-permissions.d.ts +7 -0
  170. package/dist/permissions/utils/process-permissions.js +9 -0
  171. package/dist/permissions/utils/with-cache.d.ts +10 -0
  172. package/dist/permissions/utils/with-cache.js +25 -0
  173. package/dist/services/access.d.ts +10 -0
  174. package/dist/services/access.js +43 -0
  175. package/dist/services/activity.js +22 -10
  176. package/dist/services/assets.d.ts +2 -3
  177. package/dist/services/assets.js +10 -5
  178. package/dist/services/authentication.js +18 -18
  179. package/dist/services/collections.js +18 -17
  180. package/dist/services/fields.d.ts +0 -1
  181. package/dist/services/fields.js +53 -24
  182. package/dist/services/files.d.ts +0 -4
  183. package/dist/services/files.js +10 -10
  184. package/dist/services/flows.d.ts +0 -2
  185. package/dist/services/flows.js +2 -14
  186. package/dist/services/graphql/index.d.ts +3 -3
  187. package/dist/services/graphql/index.js +126 -22
  188. package/dist/services/graphql/subscription.js +2 -4
  189. package/dist/services/import-export.js +23 -9
  190. package/dist/services/index.d.ts +3 -2
  191. package/dist/services/index.js +3 -2
  192. package/dist/services/items.d.ts +40 -14
  193. package/dist/services/items.js +182 -79
  194. package/dist/services/meta.js +60 -23
  195. package/dist/services/notifications.d.ts +0 -1
  196. package/dist/services/notifications.js +0 -7
  197. package/dist/services/operations.d.ts +0 -2
  198. package/dist/services/operations.js +2 -14
  199. package/dist/services/payload.d.ts +9 -10
  200. package/dist/services/payload.js +35 -19
  201. package/dist/services/{permissions/index.d.ts → permissions.d.ts} +5 -7
  202. package/dist/services/{permissions/index.js → permissions.js} +30 -54
  203. package/dist/services/policies.d.ts +12 -0
  204. package/dist/services/policies.js +87 -0
  205. package/dist/services/relations.d.ts +0 -6
  206. package/dist/services/relations.js +26 -29
  207. package/dist/services/roles.d.ts +4 -14
  208. package/dist/services/roles.js +56 -430
  209. package/dist/services/shares.d.ts +0 -2
  210. package/dist/services/shares.js +12 -8
  211. package/dist/services/specifications.d.ts +2 -2
  212. package/dist/services/specifications.js +39 -27
  213. package/dist/services/users.d.ts +2 -20
  214. package/dist/services/users.js +87 -192
  215. package/dist/services/utils.js +11 -7
  216. package/dist/services/versions.d.ts +0 -2
  217. package/dist/services/versions.js +34 -10
  218. package/dist/telemetry/lib/get-report.js +6 -3
  219. package/dist/telemetry/types/report.d.ts +4 -0
  220. package/dist/telemetry/utils/check-user-limits.d.ts +5 -0
  221. package/dist/telemetry/utils/check-user-limits.js +19 -0
  222. package/dist/telemetry/utils/get-filesize-sum.d.ts +5 -0
  223. package/dist/telemetry/utils/get-filesize-sum.js +7 -0
  224. package/dist/types/ast.d.ts +43 -1
  225. package/dist/types/items.d.ts +11 -0
  226. package/dist/utils/apply-query.d.ts +4 -3
  227. package/dist/utils/apply-query.js +37 -8
  228. package/dist/utils/fetch-user-count/fetch-access-lookup.d.ts +17 -0
  229. package/dist/utils/fetch-user-count/fetch-access-lookup.js +22 -0
  230. package/dist/utils/fetch-user-count/fetch-access-roles.d.ts +16 -0
  231. package/dist/utils/fetch-user-count/fetch-access-roles.js +37 -0
  232. package/dist/utils/fetch-user-count/fetch-active-users.d.ts +6 -0
  233. package/dist/utils/fetch-user-count/fetch-active-users.js +3 -0
  234. package/dist/utils/fetch-user-count/fetch-user-count.d.ts +12 -0
  235. package/dist/utils/fetch-user-count/fetch-user-count.js +57 -0
  236. package/dist/utils/fetch-user-count/get-user-count-query.d.ts +20 -0
  237. package/dist/utils/fetch-user-count/get-user-count-query.js +17 -0
  238. package/dist/utils/get-accountability-for-role.js +16 -25
  239. package/dist/utils/get-accountability-for-token.js +17 -16
  240. package/dist/utils/get-cache-key.d.ts +1 -1
  241. package/dist/utils/get-cache-key.js +12 -1
  242. package/dist/utils/get-column.d.ts +2 -1
  243. package/dist/utils/get-column.js +1 -0
  244. package/dist/utils/get-graphql-type.js +1 -0
  245. package/dist/utils/get-service.d.ts +1 -1
  246. package/dist/utils/get-service.js +14 -10
  247. package/dist/utils/reduce-schema.d.ts +4 -6
  248. package/dist/utils/reduce-schema.js +14 -34
  249. package/dist/utils/validate-user-count-integrity.d.ts +13 -0
  250. package/dist/utils/validate-user-count-integrity.js +29 -0
  251. package/dist/websocket/authenticate.d.ts +0 -2
  252. package/dist/websocket/authenticate.js +0 -12
  253. package/dist/websocket/controllers/graphql.js +1 -4
  254. package/dist/websocket/controllers/hooks.js +4 -0
  255. package/dist/websocket/controllers/rest.js +0 -2
  256. package/dist/websocket/handlers/subscribe.js +0 -2
  257. package/dist/websocket/utils/items.d.ts +1 -1
  258. package/dist/websocket/utils/items.js +4 -1
  259. package/package.json +31 -30
  260. package/dist/database/run-ast.js +0 -450
  261. package/dist/middleware/check-ip.d.ts +0 -2
  262. package/dist/middleware/check-ip.js +0 -37
  263. package/dist/middleware/get-permissions.d.ts +0 -3
  264. package/dist/middleware/get-permissions.js +0 -10
  265. package/dist/services/authorization.d.ts +0 -17
  266. package/dist/services/authorization.js +0 -456
  267. package/dist/services/permissions/lib/with-app-minimal-permissions.js +0 -13
  268. package/dist/telemetry/utils/check-increased-user-limits.d.ts +0 -7
  269. package/dist/telemetry/utils/check-increased-user-limits.js +0 -22
  270. package/dist/telemetry/utils/get-role-counts-by-roles.d.ts +0 -6
  271. package/dist/telemetry/utils/get-role-counts-by-roles.js +0 -27
  272. package/dist/telemetry/utils/get-role-counts-by-users.d.ts +0 -11
  273. package/dist/telemetry/utils/get-role-counts-by-users.js +0 -34
  274. package/dist/telemetry/utils/get-user-count.d.ts +0 -8
  275. package/dist/telemetry/utils/get-user-count.js +0 -33
  276. package/dist/telemetry/utils/get-user-counts-by-roles.d.ts +0 -7
  277. package/dist/telemetry/utils/get-user-counts-by-roles.js +0 -35
  278. package/dist/utils/get-ast-from-query.d.ts +0 -13
  279. package/dist/utils/get-ast-from-query.js +0 -297
  280. package/dist/utils/get-permissions.d.ts +0 -2
  281. package/dist/utils/get-permissions.js +0 -150
  282. package/dist/utils/merge-permissions-for-share.d.ts +0 -4
  283. package/dist/utils/merge-permissions-for-share.js +0 -109
  284. package/dist/utils/merge-permissions.d.ts +0 -3
  285. package/dist/utils/merge-permissions.js +0 -95
@@ -0,0 +1,17 @@
1
+ import { parseFilterKey } from '../../utils/parse-filter-key.js';
2
+ /**
3
+ * Derive the unaliased field key from the given AST node.
4
+ */
5
+ export function getUnaliasedFieldKey(node) {
6
+ switch (node.type) {
7
+ case 'o2m':
8
+ return node.relation.meta.one_field;
9
+ case 'a2o':
10
+ case 'm2o':
11
+ return node.relation.field;
12
+ case 'field':
13
+ case 'functionField':
14
+ // The field name might still include a function, so process that here as well
15
+ return parseFilterKey(node.name).fieldName;
16
+ }
17
+ }
@@ -0,0 +1,7 @@
1
+ import type { Accountability, Permission } from '@directus/types';
2
+ export interface ProcessPermissionsOptions {
3
+ permissions: Permission[];
4
+ accountability: Pick<Accountability, 'user' | 'role' | 'roles'>;
5
+ permissionsContext: Record<string, any>;
6
+ }
7
+ export declare function processPermissions({ permissions, accountability, permissionsContext }: ProcessPermissionsOptions): Permission[];
@@ -0,0 +1,9 @@
1
+ import { parseFilter, parsePreset } from '@directus/utils';
2
+ export function processPermissions({ permissions, accountability, permissionsContext }) {
3
+ return permissions.map((permission) => {
4
+ permission.permissions = parseFilter(permission.permissions, accountability, permissionsContext);
5
+ permission.validation = parseFilter(permission.validation, accountability, permissionsContext);
6
+ permission.presets = parsePreset(permission.presets, accountability, permissionsContext);
7
+ return permission;
8
+ });
9
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The `pick` parameter can be used to stabilize cache keys, by only using a subset of the available parameters and
3
+ * ensuring key order.
4
+ *
5
+ * If the `pick` function is provided, we pass the picked result to the handler, in order for TypeScript to ensure that
6
+ * the function only relies on the parameters that are used for generating the cache key.
7
+ *
8
+ * @NOTE only uses the first parameter for memoization
9
+ */
10
+ export declare function withCache<F extends (arg0: Arg0, ...args: any[]) => R, R, Arg0 = Parameters<F>[0]>(namespace: string, handler: F, prepareArg?: (arg0: Arg0) => Arg0): F;
@@ -0,0 +1,25 @@
1
+ import { getSimpleHash } from '@directus/utils';
2
+ import { useCache } from '../cache.js';
3
+ /**
4
+ * The `pick` parameter can be used to stabilize cache keys, by only using a subset of the available parameters and
5
+ * ensuring key order.
6
+ *
7
+ * If the `pick` function is provided, we pass the picked result to the handler, in order for TypeScript to ensure that
8
+ * the function only relies on the parameters that are used for generating the cache key.
9
+ *
10
+ * @NOTE only uses the first parameter for memoization
11
+ */
12
+ export function withCache(namespace, handler, prepareArg) {
13
+ const cache = useCache();
14
+ return (async (arg0, ...args) => {
15
+ arg0 = prepareArg ? prepareArg(arg0) : arg0;
16
+ const key = namespace + '-' + getSimpleHash(JSON.stringify(arg0));
17
+ const cached = await cache.get(key);
18
+ if (cached !== undefined) {
19
+ return cached;
20
+ }
21
+ const res = await handler(arg0, ...args);
22
+ cache.set(key, res);
23
+ return res;
24
+ });
25
+ }
@@ -0,0 +1,10 @@
1
+ import type { Item, PrimaryKey } from '@directus/types';
2
+ import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
3
+ import { ItemsService } from './items.js';
4
+ export declare class AccessService extends ItemsService {
5
+ constructor(options: AbstractServiceOptions);
6
+ private clearCaches;
7
+ createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
8
+ updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
9
+ deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
10
+ }
@@ -0,0 +1,43 @@
1
+ import { clearSystemCache } from '../cache.js';
2
+ import { UserIntegrityCheckFlag } from '../utils/validate-user-count-integrity.js';
3
+ import { ItemsService } from './items.js';
4
+ export class AccessService extends ItemsService {
5
+ constructor(options) {
6
+ super('directus_access', options);
7
+ }
8
+ async clearCaches(opts) {
9
+ await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
10
+ if (this.cache && opts?.autoPurgeCache !== false) {
11
+ await this.cache.clear();
12
+ }
13
+ }
14
+ async createOne(data, opts = {}) {
15
+ // Creating a new policy attachments affects the number of admin/app/api users.
16
+ // But it can only add app or admin users, so no need to check the remaining admin users.
17
+ opts.userIntegrityCheckFlags =
18
+ (opts.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None) | UserIntegrityCheckFlag.UserLimits;
19
+ opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
20
+ const result = await super.createOne(data, opts);
21
+ // A new policy has been attached to a user or a role, clear the caches
22
+ await this.clearCaches();
23
+ return result;
24
+ }
25
+ async updateMany(keys, data, opts = {}) {
26
+ // Updating policy attachments might affect the number of admin/app/api users
27
+ opts.userIntegrityCheckFlags = UserIntegrityCheckFlag.All;
28
+ opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
29
+ const result = await super.updateMany(keys, data, { ...opts, userIntegrityCheckFlags: UserIntegrityCheckFlag.All });
30
+ // Some policy attachments have been updated, clear the caches
31
+ await this.clearCaches();
32
+ return result;
33
+ }
34
+ async deleteMany(keys, opts = {}) {
35
+ // Changes here can affect the number of admin/app/api users
36
+ opts.userIntegrityCheckFlags = UserIntegrityCheckFlag.All;
37
+ opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
38
+ const result = await super.deleteMany(keys, opts);
39
+ // Some policy attachments have been deleted, clear the caches
40
+ await this.clearCaches();
41
+ return result;
42
+ }
43
+ }
@@ -3,11 +3,13 @@ import { useEnv } from '@directus/env';
3
3
  import { ErrorCode, isDirectusError } from '@directus/errors';
4
4
  import { uniq } from 'lodash-es';
5
5
  import { useLogger } from '../logger.js';
6
- import { getPermissions } from '../utils/get-permissions.js';
6
+ import { fetchRolesTree } from '../permissions/lib/fetch-roles-tree.js';
7
+ import { fetchGlobalAccess } from '../permissions/modules/fetch-global-access/fetch-global-access.js';
8
+ import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
9
+ import { createDefaultAccountability } from '../permissions/utils/create-default-accountability.js';
7
10
  import { isValidUuid } from '../utils/is-valid-uuid.js';
8
11
  import { Url } from '../utils/url.js';
9
12
  import { userName } from '../utils/user-name.js';
10
- import { AuthorizationService } from './authorization.js';
11
13
  import { ItemsService } from './items.js';
12
14
  import { NotificationsService } from './notifications.js';
13
15
  import { UsersService } from './users.js';
@@ -31,19 +33,29 @@ export class ActivityService extends ItemsService {
31
33
  for (const mention of mentions) {
32
34
  const userID = mention.substring(1);
33
35
  const user = await this.usersService.readOne(userID, {
34
- fields: ['id', 'first_name', 'last_name', 'email', 'role.id', 'role.admin_access', 'role.app_access'],
36
+ fields: ['id', 'first_name', 'last_name', 'email', 'role'],
35
37
  });
36
- const accountability = {
38
+ const roles = await fetchRolesTree(user['role'], this.knex);
39
+ const globalAccess = await fetchGlobalAccess({ user: user['id'], roles, ip: null }, this.knex);
40
+ const accountability = createDefaultAccountability({
37
41
  user: userID,
38
42
  role: user['role']?.id ?? null,
39
- admin: user['role']?.admin_access ?? null,
40
- app: user['role']?.app_access ?? null,
41
- };
42
- accountability.permissions = await getPermissions(accountability, this.schema);
43
- const authorizationService = new AuthorizationService({ schema: this.schema, accountability });
43
+ roles,
44
+ ...globalAccess,
45
+ });
44
46
  const usersService = new UsersService({ schema: this.schema, accountability });
45
47
  try {
46
- await authorizationService.checkAccess('read', data['collection'], data['item']);
48
+ if (this.accountability) {
49
+ await validateAccess({
50
+ accountability: this.accountability,
51
+ action: 'read',
52
+ collection: data['collection'],
53
+ primaryKeys: [data['item']],
54
+ }, {
55
+ knex: this.knex,
56
+ schema: this.schema,
57
+ });
58
+ }
47
59
  const templateData = await usersService.readByQuery({
48
60
  fields: ['id', 'first_name', 'last_name', 'email'],
49
61
  filter: { id: { _in: mentions.map((mention) => mention.substring(1)) } },
@@ -1,15 +1,14 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  import type { Range, Stat } from '@directus/storage';
3
- import type { Accountability } from '@directus/types';
3
+ import type { Accountability, SchemaOverview } from '@directus/types';
4
4
  import type { Knex } from 'knex';
5
5
  import type { Readable } from 'node:stream';
6
6
  import type { AbstractServiceOptions, TransformationSet } from '../types/index.js';
7
- import { AuthorizationService } from './authorization.js';
8
7
  import { FilesService } from './files.js';
9
8
  export declare class AssetsService {
10
9
  knex: Knex;
11
10
  accountability: Accountability | null;
12
- authorizationService: AuthorizationService;
11
+ schema: SchemaOverview;
13
12
  filesService: FilesService;
14
13
  constructor(options: AbstractServiceOptions);
15
14
  getAsset(id: string, transformation?: TransformationSet, range?: Range): Promise<{
@@ -8,24 +8,24 @@ import sharp from 'sharp';
8
8
  import { SUPPORTED_IMAGE_TRANSFORM_FORMATS } from '../constants.js';
9
9
  import getDatabase from '../database/index.js';
10
10
  import { useLogger } from '../logger.js';
11
+ import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
11
12
  import { getStorage } from '../storage/index.js';
12
13
  import { getMilliseconds } from '../utils/get-milliseconds.js';
13
14
  import { isValidUuid } from '../utils/is-valid-uuid.js';
14
15
  import * as TransformationUtils from '../utils/transformations.js';
15
- import { AuthorizationService } from './authorization.js';
16
16
  import { FilesService } from './files.js';
17
17
  const env = useEnv();
18
18
  const logger = useLogger();
19
19
  export class AssetsService {
20
20
  knex;
21
21
  accountability;
22
- authorizationService;
22
+ schema;
23
23
  filesService;
24
24
  constructor(options) {
25
25
  this.knex = options.knex || getDatabase();
26
26
  this.accountability = options.accountability || null;
27
+ this.schema = options.schema;
27
28
  this.filesService = new FilesService({ ...options, accountability: null });
28
- this.authorizationService = new AuthorizationService(options);
29
29
  }
30
30
  async getAsset(id, transformation, range) {
31
31
  const storage = await getStorage();
@@ -41,8 +41,13 @@ export class AssetsService {
41
41
  */
42
42
  if (!isValidUuid(id))
43
43
  throw new ForbiddenError();
44
- if (systemPublicKeys.includes(id) === false && this.accountability?.admin !== true) {
45
- await this.authorizationService.checkAccess('read', 'directus_files', id);
44
+ if (systemPublicKeys.includes(id) === false && this.accountability) {
45
+ await validateAccess({
46
+ accountability: this.accountability,
47
+ action: 'read',
48
+ collection: 'directus_files',
49
+ primaryKeys: [id],
50
+ }, { knex: this.knex, schema: this.schema });
46
51
  }
47
52
  const file = (await this.filesService.readOne(id, { limit: 1 }));
48
53
  const exists = await storage.location(file.storage).exists(file.filename_disk);
@@ -1,3 +1,5 @@
1
+ import { fetchRolesTree } from '../permissions/lib/fetch-roles-tree.js';
2
+ import { fetchGlobalAccess } from '../permissions/modules/fetch-global-access/fetch-global-access.js';
1
3
  import { Action } from '@directus/constants';
2
4
  import { useEnv } from '@directus/env';
3
5
  import { InvalidCredentialsError, InvalidOtpError, InvalidProviderError, ServiceUnavailableError, UserSuspendedError, } from '@directus/errors';
@@ -48,10 +50,9 @@ export class AuthenticationService {
48
50
  throw err;
49
51
  }
50
52
  const user = await this.knex
51
- .select('u.id', 'u.first_name', 'u.last_name', 'u.email', 'u.password', 'u.status', 'u.role', 'r.admin_access', 'r.app_access', 'u.tfa_secret', 'u.provider', 'u.external_identifier', 'u.auth_data')
52
- .from('directus_users as u')
53
- .leftJoin('directus_roles as r', 'u.role', 'r.id')
54
- .where('u.id', userId)
53
+ .select('id', 'first_name', 'last_name', 'email', 'password', 'status', 'role', 'tfa_secret', 'provider', 'external_identifier', 'auth_data')
54
+ .from('directus_users')
55
+ .where('id', userId)
55
56
  .first();
56
57
  const updatedPayload = await emitter.emitFilter('auth.login', payload, {
57
58
  status: 'pending',
@@ -138,11 +139,13 @@ export class AuthenticationService {
138
139
  throw new InvalidOtpError();
139
140
  }
140
141
  }
142
+ const roles = await fetchRolesTree(user.role, this.knex);
143
+ const globalAccess = await fetchGlobalAccess({ roles, user: user.id, ip: this.accountability?.ip ?? null }, this.knex);
141
144
  const tokenPayload = {
142
145
  id: user.id,
143
146
  role: user.role,
144
- app_access: user.app_access,
145
- admin_access: user.admin_access,
147
+ app_access: globalAccess.app,
148
+ admin_access: globalAccess.admin,
146
149
  };
147
150
  const refreshToken = nanoid(64);
148
151
  const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(env['REFRESH_TOKEN_TTL'], 0));
@@ -217,9 +220,7 @@ export class AuthenticationService {
217
220
  user_provider: 'u.provider',
218
221
  user_external_identifier: 'u.external_identifier',
219
222
  user_auth_data: 'u.auth_data',
220
- role_id: 'r.id',
221
- role_admin_access: 'r.admin_access',
222
- role_app_access: 'r.app_access',
223
+ user_role: 'u.role',
223
224
  share_id: 'd.id',
224
225
  share_item: 'd.item',
225
226
  share_role: 'd.role',
@@ -232,9 +233,6 @@ export class AuthenticationService {
232
233
  .from('directus_sessions AS s')
233
234
  .leftJoin('directus_users AS u', 's.user', 'u.id')
234
235
  .leftJoin('directus_shares AS d', 's.share', 'd.id')
235
- .leftJoin('directus_roles AS r', (join) => {
236
- join.onIn('r.id', [this.knex.ref('u.role'), this.knex.ref('d.role')]);
237
- })
238
236
  .where('s.token', refreshToken)
239
237
  .andWhere('s.expires', '>=', new Date())
240
238
  .andWhere((subQuery) => {
@@ -258,6 +256,8 @@ export class AuthenticationService {
258
256
  throw new InvalidCredentialsError();
259
257
  }
260
258
  }
259
+ const roles = await fetchRolesTree(record.user_role, this.knex);
260
+ const globalAccess = await fetchGlobalAccess({ user: record.user_id, roles, ip: this.accountability?.ip ?? null }, this.knex);
261
261
  if (record.user_id) {
262
262
  const provider = getAuthProvider(record.user_provider);
263
263
  await provider.refresh({
@@ -270,9 +270,9 @@ export class AuthenticationService {
270
270
  provider: record.user_provider,
271
271
  external_identifier: record.user_external_identifier,
272
272
  auth_data: record.user_auth_data,
273
- role: record.role_id,
274
- app_access: record.role_app_access,
275
- admin_access: record.role_admin_access,
273
+ role: record.user_role,
274
+ app_access: globalAccess.app,
275
+ admin_access: globalAccess.admin,
276
276
  });
277
277
  }
278
278
  let newRefreshToken = record.session_next_token ?? nanoid(64);
@@ -280,9 +280,9 @@ export class AuthenticationService {
280
280
  const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(sessionDuration, 0));
281
281
  const tokenPayload = {
282
282
  id: record.user_id,
283
- role: record.role_id,
284
- app_access: record.role_app_access,
285
- admin_access: record.role_admin_access,
283
+ role: record.user_role,
284
+ app_access: globalAccess.app,
285
+ admin_access: globalAccess.admin,
286
286
  };
287
287
  if (options?.session) {
288
288
  newRefreshToken = await this.updateStatefulSession(record, refreshToken, newRefreshToken, refreshTokenExpiration);
@@ -9,6 +9,8 @@ import { ALIAS_TYPES } from '../constants.js';
9
9
  import { getHelpers } from '../database/helpers/index.js';
10
10
  import getDatabase, { getSchemaInspector } from '../database/index.js';
11
11
  import emitter from '../emitter.js';
12
+ import { fetchAllowedCollections } from '../permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js';
13
+ import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
12
14
  import { getSchema } from '../utils/get-schema.js';
13
15
  import { shouldClearCache } from '../utils/should-clear-cache.js';
14
16
  import { transaction } from '../utils/transaction.js';
@@ -223,11 +225,13 @@ export class CollectionsService {
223
225
  ...meta,
224
226
  [item.collection]: item.group,
225
227
  }), {});
226
- let collectionsYouHavePermissionToRead = this.accountability
227
- .permissions.filter((permission) => {
228
- return permission.action === 'read';
229
- })
230
- .map(({ collection }) => collection);
228
+ let collectionsYouHavePermissionToRead = await fetchAllowedCollections({
229
+ accountability: this.accountability,
230
+ action: 'read',
231
+ }, {
232
+ knex: this.knex,
233
+ schema: this.schema,
234
+ });
231
235
  for (const collection of collectionsYouHavePermissionToRead) {
232
236
  const group = collectionsGroups[collection];
233
237
  if (group)
@@ -279,18 +283,15 @@ export class CollectionsService {
279
283
  * Read many collections by name
280
284
  */
281
285
  async readMany(collectionKeys) {
282
- if (this.accountability && this.accountability.admin !== true) {
283
- const permissions = this.accountability.permissions.filter((permission) => {
284
- return permission.action === 'read' && collectionKeys.includes(permission.collection);
285
- });
286
- if (collectionKeys.length !== permissions.length) {
287
- const collectionsYouHavePermissionToRead = permissions.map(({ collection }) => collection);
288
- for (const collectionKey of collectionKeys) {
289
- if (collectionsYouHavePermissionToRead.includes(collectionKey) === false) {
290
- throw new ForbiddenError();
291
- }
292
- }
293
- }
286
+ if (this.accountability) {
287
+ await Promise.all(collectionKeys.map((collection) => validateAccess({
288
+ accountability: this.accountability,
289
+ action: 'read',
290
+ collection,
291
+ }, {
292
+ schema: this.schema,
293
+ knex: this.knex,
294
+ })));
294
295
  }
295
296
  const collections = await this.readByQuery();
296
297
  return collections.filter(({ collection }) => collectionKeys.includes(collection));
@@ -17,7 +17,6 @@ export declare class FieldsService {
17
17
  cache: Keyv<any> | null;
18
18
  systemCache: Keyv<any>;
19
19
  constructor(options: AbstractServiceOptions);
20
- private get hasReadAccess();
21
20
  readAll(collection?: string): Promise<Field[]>;
22
21
  readOne(collection: string, field: string): Promise<Record<string, any>>;
23
22
  createField(collection: string, field: Partial<Field> & {
@@ -1,4 +1,4 @@
1
- import { KNEX_TYPES, REGEX_BETWEEN_PARENS, DEFAULT_NUMERIC_PRECISION, DEFAULT_NUMERIC_SCALE, } from '@directus/constants';
1
+ import { DEFAULT_NUMERIC_PRECISION, DEFAULT_NUMERIC_SCALE, KNEX_TYPES, REGEX_BETWEEN_PARENS, } from '@directus/constants';
2
2
  import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
3
3
  import { createInspector } from '@directus/schema';
4
4
  import { addFieldFlag, toArray } from '@directus/utils';
@@ -9,6 +9,9 @@ import { translateDatabaseError } from '../database/errors/translate.js';
9
9
  import { getHelpers } from '../database/helpers/index.js';
10
10
  import getDatabase, { getSchemaInspector } from '../database/index.js';
11
11
  import emitter from '../emitter.js';
12
+ import { fetchPermissions } from '../permissions/lib/fetch-permissions.js';
13
+ import { fetchPolicies } from '../permissions/lib/fetch-policies.js';
14
+ import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
12
15
  import getDefaultValue from '../utils/get-default-value.js';
13
16
  import { getSystemFieldRowsWithAuthProviders } from '../utils/get-field-system-rows.js';
14
17
  import getLocalType from '../utils/get-local-type.js';
@@ -42,15 +45,17 @@ export class FieldsService {
42
45
  this.cache = cache;
43
46
  this.systemCache = systemCache;
44
47
  }
45
- get hasReadAccess() {
46
- return !!this.accountability?.permissions?.find((permission) => {
47
- return permission.collection === 'directus_fields' && permission.action === 'read';
48
- });
49
- }
50
48
  async readAll(collection) {
51
49
  let fields;
52
- if (this.accountability && this.accountability.admin !== true && this.hasReadAccess === false) {
53
- throw new ForbiddenError();
50
+ if (this.accountability) {
51
+ await validateAccess({
52
+ accountability: this.accountability,
53
+ action: 'read',
54
+ collection: 'directus_fields',
55
+ }, {
56
+ schema: this.schema,
57
+ knex: this.knex,
58
+ });
54
59
  }
55
60
  const nonAuthorizedItemsService = new ItemsService('directus_fields', {
56
61
  knex: this.knex,
@@ -119,12 +124,27 @@ export class FieldsService {
119
124
  const result = [...columnsWithSystem, ...aliasFieldsAsField].filter((field) => knownCollections.includes(field.collection));
120
125
  // Filter the result so we only return the fields you have read access to
121
126
  if (this.accountability && this.accountability.admin !== true) {
122
- const permissions = this.accountability.permissions.filter((permission) => {
123
- return permission.action === 'read';
124
- });
127
+ const policies = await fetchPolicies(this.accountability, { knex: this.knex, schema: this.schema });
128
+ const permissions = await fetchPermissions(collection
129
+ ? {
130
+ action: 'read',
131
+ policies,
132
+ collections: [collection],
133
+ accountability: this.accountability,
134
+ }
135
+ : {
136
+ action: 'read',
137
+ policies,
138
+ accountability: this.accountability,
139
+ }, { knex: this.knex, schema: this.schema });
125
140
  const allowedFieldsInCollection = {};
126
141
  permissions.forEach((permission) => {
127
- allowedFieldsInCollection[permission.collection] = permission.fields ?? [];
142
+ if (!allowedFieldsInCollection[permission.collection]) {
143
+ allowedFieldsInCollection[permission.collection] = new Set();
144
+ }
145
+ for (const field of permission.fields ?? []) {
146
+ allowedFieldsInCollection[permission.collection].add(field);
147
+ }
128
148
  });
129
149
  if (collection && collection in allowedFieldsInCollection === false) {
130
150
  throw new ForbiddenError();
@@ -133,9 +153,9 @@ export class FieldsService {
133
153
  if (field.collection in allowedFieldsInCollection === false)
134
154
  return false;
135
155
  const allowedFields = allowedFieldsInCollection[field.collection];
136
- if (allowedFields[0] === '*')
156
+ if (allowedFields.has('*'))
137
157
  return true;
138
- return allowedFields.includes(field.field);
158
+ return allowedFields.has(field.field);
139
159
  });
140
160
  }
141
161
  // Update specific database type overrides
@@ -152,18 +172,27 @@ export class FieldsService {
152
172
  }
153
173
  async readOne(collection, field) {
154
174
  if (this.accountability && this.accountability.admin !== true) {
155
- if (this.hasReadAccess === false) {
156
- throw new ForbiddenError();
157
- }
158
- const permissions = this.accountability.permissions.find((permission) => {
159
- return permission.action === 'read' && permission.collection === collection;
175
+ await validateAccess({
176
+ accountability: this.accountability,
177
+ action: 'read',
178
+ collection,
179
+ }, {
180
+ schema: this.schema,
181
+ knex: this.knex,
160
182
  });
161
- if (!permissions || !permissions.fields)
183
+ const policies = await fetchPolicies(this.accountability, { knex: this.knex, schema: this.schema });
184
+ const permissions = await fetchPermissions({ action: 'read', policies, collections: [collection], accountability: this.accountability }, { knex: this.knex, schema: this.schema });
185
+ let hasAccess = false;
186
+ for (const permission of permissions) {
187
+ if (permission.fields) {
188
+ if (permission.fields.includes('*') || permission.fields.includes(field)) {
189
+ hasAccess = true;
190
+ break;
191
+ }
192
+ }
193
+ }
194
+ if (!hasAccess) {
162
195
  throw new ForbiddenError();
163
- if (permissions.fields.includes('*') === false) {
164
- const allowedFields = permissions.fields;
165
- if (allowedFields.includes(field) === false)
166
- throw new ForbiddenError();
167
196
  }
168
197
  }
169
198
  let column = undefined;
@@ -25,10 +25,6 @@ export declare class FilesService extends ItemsService {
25
25
  * Useful for associating metadata with existing file in storage
26
26
  */
27
27
  createOne(data: Partial<File>, opts?: MutationOptions): Promise<PrimaryKey>;
28
- /**
29
- * Delete a file
30
- */
31
- deleteOne(key: PrimaryKey): Promise<PrimaryKey>;
32
28
  /**
33
29
  * Delete multiple files
34
30
  */
@@ -16,6 +16,7 @@ import url from 'url';
16
16
  import { SUPPORTED_IMAGE_METADATA_FORMATS } from '../constants.js';
17
17
  import emitter from '../emitter.js';
18
18
  import { useLogger } from '../logger.js';
19
+ import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
19
20
  import { getAxios } from '../request/index.js';
20
21
  import { getStorage } from '../storage/index.js';
21
22
  import { parseIptc, parseXmp } from '../utils/parse-image-metadata.js';
@@ -276,9 +277,15 @@ export class FilesService extends ItemsService {
276
277
  * Import a single file from an external URL
277
278
  */
278
279
  async importOne(importURL, body) {
279
- const fileCreatePermissions = this.accountability?.permissions?.find((permission) => permission.collection === 'directus_files' && permission.action === 'create');
280
- if (this.accountability && this.accountability?.admin !== true && !fileCreatePermissions) {
281
- throw new ForbiddenError();
280
+ if (this.accountability) {
281
+ await validateAccess({
282
+ accountability: this.accountability,
283
+ action: 'create',
284
+ collection: 'directus_files',
285
+ }, {
286
+ knex: this.knex,
287
+ schema: this.schema,
288
+ });
282
289
  }
283
290
  let fileResponse;
284
291
  try {
@@ -318,13 +325,6 @@ export class FilesService extends ItemsService {
318
325
  const key = await super.createOne(data, opts);
319
326
  return key;
320
327
  }
321
- /**
322
- * Delete a file
323
- */
324
- async deleteOne(key) {
325
- await this.deleteMany([key]);
326
- return key;
327
- }
328
328
  /**
329
329
  * Delete multiple files
330
330
  */
@@ -4,8 +4,6 @@ import { ItemsService } from './items.js';
4
4
  export declare class FlowsService extends ItemsService<FlowRaw> {
5
5
  constructor(options: AbstractServiceOptions);
6
6
  createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
7
- createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
8
- updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
9
7
  updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
10
8
  deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
11
9
  }
@@ -5,34 +5,22 @@ export class FlowsService extends ItemsService {
5
5
  super('directus_flows', options);
6
6
  }
7
7
  async createOne(data, opts) {
8
- const flowManager = getFlowManager();
9
8
  const result = await super.createOne(data, opts);
10
- await flowManager.reload();
11
- return result;
12
- }
13
- async createMany(data, opts) {
14
- const flowManager = getFlowManager();
15
- const result = await super.createMany(data, opts);
16
- await flowManager.reload();
17
- return result;
18
- }
19
- async updateBatch(data, opts) {
20
9
  const flowManager = getFlowManager();
21
- const result = await super.updateBatch(data, opts);
22
10
  await flowManager.reload();
23
11
  return result;
24
12
  }
25
13
  async updateMany(keys, data, opts) {
26
- const flowManager = getFlowManager();
27
14
  const result = await super.updateMany(keys, data, opts);
15
+ const flowManager = getFlowManager();
28
16
  await flowManager.reload();
29
17
  return result;
30
18
  }
31
19
  async deleteMany(keys, opts) {
32
- const flowManager = getFlowManager();
33
20
  // this is to prevent foreign key constraint error on directus_operations resolve/reject during cascade deletion
34
21
  await this.knex('directus_operations').update({ resolve: null, reject: null }).whereIn('flow', keys);
35
22
  const result = await super.deleteMany(keys, opts);
23
+ const flowManager = getFlowManager();
36
24
  await flowManager.reload();
37
25
  return result;
38
26
  }
@@ -20,9 +20,9 @@ export declare class GraphQLService {
20
20
  /**
21
21
  * Generate the GraphQL schema. Pulls from the schema information generated by the get-schema util.
22
22
  */
23
- getSchema(): GraphQLSchema;
24
- getSchema(type: 'schema'): GraphQLSchema;
25
- getSchema(type: 'sdl'): GraphQLSchema | string;
23
+ getSchema(): Promise<GraphQLSchema>;
24
+ getSchema(type: 'schema'): Promise<GraphQLSchema>;
25
+ getSchema(type: 'sdl'): Promise<GraphQLSchema | string>;
26
26
  /**
27
27
  * Generic resolver that's used for every "regular" items/system query. Converts the incoming GraphQL AST / fragments into
28
28
  * Directus' query structure which is then executed by the services.