@directus/api 21.0.0 → 22.1.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.
- package/dist/app.js +4 -4
- package/dist/auth/drivers/ldap.js +4 -4
- package/dist/auth/drivers/local.js +4 -4
- package/dist/auth/drivers/oauth2.js +4 -4
- package/dist/auth/drivers/openid.js +2 -4
- package/dist/cache.js +3 -0
- package/dist/cli/commands/bootstrap/index.js +8 -2
- package/dist/cli/commands/init/index.js +9 -10
- package/dist/cli/commands/init/questions.d.ts +7 -6
- package/dist/cli/commands/init/questions.js +2 -2
- package/dist/cli/utils/create-env/index.d.ts +2 -2
- package/dist/cli/utils/create-env/index.js +3 -1
- package/dist/cli/utils/defaults.d.ts +4 -11
- package/dist/cli/utils/defaults.js +7 -1
- package/dist/cli/utils/drivers.js +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/controllers/access.d.ts +2 -0
- package/dist/controllers/access.js +148 -0
- package/dist/controllers/auth.js +5 -16
- package/dist/controllers/permissions.js +14 -2
- package/dist/controllers/policies.d.ts +2 -0
- package/dist/controllers/policies.js +169 -0
- package/dist/controllers/roles.js +22 -1
- package/dist/controllers/tus.js +14 -26
- package/dist/controllers/users.js +0 -55
- package/dist/database/get-ast-from-query/get-ast-from-query.d.ts +16 -0
- package/dist/database/get-ast-from-query/get-ast-from-query.js +82 -0
- package/dist/database/get-ast-from-query/lib/convert-wildcards.d.ts +13 -0
- package/dist/database/get-ast-from-query/lib/convert-wildcards.js +69 -0
- package/dist/database/get-ast-from-query/lib/parse-fields.d.ts +15 -0
- package/dist/database/get-ast-from-query/lib/parse-fields.js +200 -0
- package/dist/database/get-ast-from-query/utils/get-deep-query.d.ts +14 -0
- package/dist/database/get-ast-from-query/utils/get-deep-query.js +17 -0
- package/dist/database/get-ast-from-query/utils/get-related-collection.d.ts +2 -0
- package/dist/database/get-ast-from-query/utils/get-related-collection.js +13 -0
- package/dist/database/get-ast-from-query/utils/get-relation.d.ts +2 -0
- package/dist/database/get-ast-from-query/utils/get-relation.js +7 -0
- package/dist/database/helpers/fn/types.d.ts +2 -1
- package/dist/database/helpers/fn/types.js +1 -1
- package/dist/database/helpers/geometry/dialects/mssql.d.ts +1 -1
- package/dist/database/helpers/geometry/dialects/mssql.js +4 -2
- package/dist/database/helpers/geometry/dialects/mysql.js +1 -1
- package/dist/database/helpers/geometry/dialects/oracle.d.ts +1 -1
- package/dist/database/helpers/geometry/dialects/oracle.js +5 -3
- package/dist/database/helpers/geometry/types.d.ts +1 -1
- package/dist/database/helpers/geometry/types.js +4 -2
- package/dist/database/helpers/index.d.ts +3 -3
- package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/cockroachdb.js +4 -0
- package/dist/database/helpers/schema/dialects/mssql.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/mssql.js +4 -0
- package/dist/database/helpers/schema/dialects/oracle.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/oracle.js +4 -0
- package/dist/database/helpers/schema/dialects/postgres.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/postgres.js +4 -0
- package/dist/database/helpers/schema/types.d.ts +5 -0
- package/dist/database/helpers/schema/types.js +3 -0
- package/dist/database/helpers/schema/utils/preprocess-bindings.d.ts +8 -0
- package/dist/database/helpers/schema/utils/preprocess-bindings.js +30 -0
- package/dist/database/index.js +6 -1
- package/dist/database/migrations/20210519A-add-system-fk-triggers.js +3 -2
- package/dist/database/migrations/20230721A-require-shares-fields.js +3 -5
- package/dist/database/migrations/20240716A-update-files-date-fields.js +3 -7
- package/dist/{utils/merge-permissions.d.ts → database/migrations/20240806A-permissions-policies.d.ts} +4 -1
- package/dist/database/migrations/20240806A-permissions-policies.js +352 -0
- package/dist/database/run-ast/lib/get-db-query.d.ts +4 -0
- package/dist/database/run-ast/lib/get-db-query.js +218 -0
- package/dist/database/run-ast/lib/parse-current-level.d.ts +7 -0
- package/dist/database/run-ast/lib/parse-current-level.js +41 -0
- package/dist/database/run-ast/run-ast.d.ts +7 -0
- package/dist/database/run-ast/run-ast.js +107 -0
- package/dist/database/{run-ast.d.ts → run-ast/types.d.ts} +3 -9
- package/dist/database/run-ast/types.js +1 -0
- package/dist/database/run-ast/utils/apply-case-when.d.ts +16 -0
- package/dist/database/run-ast/utils/apply-case-when.js +27 -0
- package/dist/database/run-ast/utils/apply-parent-filters.d.ts +3 -0
- package/dist/database/run-ast/utils/apply-parent-filters.js +55 -0
- package/dist/database/run-ast/utils/get-column-pre-processor.d.ts +10 -0
- package/dist/database/run-ast/utils/get-column-pre-processor.js +57 -0
- package/dist/database/run-ast/utils/get-field-alias.d.ts +2 -0
- package/dist/database/run-ast/utils/get-field-alias.js +4 -0
- package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.d.ts +5 -0
- package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.js +23 -0
- package/dist/database/run-ast/utils/merge-with-parent-items.d.ts +3 -0
- package/dist/database/run-ast/utils/merge-with-parent-items.js +87 -0
- package/dist/database/run-ast/utils/remove-temporary-fields.d.ts +3 -0
- package/dist/database/run-ast/utils/remove-temporary-fields.js +73 -0
- package/dist/database/run-ast/utils/with-preprocess-bindings.d.ts +2 -0
- package/dist/database/run-ast/utils/with-preprocess-bindings.js +14 -0
- package/dist/flows.js +3 -4
- package/dist/middleware/authenticate.js +2 -7
- package/dist/middleware/cache.js +1 -1
- package/dist/middleware/respond.js +1 -1
- package/dist/permissions/cache.d.ts +2 -0
- package/dist/permissions/cache.js +23 -0
- package/dist/permissions/lib/fetch-permissions.d.ts +11 -0
- package/dist/permissions/lib/fetch-permissions.js +56 -0
- package/dist/permissions/lib/fetch-policies.d.ts +14 -0
- package/dist/permissions/lib/fetch-policies.js +43 -0
- package/dist/permissions/lib/fetch-roles-tree.d.ts +3 -0
- package/dist/permissions/lib/fetch-roles-tree.js +28 -0
- package/dist/{services/permissions → permissions}/lib/with-app-minimal-permissions.d.ts +1 -1
- package/dist/permissions/lib/with-app-minimal-permissions.js +10 -0
- package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.d.ts +7 -0
- package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js +56 -0
- package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.d.ts +3 -0
- package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js +16 -0
- package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.d.ts +8 -0
- package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +24 -0
- package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.d.ts +9 -0
- package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +31 -0
- package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.d.ts +16 -0
- package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js +27 -0
- package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +10 -0
- package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +23 -0
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +5 -0
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +7 -0
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +5 -0
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +10 -0
- package/dist/permissions/modules/fetch-global-access/types.d.ts +4 -0
- package/dist/permissions/modules/fetch-global-access/types.js +1 -0
- package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +4 -0
- package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +27 -0
- package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.d.ts +12 -0
- package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +32 -0
- package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +4 -0
- package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.js +29 -0
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.d.ts +4 -0
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.js +49 -0
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.d.ts +3 -0
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.js +56 -0
- package/dist/permissions/modules/process-ast/lib/field-map-from-ast.d.ts +4 -0
- package/dist/permissions/modules/process-ast/lib/field-map-from-ast.js +8 -0
- package/dist/permissions/modules/process-ast/lib/inject-cases.d.ts +9 -0
- package/dist/permissions/modules/process-ast/lib/inject-cases.js +93 -0
- package/dist/permissions/modules/process-ast/process-ast.d.ts +9 -0
- package/dist/permissions/modules/process-ast/process-ast.js +39 -0
- package/dist/permissions/modules/process-ast/types.d.ts +18 -0
- package/dist/permissions/modules/process-ast/types.js +1 -0
- package/dist/permissions/modules/process-ast/utils/collections-in-field-map.d.ts +2 -0
- package/dist/permissions/modules/process-ast/utils/collections-in-field-map.js +7 -0
- package/dist/permissions/modules/process-ast/utils/dedupe-access.d.ts +12 -0
- package/dist/permissions/modules/process-ast/utils/dedupe-access.js +30 -0
- package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.d.ts +15 -0
- package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +60 -0
- package/dist/permissions/modules/process-ast/utils/find-related-collection.d.ts +3 -0
- package/dist/permissions/modules/process-ast/utils/find-related-collection.js +9 -0
- package/dist/permissions/modules/process-ast/utils/flatten-filter.d.ts +3 -0
- package/dist/permissions/modules/process-ast/utils/flatten-filter.js +34 -0
- package/dist/permissions/modules/process-ast/utils/format-a2o-key.d.ts +1 -0
- package/dist/permissions/modules/process-ast/utils/format-a2o-key.js +3 -0
- package/dist/permissions/modules/process-ast/utils/get-info-for-path.d.ts +5 -0
- package/dist/permissions/modules/process-ast/utils/get-info-for-path.js +7 -0
- package/dist/permissions/modules/process-ast/utils/has-item-permissions.d.ts +2 -0
- package/dist/permissions/modules/process-ast/utils/has-item-permissions.js +3 -0
- package/dist/permissions/modules/process-ast/utils/stringify-query-path.d.ts +2 -0
- package/dist/permissions/modules/process-ast/utils/stringify-query-path.js +3 -0
- package/dist/permissions/modules/process-ast/utils/validate-path/create-error.d.ts +3 -0
- package/dist/permissions/modules/process-ast/utils/validate-path/create-error.js +16 -0
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.d.ts +2 -0
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.js +12 -0
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.d.ts +2 -0
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.js +28 -0
- package/dist/permissions/modules/process-payload/lib/is-field-nullable.d.ts +5 -0
- package/dist/permissions/modules/process-payload/lib/is-field-nullable.js +12 -0
- package/dist/permissions/modules/process-payload/process-payload.d.ts +13 -0
- package/dist/permissions/modules/process-payload/process-payload.js +77 -0
- package/dist/permissions/modules/validate-access/lib/validate-collection-access.d.ts +12 -0
- package/dist/permissions/modules/validate-access/lib/validate-collection-access.js +11 -0
- package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +9 -0
- package/dist/permissions/modules/validate-access/lib/validate-item-access.js +33 -0
- package/dist/permissions/modules/validate-access/validate-access.d.ts +14 -0
- package/dist/permissions/modules/validate-access/validate-access.js +28 -0
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.d.ts +1 -0
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.js +8 -0
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.d.ts +5 -0
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.js +10 -0
- package/dist/permissions/types.d.ts +6 -0
- package/dist/permissions/types.js +1 -0
- package/dist/permissions/utils/create-default-accountability.d.ts +2 -0
- package/dist/permissions/utils/create-default-accountability.js +11 -0
- package/dist/permissions/utils/extract-required-dynamic-variable-context.d.ts +8 -0
- package/dist/permissions/utils/extract-required-dynamic-variable-context.js +27 -0
- package/dist/permissions/utils/fetch-dynamic-variable-context.d.ts +9 -0
- package/dist/permissions/utils/fetch-dynamic-variable-context.js +43 -0
- package/dist/permissions/utils/filter-policies-by-ip.d.ts +2 -0
- package/dist/permissions/utils/filter-policies-by-ip.js +15 -0
- package/dist/permissions/utils/get-unaliased-field-key.d.ts +5 -0
- package/dist/permissions/utils/get-unaliased-field-key.js +17 -0
- package/dist/permissions/utils/process-permissions.d.ts +7 -0
- package/dist/permissions/utils/process-permissions.js +9 -0
- package/dist/permissions/utils/with-cache.d.ts +10 -0
- package/dist/permissions/utils/with-cache.js +25 -0
- package/dist/server.js +17 -4
- package/dist/services/access.d.ts +10 -0
- package/dist/services/access.js +43 -0
- package/dist/services/activity.js +22 -10
- package/dist/services/assets.d.ts +2 -3
- package/dist/services/assets.js +10 -5
- package/dist/services/authentication.js +18 -18
- package/dist/services/collections.js +18 -17
- package/dist/services/fields.d.ts +0 -1
- package/dist/services/fields.js +54 -25
- package/dist/services/files.js +10 -3
- package/dist/services/graphql/index.d.ts +3 -3
- package/dist/services/graphql/index.js +126 -22
- package/dist/services/graphql/subscription.js +2 -4
- package/dist/services/import-export.d.ts +3 -1
- package/dist/services/import-export.js +67 -9
- package/dist/services/index.d.ts +3 -2
- package/dist/services/index.js +3 -2
- package/dist/services/items.js +115 -44
- package/dist/services/meta.js +60 -23
- package/dist/services/notifications.js +14 -6
- package/dist/services/payload.d.ts +9 -10
- package/dist/services/payload.js +18 -3
- package/dist/services/{permissions/index.d.ts → permissions.d.ts} +5 -7
- package/dist/services/{permissions/index.js → permissions.js} +30 -54
- package/dist/services/policies.d.ts +12 -0
- package/dist/services/policies.js +87 -0
- package/dist/services/relations.d.ts +0 -6
- package/dist/services/relations.js +27 -30
- package/dist/services/roles.d.ts +4 -12
- package/dist/services/roles.js +57 -424
- package/dist/services/shares.d.ts +0 -2
- package/dist/services/shares.js +12 -8
- package/dist/services/specifications.d.ts +2 -2
- package/dist/services/specifications.js +39 -27
- package/dist/services/users.d.ts +1 -5
- package/dist/services/users.js +78 -161
- package/dist/services/utils.js +11 -7
- package/dist/services/versions.d.ts +0 -2
- package/dist/services/versions.js +34 -10
- package/dist/telemetry/lib/get-report.js +2 -2
- package/dist/telemetry/utils/check-user-limits.d.ts +5 -0
- package/dist/telemetry/utils/check-user-limits.js +19 -0
- package/dist/types/ast.d.ts +43 -1
- package/dist/types/database.d.ts +1 -1
- package/dist/types/items.d.ts +11 -0
- package/dist/utils/apply-query.d.ts +11 -7
- package/dist/utils/apply-query.js +69 -11
- package/dist/utils/fetch-user-count/fetch-access-lookup.d.ts +19 -0
- package/dist/utils/fetch-user-count/fetch-access-lookup.js +23 -0
- package/dist/utils/fetch-user-count/fetch-access-roles.d.ts +16 -0
- package/dist/utils/fetch-user-count/fetch-access-roles.js +37 -0
- package/dist/utils/fetch-user-count/fetch-active-users.d.ts +6 -0
- package/dist/utils/fetch-user-count/fetch-active-users.js +3 -0
- package/dist/utils/fetch-user-count/fetch-user-count.d.ts +12 -0
- package/dist/utils/fetch-user-count/fetch-user-count.js +64 -0
- package/dist/utils/fetch-user-count/get-user-count-query.d.ts +20 -0
- package/dist/utils/fetch-user-count/get-user-count-query.js +17 -0
- package/dist/utils/get-accountability-for-role.js +16 -25
- package/dist/utils/get-accountability-for-token.js +17 -16
- package/dist/utils/get-address.d.ts +5 -0
- package/dist/utils/get-address.js +13 -0
- package/dist/utils/get-cache-key.d.ts +1 -1
- package/dist/utils/get-cache-key.js +12 -1
- package/dist/utils/get-column.d.ts +2 -1
- package/dist/utils/get-column.js +1 -0
- package/dist/utils/get-service.js +5 -1
- package/dist/utils/reduce-schema.d.ts +4 -6
- package/dist/utils/reduce-schema.js +16 -32
- package/dist/utils/sanitize-schema.d.ts +1 -1
- package/dist/utils/transaction.js +28 -11
- package/dist/utils/validate-user-count-integrity.d.ts +13 -0
- package/dist/utils/validate-user-count-integrity.js +29 -0
- package/dist/websocket/authenticate.d.ts +0 -2
- package/dist/websocket/authenticate.js +0 -12
- package/dist/websocket/controllers/graphql.js +3 -7
- package/dist/websocket/controllers/hooks.js +4 -0
- package/dist/websocket/controllers/rest.js +2 -5
- package/dist/websocket/handlers/subscribe.js +0 -2
- package/dist/websocket/utils/items.d.ts +1 -1
- package/package.json +31 -30
- package/dist/database/run-ast.js +0 -458
- package/dist/middleware/check-ip.d.ts +0 -2
- package/dist/middleware/check-ip.js +0 -37
- package/dist/middleware/get-permissions.d.ts +0 -3
- package/dist/middleware/get-permissions.js +0 -10
- package/dist/services/authorization.d.ts +0 -17
- package/dist/services/authorization.js +0 -456
- package/dist/services/permissions/lib/with-app-minimal-permissions.js +0 -13
- package/dist/telemetry/utils/check-increased-user-limits.d.ts +0 -7
- package/dist/telemetry/utils/check-increased-user-limits.js +0 -25
- package/dist/telemetry/utils/get-role-counts-by-roles.d.ts +0 -6
- package/dist/telemetry/utils/get-role-counts-by-roles.js +0 -27
- package/dist/telemetry/utils/get-role-counts-by-users.d.ts +0 -11
- package/dist/telemetry/utils/get-role-counts-by-users.js +0 -34
- package/dist/telemetry/utils/get-user-count.d.ts +0 -8
- package/dist/telemetry/utils/get-user-count.js +0 -33
- package/dist/telemetry/utils/get-user-counts-by-roles.d.ts +0 -7
- package/dist/telemetry/utils/get-user-counts-by-roles.js +0 -35
- package/dist/utils/get-ast-from-query.d.ts +0 -13
- package/dist/utils/get-ast-from-query.js +0 -297
- package/dist/utils/get-permissions.d.ts +0 -2
- package/dist/utils/get-permissions.js +0 -150
- package/dist/utils/merge-permissions-for-share.d.ts +0 -4
- package/dist/utils/merge-permissions-for-share.js +0 -109
- package/dist/utils/merge-permissions.js +0 -95
package/dist/services/items.js
CHANGED
|
@@ -5,15 +5,18 @@ import { isSystemCollection } from '@directus/system-data';
|
|
|
5
5
|
import { assign, clone, cloneDeep, omit, pick, without } from 'lodash-es';
|
|
6
6
|
import { getCache } from '../cache.js';
|
|
7
7
|
import { translateDatabaseError } from '../database/errors/translate.js';
|
|
8
|
+
import { getAstFromQuery } from '../database/get-ast-from-query/get-ast-from-query.js';
|
|
8
9
|
import { getHelpers } from '../database/helpers/index.js';
|
|
9
10
|
import getDatabase from '../database/index.js';
|
|
10
|
-
import
|
|
11
|
+
import { runAst } from '../database/run-ast/run-ast.js';
|
|
11
12
|
import emitter from '../emitter.js';
|
|
12
|
-
import
|
|
13
|
+
import { processAst } from '../permissions/modules/process-ast/process-ast.js';
|
|
14
|
+
import { processPayload } from '../permissions/modules/process-payload/process-payload.js';
|
|
15
|
+
import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
|
|
13
16
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
14
17
|
import { transaction } from '../utils/transaction.js';
|
|
15
18
|
import { validateKeys } from '../utils/validate-keys.js';
|
|
16
|
-
import {
|
|
19
|
+
import { UserIntegrityCheckFlag, validateUserCountIntegrity } from '../utils/validate-user-count-integrity.js';
|
|
17
20
|
import { PayloadService } from './payload.js';
|
|
18
21
|
const env = useEnv();
|
|
19
22
|
export class ItemsService {
|
|
@@ -98,17 +101,13 @@ export class ItemsService {
|
|
|
98
101
|
// that any errors thrown in any nested relational changes will bubble up and cancel the whole
|
|
99
102
|
// update tree
|
|
100
103
|
const primaryKey = await transaction(this.knex, async (trx) => {
|
|
101
|
-
|
|
102
|
-
const payloadService = new PayloadService(this.collection, {
|
|
103
|
-
accountability: this.accountability,
|
|
104
|
-
knex: trx,
|
|
105
|
-
schema: this.schema,
|
|
106
|
-
});
|
|
107
|
-
const authorizationService = new AuthorizationService({
|
|
104
|
+
const serviceOptions = {
|
|
108
105
|
accountability: this.accountability,
|
|
109
106
|
knex: trx,
|
|
110
107
|
schema: this.schema,
|
|
111
|
-
}
|
|
108
|
+
};
|
|
109
|
+
// We're creating new services instances so they can use the transaction as their Knex interface
|
|
110
|
+
const payloadService = new PayloadService(this.collection, serviceOptions);
|
|
112
111
|
// Run all hooks that are attached to this event so the end user has the chance to augment the
|
|
113
112
|
// item that is about to be saved
|
|
114
113
|
const payloadAfterHooks = opts.emitEvents !== false
|
|
@@ -123,13 +122,21 @@ export class ItemsService {
|
|
|
123
122
|
})
|
|
124
123
|
: payload;
|
|
125
124
|
const payloadWithPresets = this.accountability
|
|
126
|
-
?
|
|
125
|
+
? await processPayload({
|
|
126
|
+
accountability: this.accountability,
|
|
127
|
+
action: 'create',
|
|
128
|
+
collection: this.collection,
|
|
129
|
+
payload: payloadAfterHooks,
|
|
130
|
+
}, {
|
|
131
|
+
knex: trx,
|
|
132
|
+
schema: this.schema,
|
|
133
|
+
})
|
|
127
134
|
: payloadAfterHooks;
|
|
128
135
|
if (opts.preMutationError) {
|
|
129
136
|
throw opts.preMutationError;
|
|
130
137
|
}
|
|
131
|
-
const { payload: payloadWithM2O, revisions: revisionsM2O, nestedActionEvents: nestedActionEventsM2O, } = await payloadService.processM2O(payloadWithPresets, opts);
|
|
132
|
-
const { payload: payloadWithA2O, revisions: revisionsA2O, nestedActionEvents: nestedActionEventsA2O, } = await payloadService.processA2O(payloadWithM2O, opts);
|
|
138
|
+
const { payload: payloadWithM2O, revisions: revisionsM2O, nestedActionEvents: nestedActionEventsM2O, userIntegrityCheckFlags: userIntegrityCheckFlagsM2O, } = await payloadService.processM2O(payloadWithPresets, opts);
|
|
139
|
+
const { payload: payloadWithA2O, revisions: revisionsA2O, nestedActionEvents: nestedActionEventsA2O, userIntegrityCheckFlags: userIntegrityCheckFlagsA2O, } = await payloadService.processA2O(payloadWithM2O, opts);
|
|
133
140
|
const payloadWithoutAliases = pick(payloadWithA2O, without(fields, ...aliases));
|
|
134
141
|
const payloadWithTypeCasting = await payloadService.processValues('create', payloadWithoutAliases);
|
|
135
142
|
// The primary key can already exist in the payload.
|
|
@@ -187,10 +194,22 @@ export class ItemsService {
|
|
|
187
194
|
}
|
|
188
195
|
// At this point, the primary key is guaranteed to be set.
|
|
189
196
|
primaryKey = primaryKey;
|
|
190
|
-
const { revisions: revisionsO2M, nestedActionEvents: nestedActionEventsO2M } = await payloadService.processO2M(payloadWithPresets, primaryKey, opts);
|
|
197
|
+
const { revisions: revisionsO2M, nestedActionEvents: nestedActionEventsO2M, userIntegrityCheckFlags: userIntegrityCheckFlagsO2M, } = await payloadService.processO2M(payloadWithPresets, primaryKey, opts);
|
|
191
198
|
nestedActionEvents.push(...nestedActionEventsM2O);
|
|
192
199
|
nestedActionEvents.push(...nestedActionEventsA2O);
|
|
193
200
|
nestedActionEvents.push(...nestedActionEventsO2M);
|
|
201
|
+
const userIntegrityCheckFlags = (opts.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None) |
|
|
202
|
+
userIntegrityCheckFlagsM2O |
|
|
203
|
+
userIntegrityCheckFlagsA2O |
|
|
204
|
+
userIntegrityCheckFlagsO2M;
|
|
205
|
+
if (userIntegrityCheckFlags) {
|
|
206
|
+
if (opts.onRequireUserIntegrityCheck) {
|
|
207
|
+
opts.onRequireUserIntegrityCheck(userIntegrityCheckFlags);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
await validateUserCountIntegrity({ flags: userIntegrityCheckFlags, knex: trx });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
194
213
|
// If this is an authenticated action, and accountability tracking is enabled, save activity row
|
|
195
214
|
if (this.accountability && this.schema.collections[this.collection].accountability !== null) {
|
|
196
215
|
const activityService = new ActivityService({
|
|
@@ -281,6 +300,7 @@ export class ItemsService {
|
|
|
281
300
|
opts.mutationTracker = this.createMutationTracker();
|
|
282
301
|
const { primaryKeys, nestedActionEvents } = await transaction(this.knex, async (knex) => {
|
|
283
302
|
const service = this.fork({ knex });
|
|
303
|
+
let userIntegrityCheckFlags = opts.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None;
|
|
284
304
|
const primaryKeys = [];
|
|
285
305
|
const nestedActionEvents = [];
|
|
286
306
|
const pkField = this.schema.collections[this.collection].primary;
|
|
@@ -294,12 +314,21 @@ export class ItemsService {
|
|
|
294
314
|
const primaryKey = await service.createOne(payload, {
|
|
295
315
|
...(opts || {}),
|
|
296
316
|
autoPurgeCache: false,
|
|
317
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
297
318
|
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
298
319
|
mutationTracker: opts.mutationTracker,
|
|
299
320
|
bypassAutoIncrementSequenceReset,
|
|
300
321
|
});
|
|
301
322
|
primaryKeys.push(primaryKey);
|
|
302
323
|
}
|
|
324
|
+
if (userIntegrityCheckFlags) {
|
|
325
|
+
if (opts.onRequireUserIntegrityCheck) {
|
|
326
|
+
opts.onRequireUserIntegrityCheck(userIntegrityCheckFlags);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
await validateUserCountIntegrity({ flags: userIntegrityCheckFlags, knex });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
303
332
|
return { primaryKeys, nestedActionEvents };
|
|
304
333
|
});
|
|
305
334
|
if (opts.emitEvents !== false) {
|
|
@@ -332,27 +361,21 @@ export class ItemsService {
|
|
|
332
361
|
accountability: this.accountability,
|
|
333
362
|
})
|
|
334
363
|
: query;
|
|
335
|
-
let ast = await
|
|
364
|
+
let ast = await getAstFromQuery({
|
|
365
|
+
collection: this.collection,
|
|
366
|
+
query: updatedQuery,
|
|
336
367
|
accountability: this.accountability,
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
// access to (a) certain item(s)
|
|
340
|
-
action: opts?.permissionsAction || 'read',
|
|
368
|
+
}, {
|
|
369
|
+
schema: this.schema,
|
|
341
370
|
knex: this.knex,
|
|
342
371
|
});
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
accountability: this.accountability,
|
|
346
|
-
knex: this.knex,
|
|
347
|
-
schema: this.schema,
|
|
348
|
-
});
|
|
349
|
-
ast = await authorizationService.processAST(ast, opts?.permissionsAction);
|
|
350
|
-
}
|
|
351
|
-
const records = await runAST(ast, this.schema, {
|
|
372
|
+
ast = await processAst({ ast, action: 'read', accountability: this.accountability }, { knex: this.knex, schema: this.schema });
|
|
373
|
+
const records = await runAst(ast, this.schema, {
|
|
352
374
|
knex: this.knex,
|
|
353
375
|
// GraphQL requires relational keys to be returned regardless
|
|
354
376
|
stripNonRequested: opts?.stripNonRequested !== undefined ? opts.stripNonRequested : true,
|
|
355
377
|
});
|
|
378
|
+
// TODO when would this happen?
|
|
356
379
|
if (records === null) {
|
|
357
380
|
throw new ForbiddenError();
|
|
358
381
|
}
|
|
@@ -450,13 +473,26 @@ export class ItemsService {
|
|
|
450
473
|
try {
|
|
451
474
|
await transaction(this.knex, async (knex) => {
|
|
452
475
|
const service = this.fork({ knex });
|
|
476
|
+
let userIntegrityCheckFlags = opts.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None;
|
|
453
477
|
for (const item of data) {
|
|
454
478
|
const primaryKey = item[primaryKeyField];
|
|
455
479
|
if (!primaryKey)
|
|
456
480
|
throw new InvalidPayloadError({ reason: `Item in update misses primary key` });
|
|
457
|
-
const combinedOpts =
|
|
481
|
+
const combinedOpts = {
|
|
482
|
+
autoPurgeCache: false,
|
|
483
|
+
...opts,
|
|
484
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
485
|
+
};
|
|
458
486
|
keys.push(await service.updateOne(primaryKey, omit(item, primaryKeyField), combinedOpts));
|
|
459
487
|
}
|
|
488
|
+
if (userIntegrityCheckFlags) {
|
|
489
|
+
if (opts.onRequireUserIntegrityCheck) {
|
|
490
|
+
opts.onRequireUserIntegrityCheck(userIntegrityCheckFlags);
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
await validateUserCountIntegrity({ flags: userIntegrityCheckFlags, knex });
|
|
494
|
+
}
|
|
495
|
+
}
|
|
460
496
|
});
|
|
461
497
|
}
|
|
462
498
|
finally {
|
|
@@ -485,11 +521,6 @@ export class ItemsService {
|
|
|
485
521
|
.map((field) => field.field);
|
|
486
522
|
const payload = cloneDeep(data);
|
|
487
523
|
const nestedActionEvents = [];
|
|
488
|
-
const authorizationService = new AuthorizationService({
|
|
489
|
-
accountability: this.accountability,
|
|
490
|
-
knex: this.knex,
|
|
491
|
-
schema: this.schema,
|
|
492
|
-
});
|
|
493
524
|
// Run all hooks that are attached to this event so the end user has the chance to augment the
|
|
494
525
|
// item that is about to be saved
|
|
495
526
|
const payloadAfterHooks = opts.emitEvents !== false
|
|
@@ -507,10 +538,26 @@ export class ItemsService {
|
|
|
507
538
|
// Sort keys to ensure that the order is maintained
|
|
508
539
|
keys.sort();
|
|
509
540
|
if (this.accountability) {
|
|
510
|
-
await
|
|
541
|
+
await validateAccess({
|
|
542
|
+
accountability: this.accountability,
|
|
543
|
+
action: 'update',
|
|
544
|
+
collection: this.collection,
|
|
545
|
+
primaryKeys: keys,
|
|
546
|
+
}, {
|
|
547
|
+
schema: this.schema,
|
|
548
|
+
knex: this.knex,
|
|
549
|
+
});
|
|
511
550
|
}
|
|
512
551
|
const payloadWithPresets = this.accountability
|
|
513
|
-
?
|
|
552
|
+
? await processPayload({
|
|
553
|
+
accountability: this.accountability,
|
|
554
|
+
action: 'update',
|
|
555
|
+
collection: this.collection,
|
|
556
|
+
payload: payloadAfterHooks,
|
|
557
|
+
}, {
|
|
558
|
+
knex: this.knex,
|
|
559
|
+
schema: this.schema,
|
|
560
|
+
})
|
|
514
561
|
: payloadAfterHooks;
|
|
515
562
|
if (opts.preMutationError) {
|
|
516
563
|
throw opts.preMutationError;
|
|
@@ -521,8 +568,8 @@ export class ItemsService {
|
|
|
521
568
|
knex: trx,
|
|
522
569
|
schema: this.schema,
|
|
523
570
|
});
|
|
524
|
-
const { payload: payloadWithM2O, revisions: revisionsM2O, nestedActionEvents: nestedActionEventsM2O, } = await payloadService.processM2O(payloadWithPresets, opts);
|
|
525
|
-
const { payload: payloadWithA2O, revisions: revisionsA2O, nestedActionEvents: nestedActionEventsA2O, } = await payloadService.processA2O(payloadWithM2O, opts);
|
|
571
|
+
const { payload: payloadWithM2O, revisions: revisionsM2O, nestedActionEvents: nestedActionEventsM2O, userIntegrityCheckFlags: userIntegrityCheckFlagsM2O, } = await payloadService.processM2O(payloadWithPresets, opts);
|
|
572
|
+
const { payload: payloadWithA2O, revisions: revisionsA2O, nestedActionEvents: nestedActionEventsA2O, userIntegrityCheckFlags: userIntegrityCheckFlagsA2O, } = await payloadService.processA2O(payloadWithM2O, opts);
|
|
526
573
|
const payloadWithoutAliasAndPK = pick(payloadWithA2O, without(fields, primaryKeyField, ...aliases));
|
|
527
574
|
const payloadWithTypeCasting = await payloadService.processValues('update', payloadWithoutAliasAndPK);
|
|
528
575
|
if (Object.keys(payloadWithTypeCasting).length > 0) {
|
|
@@ -534,12 +581,25 @@ export class ItemsService {
|
|
|
534
581
|
}
|
|
535
582
|
}
|
|
536
583
|
const childrenRevisions = [...revisionsM2O, ...revisionsA2O];
|
|
584
|
+
let userIntegrityCheckFlags = opts.userIntegrityCheckFlags ??
|
|
585
|
+
UserIntegrityCheckFlag.None | userIntegrityCheckFlagsM2O | userIntegrityCheckFlagsA2O;
|
|
537
586
|
nestedActionEvents.push(...nestedActionEventsM2O);
|
|
538
587
|
nestedActionEvents.push(...nestedActionEventsA2O);
|
|
539
588
|
for (const key of keys) {
|
|
540
|
-
const { revisions, nestedActionEvents: nestedActionEventsO2M } = await payloadService.processO2M(payloadWithA2O, key, opts);
|
|
589
|
+
const { revisions, nestedActionEvents: nestedActionEventsO2M, userIntegrityCheckFlags: userIntegrityCheckFlagsO2M, } = await payloadService.processO2M(payloadWithA2O, key, opts);
|
|
541
590
|
childrenRevisions.push(...revisions);
|
|
542
591
|
nestedActionEvents.push(...nestedActionEventsO2M);
|
|
592
|
+
userIntegrityCheckFlags |= userIntegrityCheckFlagsO2M;
|
|
593
|
+
}
|
|
594
|
+
if (userIntegrityCheckFlags) {
|
|
595
|
+
if (opts?.onRequireUserIntegrityCheck) {
|
|
596
|
+
opts.onRequireUserIntegrityCheck(userIntegrityCheckFlags);
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
// Having no onRequireUserIntegrityCheck callback indicates that
|
|
600
|
+
// this is the top level invocation of the nested updates, so perform the user integrity check
|
|
601
|
+
await validateUserCountIntegrity({ flags: userIntegrityCheckFlags, knex: trx });
|
|
602
|
+
}
|
|
543
603
|
}
|
|
544
604
|
// If this is an authenticated action, and accountability tracking is enabled, save activity row
|
|
545
605
|
if (this.accountability && this.schema.collections[this.collection].accountability !== null) {
|
|
@@ -708,13 +768,16 @@ export class ItemsService {
|
|
|
708
768
|
const { ActivityService } = await import('./activity.js');
|
|
709
769
|
const primaryKeyField = this.schema.collections[this.collection].primary;
|
|
710
770
|
validateKeys(this.schema, this.collection, primaryKeyField, keys);
|
|
711
|
-
if (this.accountability
|
|
712
|
-
|
|
771
|
+
if (this.accountability) {
|
|
772
|
+
await validateAccess({
|
|
713
773
|
accountability: this.accountability,
|
|
714
|
-
|
|
774
|
+
action: 'delete',
|
|
775
|
+
collection: this.collection,
|
|
776
|
+
primaryKeys: keys,
|
|
777
|
+
}, {
|
|
715
778
|
knex: this.knex,
|
|
779
|
+
schema: this.schema,
|
|
716
780
|
});
|
|
717
|
-
await authorizationService.checkAccess('delete', this.collection, keys);
|
|
718
781
|
}
|
|
719
782
|
if (opts.preMutationError) {
|
|
720
783
|
throw opts.preMutationError;
|
|
@@ -730,6 +793,14 @@ export class ItemsService {
|
|
|
730
793
|
}
|
|
731
794
|
await transaction(this.knex, async (trx) => {
|
|
732
795
|
await trx(this.collection).whereIn(primaryKeyField, keys).delete();
|
|
796
|
+
if (opts.userIntegrityCheckFlags) {
|
|
797
|
+
if (opts.onRequireUserIntegrityCheck) {
|
|
798
|
+
opts.onRequireUserIntegrityCheck(opts.userIntegrityCheckFlags);
|
|
799
|
+
}
|
|
800
|
+
else {
|
|
801
|
+
await validateUserCountIntegrity({ flags: opts.userIntegrityCheckFlags, knex: trx });
|
|
802
|
+
}
|
|
803
|
+
}
|
|
733
804
|
if (this.accountability && this.schema.collections[this.collection].accountability !== null) {
|
|
734
805
|
const activityService = new ActivityService({
|
|
735
806
|
knex: trx,
|
package/dist/services/meta.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import getDatabase from '../database/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import { fetchPermissions } from '../permissions/lib/fetch-permissions.js';
|
|
3
|
+
import { fetchPolicies } from '../permissions/lib/fetch-policies.js';
|
|
4
|
+
import { dedupeAccess } from '../permissions/modules/process-ast/utils/dedupe-access.js';
|
|
5
|
+
import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
|
|
3
6
|
import { applyFilter, applySearch } from '../utils/apply-query.js';
|
|
4
7
|
export class MetaService {
|
|
5
8
|
knex;
|
|
@@ -28,39 +31,73 @@ export class MetaService {
|
|
|
28
31
|
}, {});
|
|
29
32
|
}
|
|
30
33
|
async totalCount(collection) {
|
|
31
|
-
const dbQuery = this.knex(collection)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
const dbQuery = this.knex(collection);
|
|
35
|
+
let hasJoins = false;
|
|
36
|
+
if (this.accountability && this.accountability.admin === false) {
|
|
37
|
+
const context = { knex: this.knex, schema: this.schema };
|
|
38
|
+
await validateAccess({
|
|
39
|
+
accountability: this.accountability,
|
|
40
|
+
action: 'read',
|
|
41
|
+
collection,
|
|
42
|
+
}, context);
|
|
43
|
+
const policies = await fetchPolicies(this.accountability, context);
|
|
44
|
+
const permissions = await fetchPermissions({
|
|
45
|
+
action: 'read',
|
|
46
|
+
policies,
|
|
47
|
+
accountability: this.accountability,
|
|
48
|
+
...(collection ? { collections: [collection] } : {}),
|
|
49
|
+
}, context);
|
|
50
|
+
const rules = dedupeAccess(permissions);
|
|
51
|
+
const cases = rules.map(({ rule }) => rule);
|
|
52
|
+
const filter = {
|
|
53
|
+
_or: cases,
|
|
54
|
+
};
|
|
55
|
+
const result = applyFilter(this.knex, this.schema, dbQuery, filter, collection, {}, cases);
|
|
56
|
+
hasJoins = result.hasJoins;
|
|
57
|
+
}
|
|
58
|
+
if (hasJoins) {
|
|
59
|
+
const primaryKeyName = this.schema.collections[collection].primary;
|
|
60
|
+
dbQuery.countDistinct({ count: [`${collection}.${primaryKeyName}`] });
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
dbQuery.count('*', { as: 'count' });
|
|
40
64
|
}
|
|
41
|
-
const result = await dbQuery;
|
|
65
|
+
const result = await dbQuery.first();
|
|
42
66
|
return Number(result?.count ?? 0);
|
|
43
67
|
}
|
|
44
68
|
async filterCount(collection, query) {
|
|
45
69
|
const dbQuery = this.knex(collection);
|
|
46
70
|
let filter = query.filter || {};
|
|
47
71
|
let hasJoins = false;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
72
|
+
let cases = [];
|
|
73
|
+
if (this.accountability && this.accountability.admin === false) {
|
|
74
|
+
const context = { knex: this.knex, schema: this.schema };
|
|
75
|
+
await validateAccess({
|
|
76
|
+
accountability: this.accountability,
|
|
77
|
+
action: 'read',
|
|
78
|
+
collection,
|
|
79
|
+
}, context);
|
|
80
|
+
const policies = await fetchPolicies(this.accountability, context);
|
|
81
|
+
const permissions = await fetchPermissions({
|
|
82
|
+
action: 'read',
|
|
83
|
+
policies,
|
|
84
|
+
accountability: this.accountability,
|
|
85
|
+
...(collection ? { collections: [collection] } : {}),
|
|
86
|
+
}, context);
|
|
87
|
+
const rules = dedupeAccess(permissions);
|
|
88
|
+
cases = rules.map(({ rule }) => rule);
|
|
89
|
+
const permissionsFilter = {
|
|
90
|
+
_or: cases,
|
|
91
|
+
};
|
|
55
92
|
if (Object.keys(filter).length > 0) {
|
|
56
|
-
filter = { _and: [
|
|
93
|
+
filter = { _and: [permissionsFilter, filter] };
|
|
57
94
|
}
|
|
58
95
|
else {
|
|
59
|
-
filter =
|
|
96
|
+
filter = permissionsFilter;
|
|
60
97
|
}
|
|
61
98
|
}
|
|
62
99
|
if (Object.keys(filter).length > 0) {
|
|
63
|
-
({ hasJoins } = applyFilter(this.knex, this.schema, dbQuery, filter, collection, {}));
|
|
100
|
+
({ hasJoins } = applyFilter(this.knex, this.schema, dbQuery, filter, collection, {}, cases));
|
|
64
101
|
}
|
|
65
102
|
if (query.search) {
|
|
66
103
|
applySearch(this.knex, this.schema, dbQuery, query.search, collection);
|
|
@@ -72,7 +109,7 @@ export class MetaService {
|
|
|
72
109
|
else {
|
|
73
110
|
dbQuery.count('*', { as: 'count' });
|
|
74
111
|
}
|
|
75
|
-
const
|
|
76
|
-
return Number(
|
|
112
|
+
const result = await dbQuery.first();
|
|
113
|
+
return Number(result?.count ?? 0);
|
|
77
114
|
}
|
|
78
115
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { useLogger } from '../logger/index.js';
|
|
3
|
+
import { fetchRolesTree } from '../permissions/lib/fetch-roles-tree.js';
|
|
4
|
+
import { fetchGlobalAccess } from '../permissions/modules/fetch-global-access/fetch-global-access.js';
|
|
3
5
|
import { md } from '../utils/md.js';
|
|
4
6
|
import { Url } from '../utils/url.js';
|
|
5
7
|
import { ItemsService } from './items.js';
|
|
@@ -23,18 +25,24 @@ export class NotificationsService extends ItemsService {
|
|
|
23
25
|
async sendEmail(data) {
|
|
24
26
|
if (data.recipient) {
|
|
25
27
|
const user = await this.usersService.readOne(data.recipient, {
|
|
26
|
-
fields: ['id', 'email', 'email_notifications', 'role
|
|
28
|
+
fields: ['id', 'email', 'email_notifications', 'role'],
|
|
27
29
|
});
|
|
28
|
-
const manageUserAccountUrl = new Url(env['PUBLIC_URL'])
|
|
29
|
-
.addPath('admin', 'users', user['id'])
|
|
30
|
-
.toString();
|
|
31
|
-
const html = data.message ? md(data.message) : '';
|
|
32
30
|
if (user['email'] && user['email_notifications'] === true) {
|
|
31
|
+
const manageUserAccountUrl = new Url(env['PUBLIC_URL'])
|
|
32
|
+
.addPath('admin', 'users', user['id'])
|
|
33
|
+
.toString();
|
|
34
|
+
const html = data.message ? md(data.message) : '';
|
|
35
|
+
const roles = await fetchRolesTree(user['role'], this.knex);
|
|
36
|
+
const { app: app_access } = await fetchGlobalAccess({
|
|
37
|
+
user: user['id'],
|
|
38
|
+
roles,
|
|
39
|
+
ip: null,
|
|
40
|
+
}, this.knex);
|
|
33
41
|
this.mailService
|
|
34
42
|
.send({
|
|
35
43
|
template: {
|
|
36
44
|
name: 'base',
|
|
37
|
-
data:
|
|
45
|
+
data: app_access ? { url: manageUserAccountUrl, html } : { html },
|
|
38
46
|
},
|
|
39
47
|
to: user['email'],
|
|
40
48
|
subject: data.subject,
|
|
@@ -2,6 +2,7 @@ import type { Accountability, Item, PrimaryKey, SchemaOverview } from '@directus
|
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
3
|
import type { Helpers } from '../database/helpers/index.js';
|
|
4
4
|
import type { AbstractServiceOptions, ActionEventParams, MutationOptions } from '../types/index.js';
|
|
5
|
+
import { UserIntegrityCheckFlag } from '../utils/validate-user-count-integrity.js';
|
|
5
6
|
type Action = 'create' | 'read' | 'update';
|
|
6
7
|
type Transformers = {
|
|
7
8
|
[type: string]: (context: {
|
|
@@ -13,6 +14,11 @@ type Transformers = {
|
|
|
13
14
|
helpers: Helpers;
|
|
14
15
|
}) => Promise<any>;
|
|
15
16
|
};
|
|
17
|
+
type PayloadServiceProcessRelationResult = {
|
|
18
|
+
revisions: PrimaryKey[];
|
|
19
|
+
nestedActionEvents: ActionEventParams[];
|
|
20
|
+
userIntegrityCheckFlags: UserIntegrityCheckFlag;
|
|
21
|
+
};
|
|
16
22
|
/**
|
|
17
23
|
* Process a given payload for a collection to ensure the special fields (hash, uuid, date etc) are
|
|
18
24
|
* handled correctly.
|
|
@@ -46,26 +52,19 @@ export declare class PayloadService {
|
|
|
46
52
|
/**
|
|
47
53
|
* Recursively save/update all nested related Any-to-One items
|
|
48
54
|
*/
|
|
49
|
-
processA2O(data: Partial<Item>, opts?: MutationOptions): Promise<{
|
|
55
|
+
processA2O(data: Partial<Item>, opts?: MutationOptions): Promise<PayloadServiceProcessRelationResult & {
|
|
50
56
|
payload: Partial<Item>;
|
|
51
|
-
revisions: PrimaryKey[];
|
|
52
|
-
nestedActionEvents: ActionEventParams[];
|
|
53
57
|
}>;
|
|
54
58
|
/**
|
|
55
59
|
* Save/update all nested related m2o items inside the payload
|
|
56
60
|
*/
|
|
57
|
-
processM2O(data: Partial<Item>, opts?: MutationOptions): Promise<{
|
|
61
|
+
processM2O(data: Partial<Item>, opts?: MutationOptions): Promise<PayloadServiceProcessRelationResult & {
|
|
58
62
|
payload: Partial<Item>;
|
|
59
|
-
revisions: PrimaryKey[];
|
|
60
|
-
nestedActionEvents: ActionEventParams[];
|
|
61
63
|
}>;
|
|
62
64
|
/**
|
|
63
65
|
* Recursively save/update all nested related o2m items
|
|
64
66
|
*/
|
|
65
|
-
processO2M(data: Partial<Item>, parent: PrimaryKey, opts?: MutationOptions): Promise<
|
|
66
|
-
revisions: PrimaryKey[];
|
|
67
|
-
nestedActionEvents: ActionEventParams[];
|
|
68
|
-
}>;
|
|
67
|
+
processO2M(data: Partial<Item>, parent: PrimaryKey, opts?: MutationOptions): Promise<PayloadServiceProcessRelationResult>;
|
|
69
68
|
/**
|
|
70
69
|
* Transforms the input partial payload to match the output structure, to have consistency
|
|
71
70
|
* between delta and data
|
package/dist/services/payload.js
CHANGED
|
@@ -9,6 +9,7 @@ import { parse as wktToGeoJSON } from 'wellknown';
|
|
|
9
9
|
import { getHelpers } from '../database/helpers/index.js';
|
|
10
10
|
import getDatabase from '../database/index.js';
|
|
11
11
|
import { generateHash } from '../utils/generate-hash.js';
|
|
12
|
+
import { UserIntegrityCheckFlag } from '../utils/validate-user-count-integrity.js';
|
|
12
13
|
/**
|
|
13
14
|
* Process a given payload for a collection to ensure the special fields (hash, uuid, date etc) are
|
|
14
15
|
* handled correctly.
|
|
@@ -317,6 +318,7 @@ export class PayloadService {
|
|
|
317
318
|
return relation.collection === this.collection;
|
|
318
319
|
});
|
|
319
320
|
const revisions = [];
|
|
321
|
+
let userIntegrityCheckFlags = UserIntegrityCheckFlag.None;
|
|
320
322
|
const nestedActionEvents = [];
|
|
321
323
|
const payload = cloneDeep(data);
|
|
322
324
|
// Only process related records that are actually in the payload
|
|
@@ -362,6 +364,7 @@ export class PayloadService {
|
|
|
362
364
|
if (Object.keys(fieldsToUpdate).length > 0) {
|
|
363
365
|
await service.updateOne(relatedPrimaryKey, relatedRecord, {
|
|
364
366
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
367
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
365
368
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
366
369
|
emitEvents: opts?.emitEvents,
|
|
367
370
|
mutationTracker: opts?.mutationTracker,
|
|
@@ -371,6 +374,7 @@ export class PayloadService {
|
|
|
371
374
|
else {
|
|
372
375
|
relatedPrimaryKey = await service.createOne(relatedRecord, {
|
|
373
376
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
377
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
374
378
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
375
379
|
emitEvents: opts?.emitEvents,
|
|
376
380
|
mutationTracker: opts?.mutationTracker,
|
|
@@ -379,7 +383,7 @@ export class PayloadService {
|
|
|
379
383
|
// Overwrite the nested object with just the primary key, so the parent level can be saved correctly
|
|
380
384
|
payload[relation.field] = relatedPrimaryKey;
|
|
381
385
|
}
|
|
382
|
-
return { payload, revisions, nestedActionEvents };
|
|
386
|
+
return { payload, revisions, nestedActionEvents, userIntegrityCheckFlags };
|
|
383
387
|
}
|
|
384
388
|
/**
|
|
385
389
|
* Save/update all nested related m2o items inside the payload
|
|
@@ -388,6 +392,7 @@ export class PayloadService {
|
|
|
388
392
|
const payload = cloneDeep(data);
|
|
389
393
|
// All the revisions saved on this level
|
|
390
394
|
const revisions = [];
|
|
395
|
+
let userIntegrityCheckFlags = UserIntegrityCheckFlag.None;
|
|
391
396
|
const nestedActionEvents = [];
|
|
392
397
|
// Many to one relations that exist on the current collection
|
|
393
398
|
const relations = this.schema.relations.filter((relation) => {
|
|
@@ -424,6 +429,7 @@ export class PayloadService {
|
|
|
424
429
|
if (Object.keys(fieldsToUpdate).length > 0) {
|
|
425
430
|
await service.updateOne(relatedPrimaryKey, relatedRecord, {
|
|
426
431
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
432
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
427
433
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
428
434
|
emitEvents: opts?.emitEvents,
|
|
429
435
|
mutationTracker: opts?.mutationTracker,
|
|
@@ -433,6 +439,7 @@ export class PayloadService {
|
|
|
433
439
|
else {
|
|
434
440
|
relatedPrimaryKey = await service.createOne(relatedRecord, {
|
|
435
441
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
442
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
436
443
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
437
444
|
emitEvents: opts?.emitEvents,
|
|
438
445
|
mutationTracker: opts?.mutationTracker,
|
|
@@ -441,13 +448,14 @@ export class PayloadService {
|
|
|
441
448
|
// Overwrite the nested object with just the primary key, so the parent level can be saved correctly
|
|
442
449
|
payload[relation.field] = relatedPrimaryKey;
|
|
443
450
|
}
|
|
444
|
-
return { payload, revisions, nestedActionEvents };
|
|
451
|
+
return { payload, revisions, nestedActionEvents, userIntegrityCheckFlags };
|
|
445
452
|
}
|
|
446
453
|
/**
|
|
447
454
|
* Recursively save/update all nested related o2m items
|
|
448
455
|
*/
|
|
449
456
|
async processO2M(data, parent, opts) {
|
|
450
457
|
const revisions = [];
|
|
458
|
+
let userIntegrityCheckFlags = UserIntegrityCheckFlag.None;
|
|
451
459
|
const nestedActionEvents = [];
|
|
452
460
|
const relations = this.schema.relations.filter((relation) => {
|
|
453
461
|
return relation.related_collection === this.collection;
|
|
@@ -516,6 +524,7 @@ export class PayloadService {
|
|
|
516
524
|
}
|
|
517
525
|
savedPrimaryKeys.push(...(await service.upsertMany(recordsToUpsert, {
|
|
518
526
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
527
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
519
528
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
520
529
|
emitEvents: opts?.emitEvents,
|
|
521
530
|
mutationTracker: opts?.mutationTracker,
|
|
@@ -540,6 +549,7 @@ export class PayloadService {
|
|
|
540
549
|
if (relation.meta.one_deselect_action === 'delete') {
|
|
541
550
|
// There's no revision for a deletion
|
|
542
551
|
await service.deleteByQuery(query, {
|
|
552
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
543
553
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
544
554
|
emitEvents: opts?.emitEvents,
|
|
545
555
|
mutationTracker: opts?.mutationTracker,
|
|
@@ -548,6 +558,7 @@ export class PayloadService {
|
|
|
548
558
|
else {
|
|
549
559
|
await service.updateByQuery(query, { [relation.field]: null }, {
|
|
550
560
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
561
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
551
562
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
552
563
|
emitEvents: opts?.emitEvents,
|
|
553
564
|
mutationTracker: opts?.mutationTracker,
|
|
@@ -590,6 +601,7 @@ export class PayloadService {
|
|
|
590
601
|
}
|
|
591
602
|
await service.createMany(createPayload, {
|
|
592
603
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
604
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
593
605
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
594
606
|
emitEvents: opts?.emitEvents,
|
|
595
607
|
mutationTracker: opts?.mutationTracker,
|
|
@@ -603,6 +615,7 @@ export class PayloadService {
|
|
|
603
615
|
[relation.field]: parent || payload[currentPrimaryKeyField],
|
|
604
616
|
}, {
|
|
605
617
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
618
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
606
619
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
607
620
|
emitEvents: opts?.emitEvents,
|
|
608
621
|
mutationTracker: opts?.mutationTracker,
|
|
@@ -628,6 +641,7 @@ export class PayloadService {
|
|
|
628
641
|
};
|
|
629
642
|
if (relation.meta.one_deselect_action === 'delete') {
|
|
630
643
|
await service.deleteByQuery(query, {
|
|
644
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
631
645
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
632
646
|
emitEvents: opts?.emitEvents,
|
|
633
647
|
mutationTracker: opts?.mutationTracker,
|
|
@@ -636,6 +650,7 @@ export class PayloadService {
|
|
|
636
650
|
else {
|
|
637
651
|
await service.updateByQuery(query, { [relation.field]: null }, {
|
|
638
652
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
653
|
+
onRequireUserIntegrityCheck: (flags) => (userIntegrityCheckFlags |= flags),
|
|
639
654
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
640
655
|
emitEvents: opts?.emitEvents,
|
|
641
656
|
mutationTracker: opts?.mutationTracker,
|
|
@@ -644,7 +659,7 @@ export class PayloadService {
|
|
|
644
659
|
}
|
|
645
660
|
}
|
|
646
661
|
}
|
|
647
|
-
return { revisions, nestedActionEvents };
|
|
662
|
+
return { revisions, nestedActionEvents, userIntegrityCheckFlags };
|
|
648
663
|
}
|
|
649
664
|
/**
|
|
650
665
|
* Transforms the input partial payload to match the output structure, to have consistency
|