@directus/api 19.3.0 → 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.
- 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/utils/defaults.d.ts +4 -11
- package/dist/cli/utils/defaults.js +7 -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/users.js +0 -55
- package/dist/database/errors/dialects/mysql.js +23 -23
- 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 +190 -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/index.js +2 -1
- package/dist/database/migrations/20240619A-permissions-policies.d.ts +3 -0
- package/dist/database/migrations/20240619A-permissions-policies.js +163 -0
- package/dist/database/run-ast/lib/get-db-query.d.ts +4 -0
- package/dist/database/run-ast/lib/get-db-query.js +194 -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 +26 -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/extensions/lib/sandbox/generate-api-extensions-sandbox-entrypoint.d.ts +1 -1
- package/dist/flows.js +3 -4
- package/dist/middleware/authenticate.js +2 -7
- package/dist/middleware/cache.js +1 -1
- package/dist/middleware/cors.js +4 -4
- 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 +10 -0
- package/dist/permissions/lib/fetch-permissions.js +55 -0
- package/dist/permissions/lib/fetch-policies.d.ts +7 -0
- package/dist/permissions/lib/fetch-policies.js +28 -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 +24 -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 +50 -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 +24 -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/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 +53 -24
- package/dist/services/files.d.ts +0 -4
- package/dist/services/files.js +10 -10
- package/dist/services/flows.d.ts +0 -2
- package/dist/services/flows.js +2 -14
- 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.js +23 -9
- package/dist/services/index.d.ts +3 -2
- package/dist/services/index.js +3 -2
- package/dist/services/items.d.ts +40 -14
- package/dist/services/items.js +182 -79
- package/dist/services/meta.js +60 -23
- package/dist/services/notifications.d.ts +0 -1
- package/dist/services/notifications.js +0 -7
- package/dist/services/operations.d.ts +0 -2
- package/dist/services/operations.js +2 -14
- package/dist/services/payload.d.ts +9 -10
- package/dist/services/payload.js +35 -19
- 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 +26 -29
- package/dist/services/roles.d.ts +4 -14
- package/dist/services/roles.js +56 -420
- 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 +2 -20
- package/dist/services/users.js +87 -190
- 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 +6 -3
- package/dist/telemetry/types/report.d.ts +4 -0
- package/dist/telemetry/utils/check-user-limits.d.ts +5 -0
- package/dist/telemetry/utils/check-user-limits.js +19 -0
- package/dist/telemetry/utils/get-filesize-sum.d.ts +5 -0
- package/dist/telemetry/utils/get-filesize-sum.js +7 -0
- package/dist/telemetry/utils/should-check-user-limits.d.ts +4 -0
- package/dist/telemetry/utils/should-check-user-limits.js +13 -0
- package/dist/types/ast.d.ts +43 -1
- package/dist/types/items.d.ts +11 -0
- package/dist/utils/apply-query.d.ts +4 -3
- package/dist/utils/apply-query.js +37 -8
- package/dist/utils/fetch-user-count/fetch-access-lookup.d.ts +17 -0
- package/dist/utils/fetch-user-count/fetch-access-lookup.js +22 -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 +57 -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-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-graphql-type.js +1 -0
- package/dist/utils/get-service.d.ts +1 -1
- package/dist/utils/get-service.js +14 -10
- package/dist/utils/reduce-schema.d.ts +4 -6
- package/dist/utils/reduce-schema.js +14 -34
- 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 +1 -4
- package/dist/websocket/controllers/hooks.js +4 -0
- package/dist/websocket/controllers/rest.js +0 -2
- package/dist/websocket/handlers/subscribe.js +0 -2
- package/dist/websocket/utils/items.d.ts +1 -1
- package/dist/websocket/utils/items.js +4 -1
- package/package.json +36 -35
- package/dist/database/run-ast.js +0 -450
- 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 -22
- 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.d.ts +0 -3
- package/dist/utils/merge-permissions.js +0 -95
|
@@ -6,14 +6,15 @@ import { clearSystemCache, getCache } from '../cache.js';
|
|
|
6
6
|
import { getHelpers } from '../database/helpers/index.js';
|
|
7
7
|
import getDatabase, { getSchemaInspector } from '../database/index.js';
|
|
8
8
|
import emitter from '../emitter.js';
|
|
9
|
+
import { fetchAllowedFieldMap } from '../permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js';
|
|
10
|
+
import { fetchAllowedFields } from '../permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js';
|
|
11
|
+
import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
|
|
9
12
|
import { getDefaultIndexName } from '../utils/get-default-index-name.js';
|
|
10
13
|
import { getSchema } from '../utils/get-schema.js';
|
|
11
14
|
import { transaction } from '../utils/transaction.js';
|
|
12
15
|
import { ItemsService } from './items.js';
|
|
13
|
-
import { PermissionsService } from './permissions/index.js';
|
|
14
16
|
export class RelationsService {
|
|
15
17
|
knex;
|
|
16
|
-
permissionsService;
|
|
17
18
|
schemaInspector;
|
|
18
19
|
accountability;
|
|
19
20
|
schema;
|
|
@@ -22,7 +23,6 @@ export class RelationsService {
|
|
|
22
23
|
helpers;
|
|
23
24
|
constructor(options) {
|
|
24
25
|
this.knex = options.knex || getDatabase();
|
|
25
|
-
this.permissionsService = new PermissionsService(options);
|
|
26
26
|
this.schemaInspector = options.knex ? createInspector(options.knex) : getSchemaInspector();
|
|
27
27
|
this.schema = options.schema;
|
|
28
28
|
this.accountability = options.accountability || null;
|
|
@@ -37,8 +37,15 @@ export class RelationsService {
|
|
|
37
37
|
this.helpers = getHelpers(this.knex);
|
|
38
38
|
}
|
|
39
39
|
async readAll(collection, opts) {
|
|
40
|
-
if (this.accountability
|
|
41
|
-
|
|
40
|
+
if (this.accountability) {
|
|
41
|
+
await validateAccess({
|
|
42
|
+
accountability: this.accountability,
|
|
43
|
+
action: 'read',
|
|
44
|
+
collection: 'directus_relations',
|
|
45
|
+
}, {
|
|
46
|
+
knex: this.knex,
|
|
47
|
+
schema: this.schema,
|
|
48
|
+
});
|
|
42
49
|
}
|
|
43
50
|
const metaReadQuery = {
|
|
44
51
|
limit: -1,
|
|
@@ -64,18 +71,17 @@ export class RelationsService {
|
|
|
64
71
|
}
|
|
65
72
|
async readOne(collection, field) {
|
|
66
73
|
if (this.accountability && this.accountability.admin !== true) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
await validateAccess({
|
|
75
|
+
accountability: this.accountability,
|
|
76
|
+
action: 'read',
|
|
77
|
+
collection: 'directus_relations',
|
|
78
|
+
}, {
|
|
79
|
+
schema: this.schema,
|
|
80
|
+
knex: this.knex,
|
|
72
81
|
});
|
|
73
|
-
|
|
82
|
+
const allowedFields = await fetchAllowedFields({ collection, action: 'read', accountability: this.accountability }, { schema: this.schema, knex: this.knex });
|
|
83
|
+
if (allowedFields.includes('*') === false && allowedFields.includes(field) === false) {
|
|
74
84
|
throw new ForbiddenError();
|
|
75
|
-
if (permissions.fields.includes('*') === false) {
|
|
76
|
-
const allowedFields = permissions.fields;
|
|
77
|
-
if (allowedFields.includes(field) === false)
|
|
78
|
-
throw new ForbiddenError();
|
|
79
85
|
}
|
|
80
86
|
}
|
|
81
87
|
const metaRow = await this.relationsItemService.readByQuery({
|
|
@@ -357,14 +363,6 @@ export class RelationsService {
|
|
|
357
363
|
}
|
|
358
364
|
}
|
|
359
365
|
}
|
|
360
|
-
/**
|
|
361
|
-
* Whether or not the current user has read access to relations
|
|
362
|
-
*/
|
|
363
|
-
get hasReadAccess() {
|
|
364
|
-
return !!this.accountability?.permissions?.find((permission) => {
|
|
365
|
-
return permission.collection === 'directus_relations' && permission.action === 'read';
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
366
|
/**
|
|
369
367
|
* Combine raw schema foreign key information with Directus relations meta rows to form final
|
|
370
368
|
* Relation objects
|
|
@@ -413,12 +411,11 @@ export class RelationsService {
|
|
|
413
411
|
async filterForbidden(relations) {
|
|
414
412
|
if (this.accountability === null || this.accountability?.admin === true)
|
|
415
413
|
return relations;
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
})
|
|
420
|
-
|
|
421
|
-
const allowedFields = this.permissionsService.getAllowedFields('read');
|
|
414
|
+
const allowedFields = await fetchAllowedFieldMap({
|
|
415
|
+
accountability: this.accountability,
|
|
416
|
+
action: 'read',
|
|
417
|
+
}, { schema: this.schema, knex: this.knex });
|
|
418
|
+
const allowedCollections = Object.keys(allowedFields);
|
|
422
419
|
relations = toArray(relations);
|
|
423
420
|
return relations.filter((relation) => {
|
|
424
421
|
let collectionsAllowed = true;
|
package/dist/services/roles.d.ts
CHANGED
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
import type { Item, PrimaryKey
|
|
1
|
+
import type { Item, PrimaryKey } 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 RolesService extends ItemsService {
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
|
6
|
-
private checkForOtherAdminRoles;
|
|
7
|
-
private checkForOtherAdminUsers;
|
|
8
|
-
private isIpAccessValid;
|
|
9
|
-
private assertValidIpAccess;
|
|
10
|
-
private getRoleAccessType;
|
|
11
|
-
createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
12
|
-
createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
13
|
-
updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
14
|
-
updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
15
6
|
updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
deleteByQuery(query: Query, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
7
|
+
deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
8
|
+
private validateRoleNesting;
|
|
9
|
+
private clearCaches;
|
|
20
10
|
}
|
package/dist/services/roles.js
CHANGED
|
@@ -1,435 +1,51 @@
|
|
|
1
|
-
import { InvalidPayloadError
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { getRoleCountsByUsers } from '../telemetry/utils/get-role-counts-by-users.js';
|
|
5
|
-
import {} from '../telemetry/utils/get-user-count.js';
|
|
6
|
-
import { getUserCountsByRoles } from '../telemetry/utils/get-user-counts-by-roles.js';
|
|
1
|
+
import { InvalidPayloadError } from '@directus/errors';
|
|
2
|
+
import { clearSystemCache } from '../cache.js';
|
|
3
|
+
import { fetchRolesTree } from '../permissions/lib/fetch-roles-tree.js';
|
|
7
4
|
import { transaction } from '../utils/transaction.js';
|
|
5
|
+
import { UserIntegrityCheckFlag } from '../utils/validate-user-count-integrity.js';
|
|
8
6
|
import { ItemsService } from './items.js';
|
|
9
|
-
import {
|
|
7
|
+
import { AccessService } from './access.js';
|
|
10
8
|
import { PresetsService } from './presets.js';
|
|
11
9
|
import { UsersService } from './users.js';
|
|
12
|
-
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
13
|
-
import { omit } from 'lodash-es';
|
|
14
10
|
export class RolesService extends ItemsService {
|
|
15
11
|
constructor(options) {
|
|
16
12
|
super('directus_roles', options);
|
|
17
13
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
|
|
25
|
-
.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const usersBefore = (await this.knex.select('id').from('directus_users').where('role', '=', key)).map((user) => user.id);
|
|
37
|
-
const usersAdded = [];
|
|
38
|
-
const usersUpdated = [];
|
|
39
|
-
const usersCreated = [];
|
|
40
|
-
const usersRemoved = [];
|
|
41
|
-
if (Array.isArray(users)) {
|
|
42
|
-
const usersKept = [];
|
|
43
|
-
for (const user of users) {
|
|
44
|
-
if (typeof user === 'string') {
|
|
45
|
-
if (usersBefore.includes(user)) {
|
|
46
|
-
usersKept.push(user);
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
usersAdded.push({ id: user });
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else if (user.id) {
|
|
53
|
-
if (usersBefore.includes(user.id)) {
|
|
54
|
-
usersKept.push(user.id);
|
|
55
|
-
usersUpdated.push(user);
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
usersAdded.push(user);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
usersCreated.push(user);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
usersRemoved.push(...usersBefore.filter((user) => !usersKept.includes(user)));
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
for (const user of users.update) {
|
|
69
|
-
if (usersBefore.includes(user['id'])) {
|
|
70
|
-
usersUpdated.push(user);
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
usersAdded.push(user);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
usersCreated.push(...users.create);
|
|
77
|
-
usersRemoved.push(...users.delete);
|
|
78
|
-
}
|
|
79
|
-
if (role.admin_access === false || role.admin_access === 0) {
|
|
80
|
-
// Admin users might have moved in from other role, thus becoming non-admin
|
|
81
|
-
if (usersAdded.length > 0) {
|
|
82
|
-
const otherAdminUsers = await this.knex
|
|
83
|
-
.count('*', { as: 'count' })
|
|
84
|
-
.from('directus_users')
|
|
85
|
-
.leftJoin('directus_roles', 'directus_users.role', 'directus_roles.id')
|
|
86
|
-
.whereNotIn('directus_users.id', usersAdded.map((user) => user.id))
|
|
87
|
-
.andWhere({ 'directus_roles.admin_access': true, status: 'active' })
|
|
88
|
-
.first();
|
|
89
|
-
const otherAdminUsersCount = Number(otherAdminUsers?.count ?? 0);
|
|
90
|
-
if (otherAdminUsersCount === 0) {
|
|
91
|
-
throw new UnprocessableContentError({ reason: `You can't remove the last admin user from the admin role` });
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
// Only added or created new users
|
|
97
|
-
if (usersUpdated.length === 0 && usersRemoved.length === 0)
|
|
98
|
-
return;
|
|
99
|
-
// Active admin user(s) about to be created
|
|
100
|
-
if (usersCreated.some((user) => !('status' in user) || user.status === 'active'))
|
|
101
|
-
return;
|
|
102
|
-
const usersDeactivated = [...usersAdded, ...usersUpdated]
|
|
103
|
-
.filter((user) => 'status' in user && user.status !== 'active')
|
|
104
|
-
.map((user) => user.id);
|
|
105
|
-
const usersAddedNonDeactivated = usersAdded
|
|
106
|
-
.filter((user) => !usersDeactivated.includes(user.id))
|
|
107
|
-
.map((user) => user.id);
|
|
108
|
-
// Active user(s) about to become admin
|
|
109
|
-
if (usersAddedNonDeactivated.length > 0) {
|
|
110
|
-
const userCount = await this.knex
|
|
111
|
-
.count('*', { as: 'count' })
|
|
112
|
-
.from('directus_users')
|
|
113
|
-
.whereIn('id', usersAddedNonDeactivated)
|
|
114
|
-
.andWhere({ status: 'active' })
|
|
115
|
-
.first();
|
|
116
|
-
if (Number(userCount?.count ?? 0) > 0) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
const otherAdminUsers = await this.knex
|
|
121
|
-
.count('*', { as: 'count' })
|
|
122
|
-
.from('directus_users')
|
|
123
|
-
.leftJoin('directus_roles', 'directus_users.role', 'directus_roles.id')
|
|
124
|
-
.whereNotIn('directus_users.id', [...usersDeactivated, ...usersRemoved])
|
|
125
|
-
.andWhere({ 'directus_roles.admin_access': true, status: 'active' })
|
|
126
|
-
.first();
|
|
127
|
-
const otherAdminUsersCount = Number(otherAdminUsers?.count ?? 0);
|
|
128
|
-
if (otherAdminUsersCount === 0) {
|
|
129
|
-
throw new UnprocessableContentError({ reason: `You can't remove the last admin user from the admin role` });
|
|
130
|
-
}
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
isIpAccessValid(value) {
|
|
134
|
-
if (value === undefined)
|
|
135
|
-
return false;
|
|
136
|
-
if (value === null)
|
|
137
|
-
return true;
|
|
138
|
-
if (Array.isArray(value) && value.length === 0)
|
|
139
|
-
return true;
|
|
140
|
-
for (const ip of value) {
|
|
141
|
-
if (typeof ip !== 'string' || ip.includes('*'))
|
|
142
|
-
return false;
|
|
143
|
-
try {
|
|
144
|
-
const match = getMatch(ip);
|
|
145
|
-
if (match.type == 'IPMask')
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return true;
|
|
153
|
-
}
|
|
154
|
-
assertValidIpAccess(partialItem) {
|
|
155
|
-
if ('ip_access' in partialItem && !this.isIpAccessValid(partialItem['ip_access'])) {
|
|
156
|
-
throw new InvalidPayloadError({
|
|
157
|
-
reason: 'IP Access contains an incorrect value. Valid values are: IP addresses, IP ranges and CIDR blocks',
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
getRoleAccessType(data) {
|
|
162
|
-
if ('admin_access' in data && data['admin_access'] === true) {
|
|
163
|
-
return 'admin';
|
|
164
|
-
}
|
|
165
|
-
else if (('app_access' in data && data['app_access'] === true) || 'app_access' in data === false) {
|
|
166
|
-
return 'app';
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
return 'api';
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
async createOne(data, opts) {
|
|
173
|
-
this.assertValidIpAccess(data);
|
|
174
|
-
const increasedCounts = {
|
|
175
|
-
admin: 0,
|
|
176
|
-
app: 0,
|
|
177
|
-
api: 0,
|
|
178
|
-
};
|
|
179
|
-
const existingIds = [];
|
|
180
|
-
if ('users' in data) {
|
|
181
|
-
const type = this.getRoleAccessType(data);
|
|
182
|
-
increasedCounts[type] += data['users'].length;
|
|
183
|
-
for (const user of data['users']) {
|
|
184
|
-
if (typeof user === 'string') {
|
|
185
|
-
existingIds.push(user);
|
|
186
|
-
}
|
|
187
|
-
else if (typeof user === 'object' && 'id' in user) {
|
|
188
|
-
existingIds.push(user['id']);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
await checkIncreasedUserLimits(this.knex, increasedCounts, existingIds);
|
|
193
|
-
return super.createOne(data, opts);
|
|
194
|
-
}
|
|
195
|
-
async createMany(data, opts) {
|
|
196
|
-
const increasedCounts = {
|
|
197
|
-
admin: 0,
|
|
198
|
-
app: 0,
|
|
199
|
-
api: 0,
|
|
200
|
-
};
|
|
201
|
-
const existingIds = [];
|
|
202
|
-
for (const partialItem of data) {
|
|
203
|
-
this.assertValidIpAccess(partialItem);
|
|
204
|
-
if ('users' in partialItem) {
|
|
205
|
-
const type = this.getRoleAccessType(partialItem);
|
|
206
|
-
increasedCounts[type] += partialItem['users'].length;
|
|
207
|
-
for (const user of partialItem['users']) {
|
|
208
|
-
if (typeof user === 'string') {
|
|
209
|
-
existingIds.push(user);
|
|
210
|
-
}
|
|
211
|
-
else if (typeof user === 'object' && 'id' in user) {
|
|
212
|
-
existingIds.push(user['id']);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
await checkIncreasedUserLimits(this.knex, increasedCounts, existingIds);
|
|
218
|
-
return super.createMany(data, opts);
|
|
219
|
-
}
|
|
220
|
-
async updateOne(key, data, opts) {
|
|
221
|
-
this.assertValidIpAccess(data);
|
|
222
|
-
try {
|
|
223
|
-
const increasedCounts = {
|
|
224
|
-
admin: 0,
|
|
225
|
-
app: 0,
|
|
226
|
-
api: 0,
|
|
227
|
-
};
|
|
228
|
-
let increasedUsers = 0;
|
|
229
|
-
const existingIds = [];
|
|
230
|
-
let existingRole = await this.knex
|
|
231
|
-
.count('directus_users.id', { as: 'count' })
|
|
232
|
-
.select('directus_roles.admin_access', 'directus_roles.app_access')
|
|
233
|
-
.from('directus_users')
|
|
234
|
-
.where('directus_roles.id', '=', key)
|
|
235
|
-
.andWhere('directus_users.status', '=', 'active')
|
|
236
|
-
.leftJoin('directus_roles', 'directus_users.role', '=', 'directus_roles.id')
|
|
237
|
-
.groupBy('directus_roles.admin_access', 'directus_roles.app_access')
|
|
238
|
-
.first();
|
|
239
|
-
if (!existingRole) {
|
|
240
|
-
try {
|
|
241
|
-
const role = (await this.knex
|
|
242
|
-
.select('admin_access', 'app_access')
|
|
243
|
-
.from('directus_roles')
|
|
244
|
-
.where('id', '=', key)
|
|
245
|
-
.first()) ?? { admin_access: null, app_access: null };
|
|
246
|
-
existingRole = { count: 0, ...role };
|
|
247
|
-
}
|
|
248
|
-
catch {
|
|
249
|
-
existingRole = { count: 0, admin_access: null, app_access: null };
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
if ('users' in data) {
|
|
253
|
-
await this.checkForOtherAdminUsers(key, data['users']);
|
|
254
|
-
const users = data['users'];
|
|
255
|
-
if (Array.isArray(users)) {
|
|
256
|
-
increasedUsers = users.length - Number(existingRole.count);
|
|
257
|
-
for (const user of users) {
|
|
258
|
-
if (typeof user === 'string') {
|
|
259
|
-
existingIds.push(user);
|
|
260
|
-
}
|
|
261
|
-
else if (typeof user === 'object' && 'id' in user) {
|
|
262
|
-
existingIds.push(user['id']);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
increasedUsers += users.create.length;
|
|
268
|
-
increasedUsers -= users.delete.length;
|
|
269
|
-
const userIds = [];
|
|
270
|
-
for (const user of users.update) {
|
|
271
|
-
if ('status' in user) {
|
|
272
|
-
// account for users being activated and deactivated
|
|
273
|
-
if (user['status'] === 'active') {
|
|
274
|
-
increasedUsers++;
|
|
275
|
-
}
|
|
276
|
-
else {
|
|
277
|
-
increasedUsers--;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
userIds.push(user.id);
|
|
281
|
-
}
|
|
282
|
-
try {
|
|
283
|
-
const existingCounts = await getRoleCountsByUsers(this.knex, userIds);
|
|
284
|
-
if (existingRole.admin_access) {
|
|
285
|
-
increasedUsers += existingCounts.app + existingCounts.api;
|
|
286
|
-
}
|
|
287
|
-
else if (existingRole.app_access) {
|
|
288
|
-
increasedUsers += existingCounts.admin + existingCounts.api;
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
increasedUsers += existingCounts.admin + existingCounts.app;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
catch {
|
|
295
|
-
// ignore failed user call
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
let isAccessChanged = false;
|
|
300
|
-
let accessType = 'api';
|
|
301
|
-
if ('app_access' in data) {
|
|
302
|
-
if (data['app_access'] === true) {
|
|
303
|
-
accessType = 'app';
|
|
304
|
-
if (!existingRole.app_access)
|
|
305
|
-
isAccessChanged = true;
|
|
306
|
-
}
|
|
307
|
-
else if (existingRole.app_access) {
|
|
308
|
-
isAccessChanged = true;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
else if (existingRole.app_access) {
|
|
312
|
-
accessType = 'app';
|
|
313
|
-
}
|
|
314
|
-
if ('admin_access' in data) {
|
|
315
|
-
if (data['admin_access'] === true) {
|
|
316
|
-
accessType = 'admin';
|
|
317
|
-
if (!existingRole.admin_access)
|
|
318
|
-
isAccessChanged = true;
|
|
319
|
-
}
|
|
320
|
-
else if (existingRole.admin_access) {
|
|
321
|
-
isAccessChanged = true;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
else if (existingRole.admin_access) {
|
|
325
|
-
accessType = 'admin';
|
|
326
|
-
}
|
|
327
|
-
if (isAccessChanged) {
|
|
328
|
-
increasedCounts[accessType] += Number(existingRole.count);
|
|
329
|
-
}
|
|
330
|
-
increasedCounts[accessType] += increasedUsers;
|
|
331
|
-
await checkIncreasedUserLimits(this.knex, increasedCounts, existingIds);
|
|
332
|
-
}
|
|
333
|
-
catch (err) {
|
|
334
|
-
(opts || (opts = {})).preMutationError = err;
|
|
335
|
-
}
|
|
336
|
-
return super.updateOne(key, data, opts);
|
|
337
|
-
}
|
|
338
|
-
async updateBatch(data, opts = {}) {
|
|
339
|
-
for (const partialItem of data) {
|
|
340
|
-
this.assertValidIpAccess(partialItem);
|
|
341
|
-
}
|
|
342
|
-
const primaryKeyField = this.schema.collections[this.collection].primary;
|
|
343
|
-
if (!opts.mutationTracker) {
|
|
344
|
-
opts.mutationTracker = this.createMutationTracker();
|
|
345
|
-
}
|
|
346
|
-
const keys = [];
|
|
347
|
-
try {
|
|
348
|
-
await transaction(this.knex, async (trx) => {
|
|
349
|
-
const service = new RolesService({
|
|
350
|
-
accountability: this.accountability,
|
|
351
|
-
knex: trx,
|
|
352
|
-
schema: this.schema,
|
|
353
|
-
});
|
|
354
|
-
for (const item of data) {
|
|
355
|
-
const combinedOpts = Object.assign({ autoPurgeCache: false }, opts);
|
|
356
|
-
keys.push(await service.updateOne(item[primaryKeyField], omit(item, primaryKeyField), combinedOpts));
|
|
357
|
-
}
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
finally {
|
|
361
|
-
if (shouldClearCache(this.cache, opts, this.collection)) {
|
|
362
|
-
await this.cache.clear();
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
return keys;
|
|
366
|
-
}
|
|
367
|
-
async updateMany(keys, data, opts) {
|
|
368
|
-
this.assertValidIpAccess(data);
|
|
369
|
-
try {
|
|
370
|
-
if ('admin_access' in data && data['admin_access'] === false) {
|
|
371
|
-
await this.checkForOtherAdminRoles(keys);
|
|
372
|
-
}
|
|
373
|
-
if ('admin_access' in data || 'app_access' in data) {
|
|
374
|
-
const existingCounts = await getUserCountsByRoles(this.knex, keys);
|
|
375
|
-
const increasedCounts = {
|
|
376
|
-
admin: 0,
|
|
377
|
-
app: 0,
|
|
378
|
-
api: 0,
|
|
379
|
-
};
|
|
380
|
-
const type = this.getRoleAccessType(data);
|
|
381
|
-
for (const [existingType, existingCount] of Object.entries(existingCounts)) {
|
|
382
|
-
if (existingType === type)
|
|
383
|
-
continue;
|
|
384
|
-
increasedCounts[type] += existingCount;
|
|
385
|
-
}
|
|
386
|
-
await checkIncreasedUserLimits(this.knex, increasedCounts);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
catch (err) {
|
|
390
|
-
(opts || (opts = {})).preMutationError = err;
|
|
391
|
-
}
|
|
392
|
-
return super.updateMany(keys, data, opts);
|
|
14
|
+
// No need to check user integrity in createOne, as the creation of a role itself does not influence the number of
|
|
15
|
+
// users, as the role of a user is actually updated in the UsersService on the user, which will make sure to
|
|
16
|
+
// initiate a user integrity check if necessary. Same goes for role nesting check as well as cache clearing.
|
|
17
|
+
async updateMany(keys, data, opts = {}) {
|
|
18
|
+
if ('parent' in data) {
|
|
19
|
+
// If the parent of a role changed we need to make a full integrity check.
|
|
20
|
+
// Anything related to policies will be checked in the AccessService, where the policies are attached to roles
|
|
21
|
+
opts.userIntegrityCheckFlags = UserIntegrityCheckFlag.All;
|
|
22
|
+
opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
|
|
23
|
+
await this.validateRoleNesting(keys, data['parent']);
|
|
24
|
+
}
|
|
25
|
+
const result = await super.updateMany(keys, data, opts);
|
|
26
|
+
// Only clear the permissions cache if the parent role has changed
|
|
27
|
+
// If anything policies related has changed, the cache will be cleared in the AccessService as well
|
|
28
|
+
if ('parent' in data) {
|
|
29
|
+
await this.clearCaches();
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
393
32
|
}
|
|
394
|
-
async
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
async deleteOne(key) {
|
|
399
|
-
await this.deleteMany([key]);
|
|
400
|
-
return key;
|
|
401
|
-
}
|
|
402
|
-
async deleteMany(keys) {
|
|
403
|
-
const opts = {};
|
|
404
|
-
try {
|
|
405
|
-
await this.checkForOtherAdminRoles(keys);
|
|
406
|
-
}
|
|
407
|
-
catch (err) {
|
|
408
|
-
opts.preMutationError = err;
|
|
409
|
-
}
|
|
33
|
+
async deleteMany(keys, opts = {}) {
|
|
34
|
+
opts.userIntegrityCheckFlags = UserIntegrityCheckFlag.All;
|
|
35
|
+
opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
|
|
410
36
|
await transaction(this.knex, async (trx) => {
|
|
411
|
-
const
|
|
37
|
+
const options = {
|
|
412
38
|
knex: trx,
|
|
413
39
|
accountability: this.accountability,
|
|
414
40
|
schema: this.schema,
|
|
415
|
-
}
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
const presetsService = new PresetsService({
|
|
422
|
-
knex: trx,
|
|
423
|
-
accountability: this.accountability,
|
|
424
|
-
schema: this.schema,
|
|
425
|
-
});
|
|
426
|
-
const usersService = new UsersService({
|
|
427
|
-
knex: trx,
|
|
428
|
-
accountability: this.accountability,
|
|
429
|
-
schema: this.schema,
|
|
430
|
-
});
|
|
41
|
+
};
|
|
42
|
+
const itemsService = new ItemsService('directus_roles', options);
|
|
43
|
+
const rolesService = new RolesService(options);
|
|
44
|
+
const accessService = new AccessService(options);
|
|
45
|
+
const presetsService = new PresetsService(options);
|
|
46
|
+
const usersService = new UsersService(options);
|
|
431
47
|
// Delete permissions/presets for this role, suspend all remaining users in role
|
|
432
|
-
await
|
|
48
|
+
await accessService.deleteByQuery({
|
|
433
49
|
filter: { role: { _in: keys } },
|
|
434
50
|
}, { ...opts, bypassLimits: true });
|
|
435
51
|
await presetsService.deleteByQuery({
|
|
@@ -441,11 +57,31 @@ export class RolesService extends ItemsService {
|
|
|
441
57
|
status: 'suspended',
|
|
442
58
|
role: null,
|
|
443
59
|
}, { ...opts, bypassLimits: true });
|
|
60
|
+
// If the about to be deleted roles are the parent of other roles set those parents to null
|
|
61
|
+
// Use a newly created RolesService here that works within the current transaction
|
|
62
|
+
await rolesService.updateByQuery({
|
|
63
|
+
filter: { parent: { _in: keys } },
|
|
64
|
+
}, { parent: null });
|
|
444
65
|
await itemsService.deleteMany(keys, opts);
|
|
445
66
|
});
|
|
67
|
+
// Since nested roles could be updated, clear caches
|
|
68
|
+
await this.clearCaches();
|
|
446
69
|
return keys;
|
|
447
70
|
}
|
|
448
|
-
|
|
449
|
-
|
|
71
|
+
async validateRoleNesting(ids, parent) {
|
|
72
|
+
if (ids.includes(parent)) {
|
|
73
|
+
throw new InvalidPayloadError({ reason: 'A role cannot be a parent of itself' });
|
|
74
|
+
}
|
|
75
|
+
const roles = await fetchRolesTree(parent, this.knex);
|
|
76
|
+
if (ids.some((id) => roles.includes(id))) {
|
|
77
|
+
// The role tree up from the parent already includes this role, so it would create a circular reference
|
|
78
|
+
throw new InvalidPayloadError({ reason: 'A role cannot have a parent that is already a descendant of itself' });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async clearCaches(opts) {
|
|
82
|
+
await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
|
|
83
|
+
if (this.cache && opts?.autoPurgeCache !== false) {
|
|
84
|
+
await this.cache.clear();
|
|
85
|
+
}
|
|
450
86
|
}
|
|
451
87
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { Item, PrimaryKey } from '@directus/types';
|
|
2
2
|
import type { AbstractServiceOptions, LoginResult, MutationOptions } from '../types/index.js';
|
|
3
|
-
import { AuthorizationService } from './authorization.js';
|
|
4
3
|
import { ItemsService } from './items.js';
|
|
5
4
|
export declare class SharesService extends ItemsService {
|
|
6
|
-
authorizationService: AuthorizationService;
|
|
7
5
|
constructor(options: AbstractServiceOptions);
|
|
8
6
|
createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
9
7
|
login(payload: Record<string, any>, options?: Partial<{
|