@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
@@ -11,6 +11,9 @@ import { clearSystemCache, getCache } from '../../cache.js';
11
11
  import { DEFAULT_AUTH_PROVIDER, GENERATE_SPECIAL, REFRESH_COOKIE_OPTIONS, SESSION_COOKIE_OPTIONS, } from '../../constants.js';
12
12
  import getDatabase from '../../database/index.js';
13
13
  import { rateLimiter } from '../../middleware/rate-limiter-registration.js';
14
+ import { fetchAllowedFieldMap } from '../../permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js';
15
+ import { fetchInconsistentFieldMap } from '../../permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js';
16
+ import { createDefaultAccountability } from '../../permissions/utils/create-default-accountability.js';
14
17
  import { generateHash } from '../../utils/generate-hash.js';
15
18
  import { getGraphQLType } from '../../utils/get-graphql-type.js';
16
19
  import { getIPFromReq } from '../../utils/get-ip-from-req.js';
@@ -48,6 +51,9 @@ import { GraphQLVoid } from './types/void.js';
48
51
  import { addPathToValidationError } from './utils/add-path-to-validation-error.js';
49
52
  import processError from './utils/process-error.js';
50
53
  import { sanitizeGraphqlSchema } from './utils/sanitize-gql-schema.js';
54
+ import { fetchAccountabilityCollectionAccess } from '../../permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js';
55
+ import { fetchAccountabilityPolicyGlobals } from '../../permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js';
56
+ import { RolesService } from '../roles.js';
51
57
  const env = useEnv();
52
58
  const validationRules = Array.from(specifiedRules);
53
59
  if (env['GRAPHQL_INTROSPECTION'] === false) {
@@ -80,7 +86,7 @@ export class GraphQLService {
80
86
  * Execute a GraphQL structure
81
87
  */
82
88
  async execute({ document, variables, operationName, contextValue, }) {
83
- const schema = this.getSchema();
89
+ const schema = await this.getSchema();
84
90
  const validationErrors = validate(schema, document, validationRules).map((validationError) => addPathToValidationError(validationError));
85
91
  if (validationErrors.length > 0) {
86
92
  throw new GraphQLValidationError({ errors: validationErrors });
@@ -108,7 +114,7 @@ export class GraphQLService {
108
114
  formattedResult.extensions = result['extensions'];
109
115
  return formattedResult;
110
116
  }
111
- getSchema(type = 'schema') {
117
+ async getSchema(type = 'schema') {
112
118
  const key = `${this.scope}_${type}_${this.accountability?.role}_${this.accountability?.user}`;
113
119
  const cachedSchema = cache.get(key);
114
120
  if (cachedSchema)
@@ -116,20 +122,53 @@ export class GraphQLService {
116
122
  // eslint-disable-next-line @typescript-eslint/no-this-alias
117
123
  const self = this;
118
124
  const schemaComposer = new SchemaComposer();
125
+ let schema;
119
126
  const sanitizedSchema = sanitizeGraphqlSchema(this.schema);
120
- const schema = {
121
- read: this.accountability?.admin === true
122
- ? sanitizedSchema
123
- : reduceSchema(sanitizedSchema, this.accountability?.permissions || null, ['read']),
124
- create: this.accountability?.admin === true
125
- ? sanitizedSchema
126
- : reduceSchema(sanitizedSchema, this.accountability?.permissions || null, ['create']),
127
- update: this.accountability?.admin === true
128
- ? sanitizedSchema
129
- : reduceSchema(sanitizedSchema, this.accountability?.permissions || null, ['update']),
130
- delete: this.accountability?.admin === true
131
- ? sanitizedSchema
132
- : reduceSchema(sanitizedSchema, this.accountability?.permissions || null, ['delete']),
127
+ if (!this.accountability || this.accountability.admin) {
128
+ schema = {
129
+ read: sanitizedSchema,
130
+ create: sanitizedSchema,
131
+ update: sanitizedSchema,
132
+ delete: sanitizedSchema,
133
+ };
134
+ }
135
+ else {
136
+ schema = {
137
+ read: reduceSchema(sanitizedSchema, await fetchAllowedFieldMap({
138
+ accountability: this.accountability,
139
+ action: 'read',
140
+ }, { schema: this.schema, knex: this.knex })),
141
+ create: reduceSchema(sanitizedSchema, await fetchAllowedFieldMap({
142
+ accountability: this.accountability,
143
+ action: 'create',
144
+ }, { schema: this.schema, knex: this.knex })),
145
+ update: reduceSchema(sanitizedSchema, await fetchAllowedFieldMap({
146
+ accountability: this.accountability,
147
+ action: 'update',
148
+ }, { schema: this.schema, knex: this.knex })),
149
+ delete: reduceSchema(sanitizedSchema, await fetchAllowedFieldMap({
150
+ accountability: this.accountability,
151
+ action: 'delete',
152
+ }, { schema: this.schema, knex: this.knex })),
153
+ };
154
+ }
155
+ const inconsistentFields = {
156
+ read: await fetchInconsistentFieldMap({
157
+ accountability: this.accountability,
158
+ action: 'read',
159
+ }, { schema: this.schema, knex: this.knex }),
160
+ create: await fetchInconsistentFieldMap({
161
+ accountability: this.accountability,
162
+ action: 'create',
163
+ }, { schema: this.schema, knex: this.knex }),
164
+ update: await fetchInconsistentFieldMap({
165
+ accountability: this.accountability,
166
+ action: 'update',
167
+ }, { schema: this.schema, knex: this.knex }),
168
+ delete: await fetchInconsistentFieldMap({
169
+ accountability: this.accountability,
170
+ action: 'delete',
171
+ }, { schema: this.schema, knex: this.knex }),
133
172
  };
134
173
  const subscriptionEventType = schemaComposer.createEnumTC({
135
174
  name: 'EventEnum',
@@ -300,16 +339,18 @@ export class GraphQLService {
300
339
  name: action === 'read' ? collection.collection : `${action}_${collection.collection}`,
301
340
  fields: Object.values(collection.fields).reduce((acc, field) => {
302
341
  let type = getGraphQLType(field.type, field.special);
342
+ const fieldIsInconsistent = inconsistentFields[action][collection.collection]?.includes(field.field);
303
343
  // GraphQL doesn't differentiate between not-null and has-to-be-submitted. We
304
344
  // can't non-null in update, as that would require every not-nullable field to be
305
345
  // submitted on updates
306
346
  if (field.nullable === false &&
307
347
  !field.defaultValue &&
308
348
  !GENERATE_SPECIAL.some((flag) => field.special.includes(flag)) &&
349
+ fieldIsInconsistent === false &&
309
350
  action !== 'update') {
310
351
  type = new GraphQLNonNull(type);
311
352
  }
312
- if (collection.primary === field.field) {
353
+ if (collection.primary === field.field && fieldIsInconsistent === false) {
313
354
  // permissions IDs need to be nullable https://github.com/directus/directus/issues/20509
314
355
  if (collection.collection === 'directus_permissions') {
315
356
  type = GraphQLID;
@@ -1762,7 +1803,7 @@ export class GraphQLService {
1762
1803
  accountability: this.accountability,
1763
1804
  scope: args['scope'] ?? 'items',
1764
1805
  });
1765
- return service.getSchema('sdl');
1806
+ return await service.getSchema('sdl');
1766
1807
  },
1767
1808
  },
1768
1809
  server_ping: {
@@ -1815,7 +1856,7 @@ export class GraphQLService {
1815
1856
  otp: GraphQLString,
1816
1857
  },
1817
1858
  resolve: async (_, args, { req, res }) => {
1818
- const accountability = { role: null };
1859
+ const accountability = createDefaultAccountability();
1819
1860
  if (req?.ip)
1820
1861
  accountability.ip = req.ip;
1821
1862
  const userAgent = req?.get('user-agent');
@@ -1855,7 +1896,7 @@ export class GraphQLService {
1855
1896
  mode: AuthMode,
1856
1897
  },
1857
1898
  resolve: async (_, args, { req, res }) => {
1858
- const accountability = { role: null };
1899
+ const accountability = createDefaultAccountability();
1859
1900
  if (req?.ip)
1860
1901
  accountability.ip = req.ip;
1861
1902
  const userAgent = req?.get('user-agent');
@@ -1913,7 +1954,7 @@ export class GraphQLService {
1913
1954
  mode: AuthMode,
1914
1955
  },
1915
1956
  resolve: async (_, args, { req, res }) => {
1916
- const accountability = { role: null };
1957
+ const accountability = createDefaultAccountability();
1917
1958
  if (req?.ip)
1918
1959
  accountability.ip = req.ip;
1919
1960
  const userAgent = req?.get('user-agent');
@@ -1963,7 +2004,7 @@ export class GraphQLService {
1963
2004
  reset_url: GraphQLString,
1964
2005
  },
1965
2006
  resolve: async (_, args, { req }) => {
1966
- const accountability = { role: null };
2007
+ const accountability = createDefaultAccountability();
1967
2008
  if (req?.ip)
1968
2009
  accountability.ip = req.ip;
1969
2010
  const userAgent = req?.get('user-agent');
@@ -1991,7 +2032,7 @@ export class GraphQLService {
1991
2032
  password: new GraphQLNonNull(GraphQLString),
1992
2033
  },
1993
2034
  resolve: async (_, args, { req }) => {
1994
- const accountability = { role: null };
2035
+ const accountability = createDefaultAccountability();
1995
2036
  if (req?.ip)
1996
2037
  accountability.ip = req.ip;
1997
2038
  const userAgent = req?.get('user-agent');
@@ -2632,6 +2673,69 @@ export class GraphQLService {
2632
2673
  },
2633
2674
  });
2634
2675
  }
2676
+ if ('directus_permissions' in schema.read.collections) {
2677
+ schemaComposer.Query.addFields({
2678
+ permissions_me: {
2679
+ type: schemaComposer.createScalarTC({
2680
+ name: 'permissions_me_type',
2681
+ parseValue: (value) => value,
2682
+ serialize: (value) => value,
2683
+ }),
2684
+ resolve: async (_, _args, __, _info) => {
2685
+ if (!this.accountability?.user && !this.accountability?.role)
2686
+ return null;
2687
+ const result = await fetchAccountabilityCollectionAccess(this.accountability, {
2688
+ schema: this.schema,
2689
+ knex: getDatabase(),
2690
+ });
2691
+ return result;
2692
+ },
2693
+ },
2694
+ });
2695
+ }
2696
+ if ('directus_roles' in schema.read.collections) {
2697
+ schemaComposer.Query.addFields({
2698
+ roles_me: {
2699
+ type: ReadCollectionTypes['directus_roles'].List,
2700
+ resolve: async (_, args, __, info) => {
2701
+ if (!this.accountability?.user && !this.accountability?.role)
2702
+ return null;
2703
+ const service = new RolesService({
2704
+ accountability: this.accountability,
2705
+ schema: this.schema,
2706
+ });
2707
+ const selections = this.replaceFragmentsInSelections(info.fieldNodes[0]?.selectionSet?.selections, info.fragments);
2708
+ const query = this.getQuery(args, selections || [], info.variableValues);
2709
+ query.limit = -1;
2710
+ const roles = await service.readMany(this.accountability.roles, query);
2711
+ return roles;
2712
+ },
2713
+ },
2714
+ });
2715
+ }
2716
+ if ('directus_policies' in schema.read.collections) {
2717
+ schemaComposer.Query.addFields({
2718
+ policies_me_globals: {
2719
+ type: schemaComposer.createObjectTC({
2720
+ name: 'policy_me_globals_type',
2721
+ fields: {
2722
+ enforce_tfa: 'Boolean',
2723
+ app_access: 'Boolean',
2724
+ admin_access: 'Boolean',
2725
+ },
2726
+ }),
2727
+ resolve: async (_, _args, __, _info) => {
2728
+ if (!this.accountability?.user && !this.accountability?.role)
2729
+ return null;
2730
+ const result = await fetchAccountabilityPolicyGlobals(this.accountability, {
2731
+ schema: this.schema,
2732
+ knex: getDatabase(),
2733
+ });
2734
+ return result;
2735
+ },
2736
+ },
2737
+ });
2738
+ }
2635
2739
  if ('directus_users' in schema.update.collections && this.accountability?.user) {
2636
2740
  schemaComposer.Mutation.addFields({
2637
2741
  update_users_me: {
@@ -1,7 +1,6 @@
1
1
  import { EventEmitter, on } from 'events';
2
2
  import { useBus } from '../../bus/index.js';
3
3
  import { getSchema } from '../../utils/get-schema.js';
4
- import { refreshAccountability } from '../../websocket/authenticate.js';
5
4
  import { getPayload } from '../../websocket/utils/items.js';
6
5
  const messages = createPubSub(new EventEmitter());
7
6
  export function bindPubSub() {
@@ -19,7 +18,6 @@ export function createSubscriptionGenerator(self, event) {
19
18
  if ('event' in args && eventData['action'] !== args['event']) {
20
19
  continue; // skip filtered events
21
20
  }
22
- const accountability = await refreshAccountability(self.accountability);
23
21
  const schema = await getSchema();
24
22
  const subscription = {
25
23
  collection: eventData['collection'],
@@ -35,7 +33,7 @@ export function createSubscriptionGenerator(self, event) {
35
33
  if (eventData['action'] === 'create') {
36
34
  try {
37
35
  subscription.item = eventData['key'];
38
- const result = await getPayload(subscription, accountability, schema, eventData);
36
+ const result = await getPayload(subscription, self.accountability, schema, eventData);
39
37
  yield {
40
38
  [event]: {
41
39
  key: eventData['key'],
@@ -52,7 +50,7 @@ export function createSubscriptionGenerator(self, event) {
52
50
  for (const key of eventData['keys']) {
53
51
  try {
54
52
  subscription.item = key;
55
- const result = await getPayload(subscription, accountability, schema, eventData);
53
+ const result = await getPayload(subscription, self.accountability, schema, eventData);
56
54
  yield {
57
55
  [event]: {
58
56
  key,
@@ -15,12 +15,13 @@ import StreamArray from 'stream-json/streamers/StreamArray.js';
15
15
  import getDatabase from '../database/index.js';
16
16
  import emitter from '../emitter.js';
17
17
  import { useLogger } from '../logger.js';
18
+ import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
18
19
  import { getDateFormatted } from '../utils/get-date-formatted.js';
20
+ import { getService } from '../utils/get-service.js';
19
21
  import { transaction } from '../utils/transaction.js';
20
22
  import { Url } from '../utils/url.js';
21
23
  import { userName } from '../utils/user-name.js';
22
24
  import { FilesService } from './files.js';
23
- import { ItemsService } from './items.js';
24
25
  import { NotificationsService } from './notifications.js';
25
26
  import { UsersService } from './users.js';
26
27
  const env = useEnv();
@@ -37,10 +38,23 @@ export class ImportService {
37
38
  async import(collection, mimetype, stream) {
38
39
  if (this.accountability?.admin !== true && isSystemCollection(collection))
39
40
  throw new ForbiddenError();
40
- const createPermissions = this.accountability?.permissions?.find((permission) => permission.collection === collection && permission.action === 'create');
41
- const updatePermissions = this.accountability?.permissions?.find((permission) => permission.collection === collection && permission.action === 'update');
42
- if (this.accountability?.admin !== true && (!createPermissions || !updatePermissions)) {
43
- throw new ForbiddenError();
41
+ if (this.accountability) {
42
+ await validateAccess({
43
+ accountability: this.accountability,
44
+ action: 'create',
45
+ collection,
46
+ }, {
47
+ schema: this.schema,
48
+ knex: this.knex,
49
+ });
50
+ await validateAccess({
51
+ accountability: this.accountability,
52
+ action: 'update',
53
+ collection,
54
+ }, {
55
+ schema: this.schema,
56
+ knex: this.knex,
57
+ });
44
58
  }
45
59
  switch (mimetype) {
46
60
  case 'application/json':
@@ -52,11 +66,11 @@ export class ImportService {
52
66
  throw new UnsupportedMediaTypeError({ mediaType: mimetype, where: 'file import' });
53
67
  }
54
68
  }
55
- importJSON(collection, stream) {
69
+ async importJSON(collection, stream) {
56
70
  const extractJSON = StreamArray.withParser();
57
71
  const nestedActionEvents = [];
58
72
  return transaction(this.knex, (trx) => {
59
- const service = new ItemsService(collection, {
73
+ const service = getService(collection, {
60
74
  knex: trx,
61
75
  schema: this.schema,
62
76
  accountability: this.accountability,
@@ -94,7 +108,7 @@ export class ImportService {
94
108
  throw new Error('Failed to create temporary file for import');
95
109
  const nestedActionEvents = [];
96
110
  return transaction(this.knex, (trx) => {
97
- const service = new ItemsService(collection, {
111
+ const service = getService(collection, {
98
112
  knex: trx,
99
113
  schema: this.schema,
100
114
  accountability: this.accountability,
@@ -213,7 +227,7 @@ export class ExportService {
213
227
  };
214
228
  const database = getDatabase();
215
229
  await transaction(database, async (trx) => {
216
- const service = new ItemsService(collection, {
230
+ const service = getService(collection, {
217
231
  accountability: this.accountability,
218
232
  schema: this.schema,
219
233
  knex: trx,
@@ -1,7 +1,7 @@
1
+ export * from './access.js';
1
2
  export * from './activity.js';
2
3
  export * from './assets.js';
3
4
  export * from './authentication.js';
4
- export * from './authorization.js';
5
5
  export * from './collections.js';
6
6
  export * from './dashboards.js';
7
7
  export * from './extensions.js';
@@ -18,7 +18,8 @@ export * from './notifications.js';
18
18
  export * from './operations.js';
19
19
  export * from './panels.js';
20
20
  export * from './payload.js';
21
- export * from './permissions/index.js';
21
+ export * from './permissions.js';
22
+ export * from './policies.js';
22
23
  export * from './presets.js';
23
24
  export * from './relations.js';
24
25
  export * from './revisions.js';
@@ -1,7 +1,7 @@
1
+ export * from './access.js';
1
2
  export * from './activity.js';
2
3
  export * from './assets.js';
3
4
  export * from './authentication.js';
4
- export * from './authorization.js';
5
5
  export * from './collections.js';
6
6
  export * from './dashboards.js';
7
7
  export * from './extensions.js';
@@ -18,7 +18,8 @@ export * from './notifications.js';
18
18
  export * from './operations.js';
19
19
  export * from './panels.js';
20
20
  export * from './payload.js';
21
- export * from './permissions/index.js';
21
+ export * from './permissions.js';
22
+ export * from './policies.js';
22
23
  export * from './presets.js';
23
24
  export * from './relations.js';
24
25
  export * from './revisions.js';
@@ -19,6 +19,10 @@ export declare class ItemsService<Item extends AnyItem = AnyItem> implements Abs
19
19
  schema: SchemaOverview;
20
20
  cache: Keyv<any> | null;
21
21
  constructor(collection: string, options: AbstractServiceOptions);
22
+ /**
23
+ * Create a fork of the current service, allowing instantiation with different options.
24
+ */
25
+ private fork;
22
26
  createMutationTracker(initialCount?: number): MutationTracker;
23
27
  getKeysByQuery(query: Query): Promise<PrimaryKey[]>;
24
28
  /**
@@ -27,62 +31,84 @@ export declare class ItemsService<Item extends AnyItem = AnyItem> implements Abs
27
31
  createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
28
32
  /**
29
33
  * Create multiple new items at once. Inserts all provided records sequentially wrapped in a transaction.
34
+ *
35
+ * Uses `this.createOne` under the hood.
30
36
  */
31
37
  createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
32
38
  /**
33
- * Get items by query
39
+ * Get items by query.
34
40
  */
35
41
  readByQuery(query: Query, opts?: QueryOptions): Promise<Item[]>;
36
42
  /**
37
- * Get single item by primary key
43
+ * Get single item by primary key.
44
+ *
45
+ * Uses `this.readByQuery` under the hood.
38
46
  */
39
47
  readOne(key: PrimaryKey, query?: Query, opts?: QueryOptions): Promise<Item>;
40
48
  /**
41
- * Get multiple items by primary keys
49
+ * Get multiple items by primary keys.
50
+ *
51
+ * Uses `this.readByQuery` under the hood.
42
52
  */
43
53
  readMany(keys: PrimaryKey[], query?: Query, opts?: QueryOptions): Promise<Item[]>;
44
54
  /**
45
- * Update multiple items by query
55
+ * Update multiple items by query.
56
+ *
57
+ * Uses `this.updateMany` under the hood.
46
58
  */
47
59
  updateByQuery(query: Query, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
48
60
  /**
49
- * Update a single item by primary key
61
+ * Update a single item by primary key.
62
+ *
63
+ * Uses `this.updateMany` under the hood.
50
64
  */
51
65
  updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
52
66
  /**
53
- * Update multiple items in a single transaction
67
+ * Update multiple items in a single transaction.
68
+ *
69
+ * Uses `this.updateOne` under the hood.
54
70
  */
55
71
  updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
56
72
  /**
57
- * Update many items by primary key, setting all items to the same change
73
+ * Update many items by primary key, setting all items to the same change.
58
74
  */
59
75
  updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
60
76
  /**
61
- * Upsert a single item
77
+ * Upsert a single item.
78
+ *
79
+ * Uses `this.createOne` / `this.updateOne` under the hood.
62
80
  */
63
81
  upsertOne(payload: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
64
82
  /**
65
- * Upsert many items
83
+ * Upsert many items.
84
+ *
85
+ * Uses `this.upsertOne` under the hood.
66
86
  */
67
87
  upsertMany(payloads: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
68
88
  /**
69
- * Delete multiple items by query
89
+ * Delete multiple items by query.
90
+ *
91
+ * Uses `this.deleteMany` under the hood.
70
92
  */
71
93
  deleteByQuery(query: Query, opts?: MutationOptions): Promise<PrimaryKey[]>;
72
94
  /**
73
- * Delete a single item by primary key
95
+ * Delete a single item by primary key.
96
+ *
97
+ * Uses `this.deleteMany` under the hood.
74
98
  */
75
99
  deleteOne(key: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
76
100
  /**
77
- * Delete multiple items by primary key
101
+ * Delete multiple items by primary key.
78
102
  */
79
103
  deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
80
104
  /**
81
- * Read/treat collection as singleton
105
+ * Read/treat collection as singleton.
82
106
  */
83
107
  readSingleton(query: Query, opts?: QueryOptions): Promise<Partial<Item>>;
84
108
  /**
85
- * Upsert/treat collection as singleton
109
+ * Upsert/treat collection as singleton.
110
+ *
111
+ * Uses `this.createOne` / `this.updateOne` under the hood.
86
112
  */
87
113
  upsertSingleton(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
88
114
  }