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