@directus/api 21.0.0-rc.0 → 21.0.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 +5 -5
  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 +4 -2
  6. package/dist/cache.d.ts +0 -1
  7. package/dist/cache.js +7 -25
  8. package/dist/cli/commands/bootstrap/index.js +2 -8
  9. package/dist/cli/commands/init/index.js +10 -9
  10. package/dist/cli/utils/defaults.d.ts +11 -4
  11. package/dist/cli/utils/defaults.js +1 -7
  12. package/dist/constants.d.ts +1 -1
  13. package/dist/controllers/auth.js +16 -5
  14. package/dist/controllers/permissions.js +2 -14
  15. package/dist/controllers/roles.js +1 -22
  16. package/dist/controllers/tus.js +27 -13
  17. package/dist/controllers/users.js +55 -0
  18. package/dist/database/helpers/fn/types.d.ts +1 -2
  19. package/dist/database/helpers/fn/types.js +1 -1
  20. package/dist/database/helpers/geometry/dialects/mssql.d.ts +1 -1
  21. package/dist/database/helpers/geometry/dialects/mssql.js +2 -4
  22. package/dist/database/helpers/geometry/dialects/mysql.js +1 -1
  23. package/dist/database/helpers/geometry/dialects/oracle.d.ts +1 -1
  24. package/dist/database/helpers/geometry/dialects/oracle.js +3 -5
  25. package/dist/database/helpers/geometry/types.d.ts +1 -1
  26. package/dist/database/helpers/geometry/types.js +2 -4
  27. package/dist/database/index.js +11 -8
  28. package/dist/database/migrations/20240305A-change-useragent-type.js +1 -1
  29. package/dist/database/migrations/20240716A-update-files-date-fields.js +33 -0
  30. package/dist/database/{run-ast/types.d.ts → run-ast.d.ts} +9 -3
  31. package/dist/database/run-ast.js +458 -0
  32. package/dist/flows.js +4 -3
  33. package/dist/logger/index.js +1 -1
  34. package/dist/middleware/authenticate.js +7 -2
  35. package/dist/middleware/cache.js +1 -1
  36. package/dist/middleware/check-ip.d.ts +2 -0
  37. package/dist/middleware/check-ip.js +37 -0
  38. package/dist/middleware/error-handler.d.ts +2 -2
  39. package/dist/middleware/error-handler.js +54 -51
  40. package/dist/middleware/get-permissions.d.ts +3 -0
  41. package/dist/middleware/get-permissions.js +10 -0
  42. package/dist/middleware/respond.js +1 -1
  43. package/dist/services/activity.js +10 -22
  44. package/dist/services/assets.d.ts +3 -2
  45. package/dist/services/assets.js +7 -15
  46. package/dist/services/authentication.js +18 -18
  47. package/dist/services/authorization.d.ts +17 -0
  48. package/dist/services/authorization.js +456 -0
  49. package/dist/services/collections.js +17 -18
  50. package/dist/services/fields.d.ts +4 -0
  51. package/dist/services/fields.js +53 -58
  52. package/dist/services/files/lib/get-sharp-instance.d.ts +2 -0
  53. package/dist/services/files/lib/get-sharp-instance.js +10 -0
  54. package/dist/services/files/utils/get-metadata.js +7 -6
  55. package/dist/services/files.js +8 -10
  56. package/dist/services/graphql/index.d.ts +3 -3
  57. package/dist/services/graphql/index.js +22 -126
  58. package/dist/services/graphql/subscription.js +4 -2
  59. package/dist/services/import-export.js +4 -18
  60. package/dist/services/index.d.ts +2 -3
  61. package/dist/services/index.js +2 -3
  62. package/dist/services/items.js +44 -115
  63. package/dist/services/mail/index.d.ts +1 -1
  64. package/dist/services/mail/index.js +9 -1
  65. package/dist/services/meta.js +23 -60
  66. package/dist/services/notifications.js +6 -14
  67. package/dist/services/payload.d.ts +10 -9
  68. package/dist/services/payload.js +3 -18
  69. package/dist/services/{permissions.d.ts → permissions/index.d.ts} +7 -5
  70. package/dist/services/{permissions.js → permissions/index.js} +54 -30
  71. package/dist/{permissions → services/permissions}/lib/with-app-minimal-permissions.d.ts +1 -1
  72. package/dist/services/permissions/lib/with-app-minimal-permissions.js +13 -0
  73. package/dist/services/relations.d.ts +9 -1
  74. package/dist/services/relations.js +56 -31
  75. package/dist/services/roles.d.ts +12 -4
  76. package/dist/services/roles.js +424 -57
  77. package/dist/services/shares.d.ts +2 -0
  78. package/dist/services/shares.js +8 -12
  79. package/dist/services/specifications.d.ts +2 -2
  80. package/dist/services/specifications.js +27 -39
  81. package/dist/services/tus/data-store.js +4 -5
  82. package/dist/services/tus/server.d.ts +1 -1
  83. package/dist/services/tus/server.js +9 -2
  84. package/dist/services/users.d.ts +5 -1
  85. package/dist/services/users.js +161 -78
  86. package/dist/services/utils.js +7 -11
  87. package/dist/services/versions.d.ts +2 -0
  88. package/dist/services/versions.js +10 -34
  89. package/dist/telemetry/lib/get-report.js +2 -2
  90. package/dist/telemetry/utils/check-increased-user-limits.d.ts +7 -0
  91. package/dist/telemetry/utils/check-increased-user-limits.js +25 -0
  92. package/dist/telemetry/utils/get-role-counts-by-roles.d.ts +6 -0
  93. package/dist/telemetry/utils/get-role-counts-by-roles.js +27 -0
  94. package/dist/telemetry/utils/get-role-counts-by-users.d.ts +11 -0
  95. package/dist/telemetry/utils/get-role-counts-by-users.js +34 -0
  96. package/dist/telemetry/utils/get-user-count.d.ts +8 -0
  97. package/dist/telemetry/utils/get-user-count.js +33 -0
  98. package/dist/telemetry/utils/get-user-counts-by-roles.d.ts +7 -0
  99. package/dist/telemetry/utils/get-user-counts-by-roles.js +35 -0
  100. package/dist/types/ast.d.ts +1 -43
  101. package/dist/types/items.d.ts +0 -11
  102. package/dist/utils/apply-query.d.ts +3 -4
  103. package/dist/utils/apply-query.js +16 -39
  104. package/dist/utils/get-accountability-for-role.js +25 -16
  105. package/dist/utils/get-accountability-for-token.js +16 -17
  106. package/dist/utils/get-ast-from-query.d.ts +13 -0
  107. package/dist/utils/get-ast-from-query.js +297 -0
  108. package/dist/utils/get-cache-key.d.ts +1 -1
  109. package/dist/utils/get-cache-key.js +1 -12
  110. package/dist/utils/get-column.d.ts +1 -2
  111. package/dist/utils/get-column.js +0 -1
  112. package/dist/utils/get-permissions.d.ts +2 -0
  113. package/dist/utils/get-permissions.js +150 -0
  114. package/dist/utils/get-schema.js +3 -3
  115. package/dist/utils/get-service.js +1 -5
  116. package/dist/utils/merge-permissions-for-share.d.ts +4 -0
  117. package/dist/utils/merge-permissions-for-share.js +109 -0
  118. package/dist/utils/merge-permissions.d.ts +3 -0
  119. package/dist/utils/merge-permissions.js +95 -0
  120. package/dist/utils/reduce-schema.d.ts +6 -4
  121. package/dist/utils/reduce-schema.js +32 -16
  122. package/dist/websocket/authenticate.d.ts +2 -0
  123. package/dist/websocket/authenticate.js +12 -0
  124. package/dist/websocket/controllers/graphql.js +4 -1
  125. package/dist/websocket/controllers/hooks.js +0 -4
  126. package/dist/websocket/controllers/rest.js +2 -0
  127. package/dist/websocket/handlers/subscribe.js +2 -0
  128. package/dist/websocket/utils/items.d.ts +1 -1
  129. package/package.json +36 -37
  130. package/dist/controllers/access.d.ts +0 -2
  131. package/dist/controllers/access.js +0 -148
  132. package/dist/controllers/policies.d.ts +0 -2
  133. package/dist/controllers/policies.js +0 -169
  134. package/dist/database/get-ast-from-query/get-ast-from-query.d.ts +0 -16
  135. package/dist/database/get-ast-from-query/get-ast-from-query.js +0 -82
  136. package/dist/database/get-ast-from-query/lib/convert-wildcards.d.ts +0 -13
  137. package/dist/database/get-ast-from-query/lib/convert-wildcards.js +0 -69
  138. package/dist/database/get-ast-from-query/lib/parse-fields.d.ts +0 -15
  139. package/dist/database/get-ast-from-query/lib/parse-fields.js +0 -190
  140. package/dist/database/get-ast-from-query/utils/get-deep-query.d.ts +0 -14
  141. package/dist/database/get-ast-from-query/utils/get-deep-query.js +0 -17
  142. package/dist/database/get-ast-from-query/utils/get-related-collection.d.ts +0 -2
  143. package/dist/database/get-ast-from-query/utils/get-related-collection.js +0 -13
  144. package/dist/database/get-ast-from-query/utils/get-relation.d.ts +0 -2
  145. package/dist/database/get-ast-from-query/utils/get-relation.js +0 -7
  146. package/dist/database/migrations/20240710A-permissions-policies.js +0 -169
  147. package/dist/database/run-ast/lib/get-db-query.d.ts +0 -4
  148. package/dist/database/run-ast/lib/get-db-query.js +0 -208
  149. package/dist/database/run-ast/lib/parse-current-level.d.ts +0 -7
  150. package/dist/database/run-ast/lib/parse-current-level.js +0 -41
  151. package/dist/database/run-ast/run-ast.d.ts +0 -7
  152. package/dist/database/run-ast/run-ast.js +0 -107
  153. package/dist/database/run-ast/types.js +0 -1
  154. package/dist/database/run-ast/utils/apply-case-when.d.ts +0 -16
  155. package/dist/database/run-ast/utils/apply-case-when.js +0 -26
  156. package/dist/database/run-ast/utils/apply-parent-filters.d.ts +0 -3
  157. package/dist/database/run-ast/utils/apply-parent-filters.js +0 -55
  158. package/dist/database/run-ast/utils/get-column-pre-processor.d.ts +0 -10
  159. package/dist/database/run-ast/utils/get-column-pre-processor.js +0 -57
  160. package/dist/database/run-ast/utils/get-field-alias.d.ts +0 -2
  161. package/dist/database/run-ast/utils/get-field-alias.js +0 -4
  162. package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.d.ts +0 -5
  163. package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.js +0 -23
  164. package/dist/database/run-ast/utils/merge-with-parent-items.d.ts +0 -3
  165. package/dist/database/run-ast/utils/merge-with-parent-items.js +0 -87
  166. package/dist/database/run-ast/utils/remove-temporary-fields.d.ts +0 -3
  167. package/dist/database/run-ast/utils/remove-temporary-fields.js +0 -73
  168. package/dist/permissions/cache.d.ts +0 -2
  169. package/dist/permissions/cache.js +0 -23
  170. package/dist/permissions/lib/fetch-permissions.d.ts +0 -10
  171. package/dist/permissions/lib/fetch-permissions.js +0 -55
  172. package/dist/permissions/lib/fetch-policies.d.ts +0 -7
  173. package/dist/permissions/lib/fetch-policies.js +0 -28
  174. package/dist/permissions/lib/fetch-roles-tree.d.ts +0 -3
  175. package/dist/permissions/lib/fetch-roles-tree.js +0 -28
  176. package/dist/permissions/lib/with-app-minimal-permissions.js +0 -10
  177. package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.d.ts +0 -7
  178. package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js +0 -56
  179. package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.d.ts +0 -3
  180. package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js +0 -16
  181. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.d.ts +0 -8
  182. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +0 -24
  183. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.d.ts +0 -9
  184. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +0 -31
  185. package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.d.ts +0 -16
  186. package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js +0 -27
  187. package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +0 -10
  188. package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +0 -23
  189. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +0 -5
  190. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +0 -7
  191. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +0 -5
  192. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +0 -10
  193. package/dist/permissions/modules/fetch-global-access/types.d.ts +0 -4
  194. package/dist/permissions/modules/fetch-global-access/types.js +0 -1
  195. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +0 -4
  196. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +0 -27
  197. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.d.ts +0 -12
  198. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +0 -32
  199. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +0 -4
  200. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.js +0 -29
  201. package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.d.ts +0 -4
  202. package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.js +0 -49
  203. package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.d.ts +0 -3
  204. package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.js +0 -56
  205. package/dist/permissions/modules/process-ast/lib/field-map-from-ast.d.ts +0 -4
  206. package/dist/permissions/modules/process-ast/lib/field-map-from-ast.js +0 -8
  207. package/dist/permissions/modules/process-ast/lib/inject-cases.d.ts +0 -9
  208. package/dist/permissions/modules/process-ast/lib/inject-cases.js +0 -93
  209. package/dist/permissions/modules/process-ast/process-ast.d.ts +0 -9
  210. package/dist/permissions/modules/process-ast/process-ast.js +0 -39
  211. package/dist/permissions/modules/process-ast/types.d.ts +0 -24
  212. package/dist/permissions/modules/process-ast/types.js +0 -1
  213. package/dist/permissions/modules/process-ast/utils/collections-in-field-map.d.ts +0 -2
  214. package/dist/permissions/modules/process-ast/utils/collections-in-field-map.js +0 -7
  215. package/dist/permissions/modules/process-ast/utils/dedupe-access.d.ts +0 -12
  216. package/dist/permissions/modules/process-ast/utils/dedupe-access.js +0 -30
  217. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.d.ts +0 -15
  218. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +0 -50
  219. package/dist/permissions/modules/process-ast/utils/find-related-collection.d.ts +0 -3
  220. package/dist/permissions/modules/process-ast/utils/find-related-collection.js +0 -9
  221. package/dist/permissions/modules/process-ast/utils/flatten-filter.d.ts +0 -3
  222. package/dist/permissions/modules/process-ast/utils/flatten-filter.js +0 -34
  223. package/dist/permissions/modules/process-ast/utils/format-a2o-key.d.ts +0 -1
  224. package/dist/permissions/modules/process-ast/utils/format-a2o-key.js +0 -3
  225. package/dist/permissions/modules/process-ast/utils/get-info-for-path.d.ts +0 -5
  226. package/dist/permissions/modules/process-ast/utils/get-info-for-path.js +0 -7
  227. package/dist/permissions/modules/process-ast/utils/has-item-permissions.d.ts +0 -2
  228. package/dist/permissions/modules/process-ast/utils/has-item-permissions.js +0 -3
  229. package/dist/permissions/modules/process-ast/utils/stringify-query-path.d.ts +0 -2
  230. package/dist/permissions/modules/process-ast/utils/stringify-query-path.js +0 -3
  231. package/dist/permissions/modules/process-ast/utils/validate-path/create-error.d.ts +0 -3
  232. package/dist/permissions/modules/process-ast/utils/validate-path/create-error.js +0 -16
  233. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.d.ts +0 -2
  234. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.js +0 -12
  235. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.d.ts +0 -2
  236. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.js +0 -28
  237. package/dist/permissions/modules/process-payload/lib/is-field-nullable.d.ts +0 -5
  238. package/dist/permissions/modules/process-payload/lib/is-field-nullable.js +0 -12
  239. package/dist/permissions/modules/process-payload/process-payload.d.ts +0 -13
  240. package/dist/permissions/modules/process-payload/process-payload.js +0 -77
  241. package/dist/permissions/modules/validate-access/lib/validate-collection-access.d.ts +0 -12
  242. package/dist/permissions/modules/validate-access/lib/validate-collection-access.js +0 -11
  243. package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +0 -9
  244. package/dist/permissions/modules/validate-access/lib/validate-item-access.js +0 -33
  245. package/dist/permissions/modules/validate-access/validate-access.d.ts +0 -14
  246. package/dist/permissions/modules/validate-access/validate-access.js +0 -28
  247. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.d.ts +0 -1
  248. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.js +0 -8
  249. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.d.ts +0 -5
  250. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.js +0 -10
  251. package/dist/permissions/types.d.ts +0 -6
  252. package/dist/permissions/types.js +0 -1
  253. package/dist/permissions/utils/create-default-accountability.d.ts +0 -2
  254. package/dist/permissions/utils/create-default-accountability.js +0 -11
  255. package/dist/permissions/utils/extract-required-dynamic-variable-context.d.ts +0 -8
  256. package/dist/permissions/utils/extract-required-dynamic-variable-context.js +0 -27
  257. package/dist/permissions/utils/fetch-dynamic-variable-context.d.ts +0 -9
  258. package/dist/permissions/utils/fetch-dynamic-variable-context.js +0 -43
  259. package/dist/permissions/utils/filter-policies-by-ip.d.ts +0 -2
  260. package/dist/permissions/utils/filter-policies-by-ip.js +0 -15
  261. package/dist/permissions/utils/get-unaliased-field-key.d.ts +0 -5
  262. package/dist/permissions/utils/get-unaliased-field-key.js +0 -17
  263. package/dist/permissions/utils/process-permissions.d.ts +0 -7
  264. package/dist/permissions/utils/process-permissions.js +0 -9
  265. package/dist/permissions/utils/with-cache.d.ts +0 -10
  266. package/dist/permissions/utils/with-cache.js +0 -25
  267. package/dist/services/access.d.ts +0 -10
  268. package/dist/services/access.js +0 -43
  269. package/dist/services/policies.d.ts +0 -12
  270. package/dist/services/policies.js +0 -87
  271. package/dist/telemetry/utils/check-user-limits.d.ts +0 -5
  272. package/dist/telemetry/utils/check-user-limits.js +0 -19
  273. package/dist/utils/fetch-user-count/fetch-access-lookup.d.ts +0 -17
  274. package/dist/utils/fetch-user-count/fetch-access-lookup.js +0 -22
  275. package/dist/utils/fetch-user-count/fetch-access-roles.d.ts +0 -16
  276. package/dist/utils/fetch-user-count/fetch-access-roles.js +0 -37
  277. package/dist/utils/fetch-user-count/fetch-active-users.d.ts +0 -6
  278. package/dist/utils/fetch-user-count/fetch-active-users.js +0 -3
  279. package/dist/utils/fetch-user-count/fetch-user-count.d.ts +0 -12
  280. package/dist/utils/fetch-user-count/fetch-user-count.js +0 -57
  281. package/dist/utils/fetch-user-count/get-user-count-query.d.ts +0 -20
  282. package/dist/utils/fetch-user-count/get-user-count-query.js +0 -17
  283. package/dist/utils/validate-user-count-integrity.d.ts +0 -13
  284. package/dist/utils/validate-user-count-integrity.js +0 -29
  285. /package/dist/database/migrations/{20240710A-permissions-policies.d.ts → 20240716A-update-files-date-fields.d.ts} +0 -0
@@ -1,17 +1,14 @@
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';
5
4
  import { version } from 'directus/version';
6
5
  import { cloneDeep, mergeWith } from 'lodash-es';
7
6
  import { OAS_REQUIRED_SCHEMAS } from '../constants.js';
8
7
  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';
12
8
  import { getRelationType } from '../utils/get-relation-type.js';
13
9
  import { reduceSchema } from '../utils/reduce-schema.js';
14
10
  import { GraphQLService } from './graphql/index.js';
11
+ import { isSystemCollection } from '@directus/system-data';
15
12
  const env = useEnv();
16
13
  export class SpecificationService {
17
14
  accountability;
@@ -19,38 +16,29 @@ export class SpecificationService {
19
16
  schema;
20
17
  oas;
21
18
  graphql;
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);
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 });
28
25
  }
29
26
  }
30
27
  class OASSpecsService {
31
28
  accountability;
32
29
  knex;
33
30
  schema;
34
- constructor(options) {
35
- this.accountability = options.accountability || null;
36
- this.knex = options.knex || getDatabase();
37
- this.schema = options.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);
38
36
  }
39
37
  async generate(host) {
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);
38
+ const permissions = this.accountability?.permissions ?? [];
39
+ const tags = await this.generateTags();
52
40
  const paths = await this.generatePaths(permissions, tags);
53
- const components = await this.generateComponents(schema, tags);
41
+ const components = await this.generateComponents(tags);
54
42
  const isDefaultPublicUrl = env['PUBLIC_URL'] === '/';
55
43
  const url = isDefaultPublicUrl && host ? host : env['PUBLIC_URL'];
56
44
  const spec = {
@@ -74,9 +62,9 @@ class OASSpecsService {
74
62
  spec.components = components;
75
63
  return spec;
76
64
  }
77
- async generateTags(schema) {
65
+ async generateTags() {
78
66
  const systemTags = cloneDeep(spec.tags);
79
- const collections = Object.values(schema.collections);
67
+ const collections = Object.values(this.schema.collections);
80
68
  const tags = [];
81
69
  for (const systemTag of systemTags) {
82
70
  // Check if necessary authentication level is given
@@ -258,7 +246,7 @@ class OASSpecsService {
258
246
  }
259
247
  return paths;
260
248
  }
261
- async generateComponents(schema, tags) {
249
+ async generateComponents(tags) {
262
250
  if (!tags)
263
251
  return;
264
252
  let components = cloneDeep(spec.components);
@@ -276,7 +264,7 @@ class OASSpecsService {
276
264
  };
277
265
  }
278
266
  }
279
- const collections = Object.values(schema.collections);
267
+ const collections = Object.values(this.schema.collections);
280
268
  for (const collection of collections) {
281
269
  const tag = tags.find((tag) => tag['x-collection'] === collection.collection);
282
270
  if (!tag)
@@ -289,7 +277,7 @@ class OASSpecsService {
289
277
  schemaComponent['x-collection'] = collection.collection;
290
278
  for (const field of fieldsInCollection) {
291
279
  schemaComponent.properties[field.field] =
292
- cloneDeep(spec.components.schemas[tag.name].properties[field.field]) || this.generateField(schema, collection.collection, field, tags);
280
+ cloneDeep(spec.components.schemas[tag.name].properties[field.field]) || this.generateField(collection.collection, field, tags);
293
281
  }
294
282
  components.schemas[tag.name] = schemaComponent;
295
283
  }
@@ -300,7 +288,7 @@ class OASSpecsService {
300
288
  'x-collection': collection.collection,
301
289
  };
302
290
  for (const field of fieldsInCollection) {
303
- schemaComponent.properties[field.field] = this.generateField(schema, collection.collection, field, tags);
291
+ schemaComponent.properties[field.field] = this.generateField(collection.collection, field, tags);
304
292
  }
305
293
  components.schemas[tag.name] = schemaComponent;
306
294
  }
@@ -323,13 +311,13 @@ class OASSpecsService {
323
311
  return 'read';
324
312
  }
325
313
  }
326
- generateField(schema, collection, field, tags) {
314
+ generateField(collection, field, tags) {
327
315
  let propertyObject = {};
328
316
  propertyObject.nullable = field.nullable;
329
317
  if (field.note) {
330
318
  propertyObject.description = field.note;
331
319
  }
332
- const relation = schema.relations.find((relation) => (relation.collection === collection && relation.field === field.field) ||
320
+ const relation = this.schema.relations.find((relation) => (relation.collection === collection && relation.field === field.field) ||
333
321
  (relation.related_collection === collection && relation.meta?.one_field === field.field));
334
322
  if (!relation) {
335
323
  propertyObject = {
@@ -347,10 +335,10 @@ class OASSpecsService {
347
335
  const relatedTag = tags.find((tag) => tag['x-collection'] === relation.related_collection);
348
336
  if (!relatedTag ||
349
337
  !relation.related_collection ||
350
- relation.related_collection in schema.collections === false) {
338
+ relation.related_collection in this.schema.collections === false) {
351
339
  return propertyObject;
352
340
  }
353
- const relatedCollection = schema.collections[relation.related_collection];
341
+ const relatedCollection = this.schema.collections[relation.related_collection];
354
342
  const relatedPrimaryKeyField = relatedCollection.fields[relatedCollection.primary];
355
343
  propertyObject.oneOf = [
356
344
  {
@@ -363,10 +351,10 @@ class OASSpecsService {
363
351
  }
364
352
  else if (relationType === 'o2m') {
365
353
  const relatedTag = tags.find((tag) => tag['x-collection'] === relation.collection);
366
- if (!relatedTag || !relation.related_collection || relation.collection in schema.collections === false) {
354
+ if (!relatedTag || !relation.related_collection || relation.collection in this.schema.collections === false) {
367
355
  return propertyObject;
368
356
  }
369
- const relatedCollection = schema.collections[relation.collection];
357
+ const relatedCollection = this.schema.collections[relation.collection];
370
358
  const relatedPrimaryKeyField = relatedCollection.fields[relatedCollection.primary];
371
359
  if (!relatedTag || !relatedPrimaryKeyField)
372
360
  return propertyObject;
@@ -79,16 +79,15 @@ export class TusDataStore extends DataStore {
79
79
  }
80
80
  // If this is a new file upload, we need to generate a new primary key and DB record
81
81
  const primaryKey = await itemsService.createOne(fileData, { emitEvents: false });
82
+ // Set the file id, so it is available to be sent as a header on upload creation / resume
83
+ if (!upload.metadata['id']) {
84
+ upload.metadata['id'] = primaryKey;
85
+ }
82
86
  const fileExtension = extname(upload.metadata['filename_download']) ||
83
87
  (upload.metadata['type'] && '.' + extension(upload.metadata['type'])) ||
84
88
  '';
85
89
  // The filename_disk is the FINAL filename on disk
86
90
  fileData.filename_disk ||= primaryKey + (fileExtension || '');
87
- // Temp filename is used for replacements
88
- // const tempFilenameDisk = fileData.tus_id! + (fileExtension || '');
89
- // if (isReplacement) {
90
- // upload.metadata['temp_file'] = tempFilenameDisk;
91
- // }
92
91
  try {
93
92
  // If this is a replacement, we'll write the file to a temp location first to ensure we don't overwrite the existing file if something goes wrong
94
93
  upload = (await this.storageDriver.createChunkedUpload(fileData.filename_disk, upload));
@@ -4,5 +4,5 @@ type Context = {
4
4
  schema: SchemaOverview;
5
5
  accountability?: Accountability | undefined;
6
6
  };
7
- export declare function createTusServer(context: Context): Promise<Server>;
7
+ export declare function createTusServer(context: Context): Promise<[Server, () => void]>;
8
8
  export {};
@@ -6,7 +6,7 @@
6
6
  import { useEnv } from '@directus/env';
7
7
  import { supportsTus } from '@directus/storage';
8
8
  import { toArray } from '@directus/utils';
9
- import { Server } from '@tus/server';
9
+ import { Server, EVENTS } from '@tus/server';
10
10
  import { RESUMABLE_UPLOADS } from '../../constants.js';
11
11
  import { getStorage } from '../../storage/index.js';
12
12
  import { extractMetadata } from '../files/lib/extract-metadata.js';
@@ -33,7 +33,7 @@ async function createTusStore(context) {
33
33
  export async function createTusServer(context) {
34
34
  const env = useEnv();
35
35
  const store = await createTusStore(context);
36
- return new Server({
36
+ const server = new Server({
37
37
  path: '/files/tus',
38
38
  datastore: store,
39
39
  locker: getTusLocker(),
@@ -77,4 +77,11 @@ export async function createTusServer(context) {
77
77
  },
78
78
  relativeLocation: String(env['PUBLIC_URL']).startsWith('http'),
79
79
  });
80
+ server.on(EVENTS.POST_CREATE, async (_req, res, upload) => {
81
+ res.setHeader('Directus-File-Id', upload.metadata['id']);
82
+ });
83
+ return [server, cleanup];
84
+ function cleanup() {
85
+ server.removeAllListeners();
86
+ }
80
87
  }
@@ -13,6 +13,11 @@ 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;
16
21
  /**
17
22
  * Get basic information of user identified by email
18
23
  */
@@ -47,5 +52,4 @@ export declare class UsersService extends ItemsService {
47
52
  verifyRegistration(token: string): Promise<string>;
48
53
  requestPasswordReset(email: string, url: string | null, subject?: string | null): Promise<void>;
49
54
  resetPassword(token: string, password: string): Promise<void>;
50
- private clearCaches;
51
55
  }
@@ -1,22 +1,23 @@
1
1
  import { useEnv } from '@directus/env';
2
- import { ForbiddenError, InvalidPayloadError, RecordNotUniqueError } from '@directus/errors';
3
- import { getSimpleHash, toArray, validatePayload } from '@directus/utils';
2
+ import { ForbiddenError, InvalidPayloadError, RecordNotUniqueError, UnprocessableContentError } from '@directus/errors';
3
+ import { getSimpleHash, toArray, toBoolean, 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 } from 'lodash-es';
7
+ import { isEmpty, mergeWith } from 'lodash-es';
8
8
  import { performance } from 'perf_hooks';
9
- import { clearSystemCache } from '../cache.js';
10
9
  import getDatabase from '../database/index.js';
11
10
  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';
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';
14
16
  import { getSecret } from '../utils/get-secret.js';
15
17
  import isUrlAllowed from '../utils/is-url-allowed.js';
16
18
  import { verifyJWT } from '../utils/jwt.js';
17
19
  import { stall } from '../utils/stall.js';
18
20
  import { Url } from '../utils/url.js';
19
- import { UserIntegrityCheckFlag } from '../utils/validate-user-count-integrity.js';
20
21
  import { ItemsService } from './items.js';
21
22
  import { MailService } from './mail/index.js';
22
23
  import { SettingsService } from './settings.js';
@@ -87,11 +88,42 @@ export class UsersService extends ItemsService {
87
88
  }
88
89
  }
89
90
  }
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
+ }
90
122
  /**
91
123
  * Get basic information of user identified by email
92
124
  */
93
125
  async getUserByEmail(email) {
94
- return this.knex
126
+ return await this.knex
95
127
  .select('id', 'role', 'status', 'password', 'email')
96
128
  .from('directus_users')
97
129
  .whereRaw(`LOWER(??) = ?`, ['email', email.toLowerCase()])
@@ -126,34 +158,51 @@ export class UsersService extends ItemsService {
126
158
  /**
127
159
  * Create a new user
128
160
  */
129
- async createOne(data, opts = {}) {
161
+ async createOne(data, opts) {
130
162
  try {
131
- if ('email' in data) {
163
+ if (data['email']) {
132
164
  this.validateEmail(data['email']);
133
165
  await this.checkUniqueEmails([data['email']]);
134
166
  }
135
- if ('password' in data) {
167
+ if (data['password']) {
136
168
  await this.checkPasswordPolicy([data['password']]);
137
169
  }
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
+ }
138
193
  }
139
194
  catch (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);
195
+ (opts || (opts = {})).preMutationError = err;
147
196
  }
148
197
  return await super.createOne(data, opts);
149
198
  }
150
199
  /**
151
200
  * Create multiple new users
152
201
  */
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');
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);
157
206
  try {
158
207
  if (emails.length) {
159
208
  this.validateEmail(emails);
@@ -162,30 +211,96 @@ export class UsersService extends ItemsService {
162
211
  if (passwords.length) {
163
212
  await this.checkPasswordPolicy(passwords);
164
213
  }
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
+ }
165
241
  }
166
242
  catch (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);
243
+ (opts || (opts = {})).preMutationError = err;
174
244
  }
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);
245
+ return await super.createMany(data, opts);
183
246
  }
184
247
  /**
185
248
  * Update many users by primary key
186
249
  */
187
- async updateMany(keys, data, opts = {}) {
250
+ async updateMany(keys, data, opts) {
188
251
  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
+ }
189
304
  if (data['email']) {
190
305
  if (keys.length > 1) {
191
306
  throw new RecordNotUniqueError({
@@ -216,45 +331,19 @@ export class UsersService extends ItemsService {
216
331
  }
217
332
  }
218
333
  catch (err) {
219
- opts.preMutationError = err;
220
- }
221
- if ('role' in data) {
222
- opts.userIntegrityCheckFlags = UserIntegrityCheckFlag.All;
334
+ (opts || (opts = {})).preMutationError = err;
223
335
  }
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;
336
+ return await super.updateMany(keys, data, opts);
243
337
  }
244
338
  /**
245
339
  * Delete multiple users by primary key
246
340
  */
247
- async deleteMany(keys, opts = {}) {
248
- if (opts?.onRequireUserIntegrityCheck) {
249
- opts.onRequireUserIntegrityCheck(opts?.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None);
341
+ async deleteMany(keys, opts) {
342
+ try {
343
+ await this.checkRemainingAdminExistence(keys);
250
344
  }
251
- else {
252
- try {
253
- await validateRemainingAdminUsers({ excludeUsers: keys }, { knex: this.knex, schema: this.schema });
254
- }
255
- catch (err) {
256
- opts.preMutationError = err;
257
- }
345
+ catch (err) {
346
+ (opts || (opts = {})).preMutationError = err;
258
347
  }
259
348
  // Manual constraint, see https://github.com/directus/directus/pull/19912
260
349
  await this.knex('directus_notifications').update({ sender: null }).whereIn('sender', keys);
@@ -477,16 +566,10 @@ export class UsersService extends ItemsService {
477
566
  knex: this.knex,
478
567
  schema: this.schema,
479
568
  accountability: {
480
- ...(this.accountability ?? createDefaultAccountability()),
569
+ ...(this.accountability ?? { role: null }),
481
570
  admin: true, // We need to skip permissions checks for the update call below
482
571
  },
483
572
  });
484
573
  await service.updateOne(user.id, { password, status: 'active' }, opts);
485
574
  }
486
- async clearCaches(opts) {
487
- await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
488
- if (this.cache && opts?.autoPurgeCache !== false) {
489
- await this.cache.clear();
490
- }
491
- }
492
575
  }
@@ -3,8 +3,6 @@ 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';
8
6
  import { shouldClearCache } from '../utils/should-clear-cache.js';
9
7
  export class UtilsService {
10
8
  knex;
@@ -22,16 +20,14 @@ export class UtilsService {
22
20
  if (!sortField) {
23
21
  throw new InvalidPayloadError({ reason: `Collection "${collection}" doesn't have a sort field` });
24
22
  }
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,
23
+ if (this.accountability?.admin !== true) {
24
+ const permissions = this.accountability?.permissions?.find((permission) => {
25
+ return permission.collection === collection && permission.action === 'update';
33
26
  });
34
- const allowedFields = await fetchAllowedFields({ collection, action: 'update', accountability: this.accountability }, { schema: this.schema, knex: this.knex });
27
+ if (!permissions) {
28
+ throw new ForbiddenError();
29
+ }
30
+ const allowedFields = permissions.fields ?? [];
35
31
  if (allowedFields[0] !== '*' && allowedFields.includes(sortField) === false) {
36
32
  throw new ForbiddenError();
37
33
  }
@@ -1,7 +1,9 @@
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';
3
4
  import { ItemsService } from './items.js';
4
5
  export declare class VersionsService extends ItemsService {
6
+ authorizationService: AuthorizationService;
5
7
  constructor(options: AbstractServiceOptions);
6
8
  private validateCreateData;
7
9
  getMainItem(collection: string, item: PrimaryKey, query?: Query): Promise<Item>;