@directus/api 21.0.0-rc.0 → 21.0.1

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 (286) 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 +2 -3
  7. package/dist/cache.js +9 -27
  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 +2 -1
  43. package/dist/request/is-denied-ip.js +7 -1
  44. package/dist/services/activity.js +10 -22
  45. package/dist/services/assets.d.ts +3 -2
  46. package/dist/services/assets.js +7 -15
  47. package/dist/services/authentication.js +18 -18
  48. package/dist/services/authorization.d.ts +17 -0
  49. package/dist/services/authorization.js +456 -0
  50. package/dist/services/collections.js +17 -18
  51. package/dist/services/fields.d.ts +4 -0
  52. package/dist/services/fields.js +53 -58
  53. package/dist/services/files/lib/get-sharp-instance.d.ts +2 -0
  54. package/dist/services/files/lib/get-sharp-instance.js +10 -0
  55. package/dist/services/files/utils/get-metadata.js +7 -6
  56. package/dist/services/files.js +8 -10
  57. package/dist/services/graphql/index.d.ts +3 -3
  58. package/dist/services/graphql/index.js +22 -126
  59. package/dist/services/graphql/subscription.js +4 -2
  60. package/dist/services/import-export.js +4 -18
  61. package/dist/services/index.d.ts +2 -3
  62. package/dist/services/index.js +2 -3
  63. package/dist/services/items.js +44 -115
  64. package/dist/services/mail/index.d.ts +1 -1
  65. package/dist/services/mail/index.js +9 -1
  66. package/dist/services/meta.js +23 -60
  67. package/dist/services/notifications.js +6 -14
  68. package/dist/services/payload.d.ts +10 -9
  69. package/dist/services/payload.js +3 -18
  70. package/dist/services/{permissions.d.ts → permissions/index.d.ts} +7 -5
  71. package/dist/services/{permissions.js → permissions/index.js} +54 -30
  72. package/dist/{permissions → services/permissions}/lib/with-app-minimal-permissions.d.ts +1 -1
  73. package/dist/services/permissions/lib/with-app-minimal-permissions.js +13 -0
  74. package/dist/services/relations.d.ts +9 -1
  75. package/dist/services/relations.js +56 -31
  76. package/dist/services/roles.d.ts +12 -4
  77. package/dist/services/roles.js +424 -57
  78. package/dist/services/shares.d.ts +2 -0
  79. package/dist/services/shares.js +8 -12
  80. package/dist/services/specifications.d.ts +2 -2
  81. package/dist/services/specifications.js +27 -39
  82. package/dist/services/tus/data-store.js +4 -5
  83. package/dist/services/tus/server.d.ts +1 -1
  84. package/dist/services/tus/server.js +9 -2
  85. package/dist/services/users.d.ts +5 -1
  86. package/dist/services/users.js +161 -78
  87. package/dist/services/utils.js +7 -11
  88. package/dist/services/versions.d.ts +2 -0
  89. package/dist/services/versions.js +10 -34
  90. package/dist/telemetry/lib/get-report.js +2 -2
  91. package/dist/telemetry/utils/check-increased-user-limits.d.ts +7 -0
  92. package/dist/telemetry/utils/check-increased-user-limits.js +25 -0
  93. package/dist/telemetry/utils/get-role-counts-by-roles.d.ts +6 -0
  94. package/dist/telemetry/utils/get-role-counts-by-roles.js +27 -0
  95. package/dist/telemetry/utils/get-role-counts-by-users.d.ts +11 -0
  96. package/dist/telemetry/utils/get-role-counts-by-users.js +34 -0
  97. package/dist/telemetry/utils/get-user-count.d.ts +8 -0
  98. package/dist/telemetry/utils/get-user-count.js +33 -0
  99. package/dist/telemetry/utils/get-user-counts-by-roles.d.ts +7 -0
  100. package/dist/telemetry/utils/get-user-counts-by-roles.js +35 -0
  101. package/dist/types/ast.d.ts +1 -43
  102. package/dist/types/items.d.ts +0 -11
  103. package/dist/utils/apply-query.d.ts +3 -4
  104. package/dist/utils/apply-query.js +16 -39
  105. package/dist/utils/get-accountability-for-role.js +25 -16
  106. package/dist/utils/get-accountability-for-token.js +16 -17
  107. package/dist/utils/get-ast-from-query.d.ts +13 -0
  108. package/dist/utils/get-ast-from-query.js +297 -0
  109. package/dist/utils/get-cache-key.d.ts +1 -1
  110. package/dist/utils/get-cache-key.js +1 -12
  111. package/dist/utils/get-column.d.ts +1 -2
  112. package/dist/utils/get-column.js +0 -1
  113. package/dist/utils/get-permissions.d.ts +2 -0
  114. package/dist/utils/get-permissions.js +150 -0
  115. package/dist/utils/get-schema.js +22 -27
  116. package/dist/utils/get-service.js +1 -5
  117. package/dist/utils/merge-permissions-for-share.d.ts +4 -0
  118. package/dist/utils/merge-permissions-for-share.js +109 -0
  119. package/dist/utils/merge-permissions.d.ts +3 -0
  120. package/dist/utils/merge-permissions.js +95 -0
  121. package/dist/utils/reduce-schema.d.ts +6 -4
  122. package/dist/utils/reduce-schema.js +32 -16
  123. package/dist/websocket/authenticate.d.ts +2 -0
  124. package/dist/websocket/authenticate.js +12 -0
  125. package/dist/websocket/controllers/graphql.js +4 -1
  126. package/dist/websocket/controllers/hooks.js +0 -4
  127. package/dist/websocket/controllers/rest.js +2 -0
  128. package/dist/websocket/handlers/subscribe.js +2 -0
  129. package/dist/websocket/utils/items.d.ts +1 -1
  130. package/package.json +35 -36
  131. package/dist/controllers/access.d.ts +0 -2
  132. package/dist/controllers/access.js +0 -148
  133. package/dist/controllers/policies.d.ts +0 -2
  134. package/dist/controllers/policies.js +0 -169
  135. package/dist/database/get-ast-from-query/get-ast-from-query.d.ts +0 -16
  136. package/dist/database/get-ast-from-query/get-ast-from-query.js +0 -82
  137. package/dist/database/get-ast-from-query/lib/convert-wildcards.d.ts +0 -13
  138. package/dist/database/get-ast-from-query/lib/convert-wildcards.js +0 -69
  139. package/dist/database/get-ast-from-query/lib/parse-fields.d.ts +0 -15
  140. package/dist/database/get-ast-from-query/lib/parse-fields.js +0 -190
  141. package/dist/database/get-ast-from-query/utils/get-deep-query.d.ts +0 -14
  142. package/dist/database/get-ast-from-query/utils/get-deep-query.js +0 -17
  143. package/dist/database/get-ast-from-query/utils/get-related-collection.d.ts +0 -2
  144. package/dist/database/get-ast-from-query/utils/get-related-collection.js +0 -13
  145. package/dist/database/get-ast-from-query/utils/get-relation.d.ts +0 -2
  146. package/dist/database/get-ast-from-query/utils/get-relation.js +0 -7
  147. package/dist/database/migrations/20240710A-permissions-policies.js +0 -169
  148. package/dist/database/run-ast/lib/get-db-query.d.ts +0 -4
  149. package/dist/database/run-ast/lib/get-db-query.js +0 -208
  150. package/dist/database/run-ast/lib/parse-current-level.d.ts +0 -7
  151. package/dist/database/run-ast/lib/parse-current-level.js +0 -41
  152. package/dist/database/run-ast/run-ast.d.ts +0 -7
  153. package/dist/database/run-ast/run-ast.js +0 -107
  154. package/dist/database/run-ast/types.js +0 -1
  155. package/dist/database/run-ast/utils/apply-case-when.d.ts +0 -16
  156. package/dist/database/run-ast/utils/apply-case-when.js +0 -26
  157. package/dist/database/run-ast/utils/apply-parent-filters.d.ts +0 -3
  158. package/dist/database/run-ast/utils/apply-parent-filters.js +0 -55
  159. package/dist/database/run-ast/utils/get-column-pre-processor.d.ts +0 -10
  160. package/dist/database/run-ast/utils/get-column-pre-processor.js +0 -57
  161. package/dist/database/run-ast/utils/get-field-alias.d.ts +0 -2
  162. package/dist/database/run-ast/utils/get-field-alias.js +0 -4
  163. package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.d.ts +0 -5
  164. package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.js +0 -23
  165. package/dist/database/run-ast/utils/merge-with-parent-items.d.ts +0 -3
  166. package/dist/database/run-ast/utils/merge-with-parent-items.js +0 -87
  167. package/dist/database/run-ast/utils/remove-temporary-fields.d.ts +0 -3
  168. package/dist/database/run-ast/utils/remove-temporary-fields.js +0 -73
  169. package/dist/permissions/cache.d.ts +0 -2
  170. package/dist/permissions/cache.js +0 -23
  171. package/dist/permissions/lib/fetch-permissions.d.ts +0 -10
  172. package/dist/permissions/lib/fetch-permissions.js +0 -55
  173. package/dist/permissions/lib/fetch-policies.d.ts +0 -7
  174. package/dist/permissions/lib/fetch-policies.js +0 -28
  175. package/dist/permissions/lib/fetch-roles-tree.d.ts +0 -3
  176. package/dist/permissions/lib/fetch-roles-tree.js +0 -28
  177. package/dist/permissions/lib/with-app-minimal-permissions.js +0 -10
  178. package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.d.ts +0 -7
  179. package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js +0 -56
  180. package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.d.ts +0 -3
  181. package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js +0 -16
  182. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.d.ts +0 -8
  183. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +0 -24
  184. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.d.ts +0 -9
  185. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +0 -31
  186. package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.d.ts +0 -16
  187. package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js +0 -27
  188. package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +0 -10
  189. package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +0 -23
  190. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +0 -5
  191. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +0 -7
  192. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +0 -5
  193. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +0 -10
  194. package/dist/permissions/modules/fetch-global-access/types.d.ts +0 -4
  195. package/dist/permissions/modules/fetch-global-access/types.js +0 -1
  196. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +0 -4
  197. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +0 -27
  198. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.d.ts +0 -12
  199. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +0 -32
  200. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +0 -4
  201. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.js +0 -29
  202. package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.d.ts +0 -4
  203. package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.js +0 -49
  204. package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.d.ts +0 -3
  205. package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.js +0 -56
  206. package/dist/permissions/modules/process-ast/lib/field-map-from-ast.d.ts +0 -4
  207. package/dist/permissions/modules/process-ast/lib/field-map-from-ast.js +0 -8
  208. package/dist/permissions/modules/process-ast/lib/inject-cases.d.ts +0 -9
  209. package/dist/permissions/modules/process-ast/lib/inject-cases.js +0 -93
  210. package/dist/permissions/modules/process-ast/process-ast.d.ts +0 -9
  211. package/dist/permissions/modules/process-ast/process-ast.js +0 -39
  212. package/dist/permissions/modules/process-ast/types.d.ts +0 -24
  213. package/dist/permissions/modules/process-ast/types.js +0 -1
  214. package/dist/permissions/modules/process-ast/utils/collections-in-field-map.d.ts +0 -2
  215. package/dist/permissions/modules/process-ast/utils/collections-in-field-map.js +0 -7
  216. package/dist/permissions/modules/process-ast/utils/dedupe-access.d.ts +0 -12
  217. package/dist/permissions/modules/process-ast/utils/dedupe-access.js +0 -30
  218. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.d.ts +0 -15
  219. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +0 -50
  220. package/dist/permissions/modules/process-ast/utils/find-related-collection.d.ts +0 -3
  221. package/dist/permissions/modules/process-ast/utils/find-related-collection.js +0 -9
  222. package/dist/permissions/modules/process-ast/utils/flatten-filter.d.ts +0 -3
  223. package/dist/permissions/modules/process-ast/utils/flatten-filter.js +0 -34
  224. package/dist/permissions/modules/process-ast/utils/format-a2o-key.d.ts +0 -1
  225. package/dist/permissions/modules/process-ast/utils/format-a2o-key.js +0 -3
  226. package/dist/permissions/modules/process-ast/utils/get-info-for-path.d.ts +0 -5
  227. package/dist/permissions/modules/process-ast/utils/get-info-for-path.js +0 -7
  228. package/dist/permissions/modules/process-ast/utils/has-item-permissions.d.ts +0 -2
  229. package/dist/permissions/modules/process-ast/utils/has-item-permissions.js +0 -3
  230. package/dist/permissions/modules/process-ast/utils/stringify-query-path.d.ts +0 -2
  231. package/dist/permissions/modules/process-ast/utils/stringify-query-path.js +0 -3
  232. package/dist/permissions/modules/process-ast/utils/validate-path/create-error.d.ts +0 -3
  233. package/dist/permissions/modules/process-ast/utils/validate-path/create-error.js +0 -16
  234. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.d.ts +0 -2
  235. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.js +0 -12
  236. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.d.ts +0 -2
  237. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.js +0 -28
  238. package/dist/permissions/modules/process-payload/lib/is-field-nullable.d.ts +0 -5
  239. package/dist/permissions/modules/process-payload/lib/is-field-nullable.js +0 -12
  240. package/dist/permissions/modules/process-payload/process-payload.d.ts +0 -13
  241. package/dist/permissions/modules/process-payload/process-payload.js +0 -77
  242. package/dist/permissions/modules/validate-access/lib/validate-collection-access.d.ts +0 -12
  243. package/dist/permissions/modules/validate-access/lib/validate-collection-access.js +0 -11
  244. package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +0 -9
  245. package/dist/permissions/modules/validate-access/lib/validate-item-access.js +0 -33
  246. package/dist/permissions/modules/validate-access/validate-access.d.ts +0 -14
  247. package/dist/permissions/modules/validate-access/validate-access.js +0 -28
  248. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.d.ts +0 -1
  249. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.js +0 -8
  250. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.d.ts +0 -5
  251. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.js +0 -10
  252. package/dist/permissions/types.d.ts +0 -6
  253. package/dist/permissions/types.js +0 -1
  254. package/dist/permissions/utils/create-default-accountability.d.ts +0 -2
  255. package/dist/permissions/utils/create-default-accountability.js +0 -11
  256. package/dist/permissions/utils/extract-required-dynamic-variable-context.d.ts +0 -8
  257. package/dist/permissions/utils/extract-required-dynamic-variable-context.js +0 -27
  258. package/dist/permissions/utils/fetch-dynamic-variable-context.d.ts +0 -9
  259. package/dist/permissions/utils/fetch-dynamic-variable-context.js +0 -43
  260. package/dist/permissions/utils/filter-policies-by-ip.d.ts +0 -2
  261. package/dist/permissions/utils/filter-policies-by-ip.js +0 -15
  262. package/dist/permissions/utils/get-unaliased-field-key.d.ts +0 -5
  263. package/dist/permissions/utils/get-unaliased-field-key.js +0 -17
  264. package/dist/permissions/utils/process-permissions.d.ts +0 -7
  265. package/dist/permissions/utils/process-permissions.js +0 -9
  266. package/dist/permissions/utils/with-cache.d.ts +0 -10
  267. package/dist/permissions/utils/with-cache.js +0 -25
  268. package/dist/services/access.d.ts +0 -10
  269. package/dist/services/access.js +0 -43
  270. package/dist/services/policies.d.ts +0 -12
  271. package/dist/services/policies.js +0 -87
  272. package/dist/telemetry/utils/check-user-limits.d.ts +0 -5
  273. package/dist/telemetry/utils/check-user-limits.js +0 -19
  274. package/dist/utils/fetch-user-count/fetch-access-lookup.d.ts +0 -17
  275. package/dist/utils/fetch-user-count/fetch-access-lookup.js +0 -22
  276. package/dist/utils/fetch-user-count/fetch-access-roles.d.ts +0 -16
  277. package/dist/utils/fetch-user-count/fetch-access-roles.js +0 -37
  278. package/dist/utils/fetch-user-count/fetch-active-users.d.ts +0 -6
  279. package/dist/utils/fetch-user-count/fetch-active-users.js +0 -3
  280. package/dist/utils/fetch-user-count/fetch-user-count.d.ts +0 -12
  281. package/dist/utils/fetch-user-count/fetch-user-count.js +0 -57
  282. package/dist/utils/fetch-user-count/get-user-count-query.d.ts +0 -20
  283. package/dist/utils/fetch-user-count/get-user-count-query.js +0 -17
  284. package/dist/utils/validate-user-count-integrity.d.ts +0 -13
  285. package/dist/utils/validate-user-count-integrity.js +0 -29
  286. /package/dist/database/migrations/{20240710A-permissions-policies.d.ts → 20240716A-update-files-date-fields.d.ts} +0 -0
@@ -1,40 +1,38 @@
1
- import { ErrorCode, MethodNotAllowedError, isDirectusError } from '@directus/errors';
2
- import { isObject, toArray } from '@directus/utils';
1
+ import { ErrorCode, InternalServerError, isDirectusError } from '@directus/errors';
2
+ import { isObject } from '@directus/utils';
3
3
  import { getNodeEnv } from '@directus/utils/node';
4
4
  import getDatabase from '../database/index.js';
5
5
  import emitter from '../emitter.js';
6
6
  import { useLogger } from '../logger/index.js';
7
- // Note: keep all 4 parameters here. That's how Express recognizes it's the error handler, even if
8
- // we don't use next
9
- const errorHandler = (err, req, res, _next) => {
7
+ const FALLBACK_ERROR = new InternalServerError();
8
+ export const errorHandler = asyncErrorHandler(async (err, req, res) => {
10
9
  const logger = useLogger();
11
- let payload = {
12
- errors: [],
13
- };
14
- const errors = toArray(err);
10
+ let errors = [];
15
11
  let status = null;
16
- for (const error of errors) {
17
- if (getNodeEnv() === 'development') {
18
- if (isObject(error)) {
19
- error['extensions'] = {
20
- ...(error['extensions'] || {}),
21
- stack: error['stack'],
22
- };
23
- }
12
+ // It can be assumed that at least one error is given
13
+ const receivedErrors = Array.isArray(err) ? err : [err];
14
+ for (const error of receivedErrors) {
15
+ // In dev mode, if available, expose stack trace under error's extensions data
16
+ if (getNodeEnv() === 'development' && error instanceof Error && error.stack) {
17
+ (error.extensions ??= {})['stack'] = error.stack;
24
18
  }
25
19
  if (isDirectusError(error)) {
26
20
  logger.debug(error);
27
- if (!status) {
21
+ if (status === null) {
22
+ // Use current error status as response status
28
23
  status = error.status;
29
24
  }
30
25
  else if (status !== error.status) {
31
- status = 500;
26
+ // Fallback if status has already been set by a preceding error
27
+ // and doesn't match the current one
28
+ status = FALLBACK_ERROR.status;
32
29
  }
33
- payload.errors.push({
30
+ errors.push({
34
31
  message: error.message,
35
32
  extensions: {
36
- code: error.code,
37
33
  ...(error.extensions ?? {}),
34
+ // Expose error code under error's extensions data
35
+ code: error.code,
38
36
  },
39
37
  });
40
38
  if (isDirectusError(error, ErrorCode.MethodNotAllowed)) {
@@ -43,45 +41,50 @@ const errorHandler = (err, req, res, _next) => {
43
41
  }
44
42
  else {
45
43
  logger.error(error);
46
- status = 500;
44
+ status = FALLBACK_ERROR.status;
47
45
  if (req.accountability?.admin === true) {
48
46
  const localError = isObject(error) ? error : {};
49
- const message = localError['message'] ?? typeof error === 'string' ? error : null;
50
- payload = {
51
- errors: [
52
- {
53
- message: message || 'An unexpected error occurred.',
54
- extensions: {
55
- code: 'INTERNAL_SERVER_ERROR',
56
- ...(localError['extensions'] ?? {}),
57
- },
47
+ // Use 'message' prop if available, otherwise if 'error' is a string use that
48
+ const message = (typeof localError['message'] === 'string' ? localError['message'] : null) ??
49
+ (typeof error === 'string' ? error : null);
50
+ errors = [
51
+ {
52
+ message: message || FALLBACK_ERROR.message,
53
+ extensions: {
54
+ code: FALLBACK_ERROR.code,
55
+ ...(localError['extensions'] ?? {}),
58
56
  },
59
- ],
60
- };
57
+ },
58
+ ];
61
59
  }
62
60
  else {
63
- payload = {
64
- errors: [
65
- {
66
- message: 'An unexpected error occurred.',
67
- extensions: {
68
- code: 'INTERNAL_SERVER_ERROR',
69
- },
70
- },
71
- ],
72
- };
61
+ // Don't expose unknown errors to non-admin users
62
+ errors = [{ message: FALLBACK_ERROR.message, extensions: { code: FALLBACK_ERROR.code } }];
73
63
  }
74
64
  }
75
65
  }
76
- res.status(status ?? 500);
77
- emitter
78
- .emitFilter('request.error', payload.errors, {}, {
66
+ res.status(status ?? FALLBACK_ERROR.status);
67
+ const updatedErrors = await emitter.emitFilter('request.error', errors, {}, {
79
68
  database: getDatabase(),
80
69
  schema: req.schema,
81
70
  accountability: req.accountability ?? null,
82
- })
83
- .then((updatedErrors) => {
84
- return res.json({ ...payload, errors: updatedErrors });
85
71
  });
86
- };
87
- export default errorHandler;
72
+ return res.json({ errors: updatedErrors });
73
+ });
74
+ function asyncErrorHandler(fn) {
75
+ return (err, req, res, next) => fn(err, req, res, next).catch((error) => {
76
+ // To be on the safe side and ensure this doesn't lead to an unhandled (potentially crashing) error
77
+ try {
78
+ const logger = useLogger();
79
+ logger.error(error, 'Unexpected error in error handler');
80
+ }
81
+ catch {
82
+ // Ignore
83
+ }
84
+ // Delegate to default error handler to close the connection
85
+ if (res.headersSent)
86
+ return next(err);
87
+ res.status(FALLBACK_ERROR.status);
88
+ return res.json({ errors: [{ message: FALLBACK_ERROR.message, extensions: { code: FALLBACK_ERROR.code } }] });
89
+ });
90
+ }
@@ -0,0 +1,3 @@
1
+ import type { RequestHandler } from 'express';
2
+ declare const getPermissions: RequestHandler;
3
+ export default getPermissions;
@@ -0,0 +1,10 @@
1
+ import asyncHandler from '../utils/async-handler.js';
2
+ import { getPermissions as getPermissionsUtil } from '../utils/get-permissions.js';
3
+ const getPermissions = asyncHandler(async (req, _res, next) => {
4
+ if (!req.accountability) {
5
+ throw new Error('getPermissions middleware needs to be called after authenticate');
6
+ }
7
+ req.accountability.permissions = await getPermissionsUtil(req.accountability, req.schema);
8
+ return next();
9
+ });
10
+ export default getPermissions;
@@ -20,12 +20,13 @@ export const respond = asyncHandler(async (req, res) => {
20
20
  exceedsMaxSize = valueSize > maxSize;
21
21
  }
22
22
  if ((req.method.toLowerCase() === 'get' || req.originalUrl?.startsWith('/graphql')) &&
23
+ req.originalUrl?.startsWith('/auth') === false &&
23
24
  env['CACHE_ENABLED'] === true &&
24
25
  cache &&
25
26
  !req.sanitizedQuery.export &&
26
27
  res.locals['cache'] !== false &&
27
28
  exceedsMaxSize === false) {
28
- const key = await getCacheKey(req);
29
+ const key = getCacheKey(req);
29
30
  try {
30
31
  await setCacheValue(cache, key, res.locals['payload'], getMilliseconds(env['CACHE_TTL']));
31
32
  await setCacheValue(cache, `${key}__expires_at`, { exp: Date.now() + getMilliseconds(env['CACHE_TTL'], 0) });
@@ -1,4 +1,5 @@
1
1
  import { useEnv } from '@directus/env';
2
+ import { matches } from 'ip-matching';
2
3
  import os from 'node:os';
3
4
  import { useLogger } from '../logger/index.js';
4
5
  import { ipInNetworks } from '../utils/ip-in-networks.js';
@@ -24,8 +25,13 @@ export function isDeniedIp(ip) {
24
25
  if (!networkInfo)
25
26
  continue;
26
27
  for (const info of networkInfo) {
27
- if (info.address === ip)
28
+ if (info.internal && info.cidr) {
29
+ if (matches(ip, info.cidr))
30
+ return true;
31
+ }
32
+ else if (info.address === ip) {
28
33
  return true;
34
+ }
29
35
  }
30
36
  }
31
37
  }
@@ -3,13 +3,11 @@ 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/index.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';
6
+ import { getPermissions } from '../utils/get-permissions.js';
10
7
  import { isValidUuid } from '../utils/is-valid-uuid.js';
11
8
  import { Url } from '../utils/url.js';
12
9
  import { userName } from '../utils/user-name.js';
10
+ import { AuthorizationService } from './authorization.js';
13
11
  import { ItemsService } from './items.js';
14
12
  import { NotificationsService } from './notifications.js';
15
13
  import { UsersService } from './users.js';
@@ -33,29 +31,19 @@ export class ActivityService extends ItemsService {
33
31
  for (const mention of mentions) {
34
32
  const userID = mention.substring(1);
35
33
  const user = await this.usersService.readOne(userID, {
36
- fields: ['id', 'first_name', 'last_name', 'email', 'role'],
34
+ fields: ['id', 'first_name', 'last_name', 'email', 'role.id', 'role.admin_access', 'role.app_access'],
37
35
  });
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({
36
+ const accountability = {
41
37
  user: userID,
42
38
  role: user['role']?.id ?? null,
43
- roles,
44
- ...globalAccess,
45
- });
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 });
46
44
  const usersService = new UsersService({ schema: this.schema, accountability });
47
45
  try {
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
- }
46
+ await authorizationService.checkAccess('read', data['collection'], data['item']);
59
47
  const templateData = await usersService.readByQuery({
60
48
  fields: ['id', 'first_name', 'last_name', 'email'],
61
49
  filter: { id: { _in: mentions.map((mention) => mention.substring(1)) } },
@@ -1,14 +1,15 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  import type { Range, Stat } from '@directus/storage';
3
- import type { Accountability, SchemaOverview } from '@directus/types';
3
+ import type { Accountability } 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';
7
8
  import { FilesService } from './files.js';
8
9
  export declare class AssetsService {
9
10
  knex: Knex;
10
11
  accountability: Accountability | null;
11
- schema: SchemaOverview;
12
+ authorizationService: AuthorizationService;
12
13
  filesService: FilesService;
13
14
  constructor(options: AbstractServiceOptions);
14
15
  getAsset(id: string, transformation?: TransformationSet, range?: Range): Promise<{
@@ -8,24 +8,25 @@ 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/index.js';
11
- import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
12
11
  import { getStorage } from '../storage/index.js';
13
12
  import { getMilliseconds } from '../utils/get-milliseconds.js';
14
13
  import { isValidUuid } from '../utils/is-valid-uuid.js';
15
14
  import * as TransformationUtils from '../utils/transformations.js';
15
+ import { AuthorizationService } from './authorization.js';
16
16
  import { FilesService } from './files.js';
17
+ import { getSharpInstance } from './files/lib/get-sharp-instance.js';
17
18
  const env = useEnv();
18
19
  const logger = useLogger();
19
20
  export class AssetsService {
20
21
  knex;
21
22
  accountability;
22
- schema;
23
+ authorizationService;
23
24
  filesService;
24
25
  constructor(options) {
25
26
  this.knex = options.knex || getDatabase();
26
27
  this.accountability = options.accountability || null;
27
- this.schema = options.schema;
28
28
  this.filesService = new FilesService({ ...options, accountability: null });
29
+ this.authorizationService = new AuthorizationService(options);
29
30
  }
30
31
  async getAsset(id, transformation, range) {
31
32
  const storage = await getStorage();
@@ -41,13 +42,8 @@ export class AssetsService {
41
42
  */
42
43
  if (!isValidUuid(id))
43
44
  throw new ForbiddenError();
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 });
45
+ if (systemPublicKeys.includes(id) === false && this.accountability?.admin !== true) {
46
+ await this.authorizationService.checkAccess('read', 'directus_files', id);
51
47
  }
52
48
  const file = (await this.filesService.readOne(id, { limit: 1 }));
53
49
  const exists = await storage.location(file.storage).exists(file.filename_disk);
@@ -121,11 +117,7 @@ export class AssetsService {
121
117
  });
122
118
  }
123
119
  const readStream = await storage.location(file.storage).read(file.filename_disk, range);
124
- const transformer = sharp({
125
- limitInputPixels: Math.pow(env['ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION'], 2),
126
- sequentialRead: true,
127
- failOn: env['ASSETS_INVALID_IMAGE_SENSITIVITY_LEVEL'],
128
- });
120
+ const transformer = getSharpInstance();
129
121
  transformer.timeout({
130
122
  seconds: clamp(Math.round(getMilliseconds(env['ASSETS_TRANSFORM_TIMEOUT'], 0) / 1000), 1, 3600),
131
123
  });
@@ -1,5 +1,3 @@
1
- import { fetchRolesTree } from '../permissions/lib/fetch-roles-tree.js';
2
- import { fetchGlobalAccess } from '../permissions/modules/fetch-global-access/fetch-global-access.js';
3
1
  import { Action } from '@directus/constants';
4
2
  import { useEnv } from '@directus/env';
5
3
  import { InvalidCredentialsError, InvalidOtpError, ServiceUnavailableError, UserSuspendedError, } from '@directus/errors';
@@ -50,9 +48,10 @@ export class AuthenticationService {
50
48
  throw err;
51
49
  }
52
50
  const user = await this.knex
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)
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)
56
55
  .first();
57
56
  const updatedPayload = await emitter.emitFilter('auth.login', payload, {
58
57
  status: 'pending',
@@ -129,13 +128,11 @@ export class AuthenticationService {
129
128
  throw new InvalidOtpError();
130
129
  }
131
130
  }
132
- const roles = await fetchRolesTree(user.role, this.knex);
133
- const globalAccess = await fetchGlobalAccess({ roles, user: user.id, ip: this.accountability?.ip ?? null }, this.knex);
134
131
  const tokenPayload = {
135
132
  id: user.id,
136
133
  role: user.role,
137
- app_access: globalAccess.app,
138
- admin_access: globalAccess.admin,
134
+ app_access: user.app_access,
135
+ admin_access: user.admin_access,
139
136
  };
140
137
  const refreshToken = nanoid(64);
141
138
  const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(env['REFRESH_TOKEN_TTL'], 0));
@@ -210,7 +207,9 @@ export class AuthenticationService {
210
207
  user_provider: 'u.provider',
211
208
  user_external_identifier: 'u.external_identifier',
212
209
  user_auth_data: 'u.auth_data',
213
- user_role: 'u.role',
210
+ role_id: 'r.id',
211
+ role_admin_access: 'r.admin_access',
212
+ role_app_access: 'r.app_access',
214
213
  share_id: 'd.id',
215
214
  share_item: 'd.item',
216
215
  share_role: 'd.role',
@@ -223,6 +222,9 @@ export class AuthenticationService {
223
222
  .from('directus_sessions AS s')
224
223
  .leftJoin('directus_users AS u', 's.user', 'u.id')
225
224
  .leftJoin('directus_shares AS d', 's.share', 'd.id')
225
+ .leftJoin('directus_roles AS r', (join) => {
226
+ join.onIn('r.id', [this.knex.ref('u.role'), this.knex.ref('d.role')]);
227
+ })
226
228
  .where('s.token', refreshToken)
227
229
  .andWhere('s.expires', '>=', new Date())
228
230
  .andWhere((subQuery) => {
@@ -246,8 +248,6 @@ export class AuthenticationService {
246
248
  throw new InvalidCredentialsError();
247
249
  }
248
250
  }
249
- const roles = await fetchRolesTree(record.user_role, this.knex);
250
- const globalAccess = await fetchGlobalAccess({ user: record.user_id, roles, ip: this.accountability?.ip ?? null }, this.knex);
251
251
  if (record.user_id) {
252
252
  const provider = getAuthProvider(record.user_provider);
253
253
  await provider.refresh({
@@ -260,9 +260,9 @@ export class AuthenticationService {
260
260
  provider: record.user_provider,
261
261
  external_identifier: record.user_external_identifier,
262
262
  auth_data: record.user_auth_data,
263
- role: record.user_role,
264
- app_access: globalAccess.app,
265
- admin_access: globalAccess.admin,
263
+ role: record.role_id,
264
+ app_access: record.role_app_access,
265
+ admin_access: record.role_admin_access,
266
266
  });
267
267
  }
268
268
  let newRefreshToken = record.session_next_token ?? nanoid(64);
@@ -270,9 +270,9 @@ export class AuthenticationService {
270
270
  const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(sessionDuration, 0));
271
271
  const tokenPayload = {
272
272
  id: record.user_id,
273
- role: record.user_role,
274
- app_access: globalAccess.app,
275
- admin_access: globalAccess.admin,
273
+ role: record.role_id,
274
+ app_access: record.role_app_access,
275
+ admin_access: record.role_admin_access,
276
276
  };
277
277
  if (options?.session) {
278
278
  newRefreshToken = await this.updateStatefulSession(record, refreshToken, newRefreshToken, refreshTokenExpiration);
@@ -0,0 +1,17 @@
1
+ import type { Accountability, Item, PermissionsAction, PrimaryKey, SchemaOverview } from '@directus/types';
2
+ import type { Knex } from 'knex';
3
+ import type { AST, AbstractServiceOptions } from '../types/index.js';
4
+ import { PayloadService } from './payload.js';
5
+ export declare class AuthorizationService {
6
+ knex: Knex;
7
+ accountability: Accountability | null;
8
+ payloadService: PayloadService;
9
+ schema: SchemaOverview;
10
+ constructor(options: AbstractServiceOptions);
11
+ processAST(ast: AST, action?: PermissionsAction): Promise<AST>;
12
+ /**
13
+ * Checks if the provided payload matches the configured permissions, and adds the presets to the payload.
14
+ */
15
+ validatePayload(action: PermissionsAction, collection: string, data: Partial<Item>): Partial<Item>;
16
+ checkAccess(action: PermissionsAction, collection: string, pk?: PrimaryKey | PrimaryKey[]): Promise<void>;
17
+ }