@directus/api 21.0.0 → 22.0.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/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/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/{utils/merge-permissions.d.ts → database/migrations/20240806A-permissions-policies.d.ts} +4 -1
- package/dist/database/migrations/20240806A-permissions-policies.js +338 -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/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/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-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/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/package.json +30 -29
- 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/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
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import type { Item, ItemPermissions,
|
|
2
|
-
import type
|
|
3
|
-
import type {
|
|
4
|
-
import
|
|
5
|
-
import { ItemsService } from '../items.js';
|
|
1
|
+
import type { Item, ItemPermissions, PrimaryKey, Query } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
|
+
import type { QueryOptions } from './items.js';
|
|
4
|
+
import { ItemsService } from './items.js';
|
|
6
5
|
export declare class PermissionsService extends ItemsService {
|
|
7
|
-
systemCache: Keyv<any>;
|
|
8
6
|
constructor(options: AbstractServiceOptions);
|
|
9
|
-
|
|
7
|
+
private clearCaches;
|
|
10
8
|
readByQuery(query: Query, opts?: QueryOptions): Promise<Partial<Item>[]>;
|
|
11
9
|
createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
12
10
|
createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
@@ -1,32 +1,17 @@
|
|
|
1
1
|
import { ForbiddenError } from '@directus/errors';
|
|
2
|
-
import { clearSystemCache
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { clearSystemCache } from '../cache.js';
|
|
3
|
+
import { withAppMinimalPermissions } from '../permissions/lib/with-app-minimal-permissions.js';
|
|
4
|
+
import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
|
|
5
|
+
import { ItemsService } from './items.js';
|
|
6
6
|
export class PermissionsService extends ItemsService {
|
|
7
|
-
systemCache;
|
|
8
7
|
constructor(options) {
|
|
9
8
|
super('directus_permissions', options);
|
|
10
|
-
const { systemCache } = getCache();
|
|
11
|
-
this.systemCache = systemCache;
|
|
12
9
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
matchesCollection = permission.collection === collection;
|
|
18
|
-
}
|
|
19
|
-
const matchesAction = permission.action === action;
|
|
20
|
-
return collection ? matchesCollection && matchesAction : matchesAction;
|
|
21
|
-
}) ?? [];
|
|
22
|
-
const fieldsPerCollection = {};
|
|
23
|
-
for (const result of results) {
|
|
24
|
-
const { collection, fields } = result;
|
|
25
|
-
if (!fieldsPerCollection[collection])
|
|
26
|
-
fieldsPerCollection[collection] = [];
|
|
27
|
-
fieldsPerCollection[collection].push(...(fields ?? []));
|
|
10
|
+
async clearCaches(opts) {
|
|
11
|
+
await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
|
|
12
|
+
if (this.cache && opts?.autoPurgeCache !== false) {
|
|
13
|
+
await this.cache.clear();
|
|
28
14
|
}
|
|
29
|
-
return fieldsPerCollection;
|
|
30
15
|
}
|
|
31
16
|
async readByQuery(query, opts) {
|
|
32
17
|
const result = (await super.readByQuery(query, opts));
|
|
@@ -34,50 +19,32 @@ export class PermissionsService extends ItemsService {
|
|
|
34
19
|
}
|
|
35
20
|
async createOne(data, opts) {
|
|
36
21
|
const res = await super.createOne(data, opts);
|
|
37
|
-
await
|
|
38
|
-
if (this.cache && opts?.autoPurgeCache !== false) {
|
|
39
|
-
await this.cache.clear();
|
|
40
|
-
}
|
|
22
|
+
await this.clearCaches(opts);
|
|
41
23
|
return res;
|
|
42
24
|
}
|
|
43
25
|
async createMany(data, opts) {
|
|
44
26
|
const res = await super.createMany(data, opts);
|
|
45
|
-
await
|
|
46
|
-
if (this.cache && opts?.autoPurgeCache !== false) {
|
|
47
|
-
await this.cache.clear();
|
|
48
|
-
}
|
|
27
|
+
await this.clearCaches(opts);
|
|
49
28
|
return res;
|
|
50
29
|
}
|
|
51
30
|
async updateBatch(data, opts) {
|
|
52
31
|
const res = await super.updateBatch(data, opts);
|
|
53
|
-
await
|
|
54
|
-
if (this.cache && opts?.autoPurgeCache !== false) {
|
|
55
|
-
await this.cache.clear();
|
|
56
|
-
}
|
|
32
|
+
await this.clearCaches(opts);
|
|
57
33
|
return res;
|
|
58
34
|
}
|
|
59
35
|
async updateMany(keys, data, opts) {
|
|
60
36
|
const res = await super.updateMany(keys, data, opts);
|
|
61
|
-
await
|
|
62
|
-
if (this.cache && opts?.autoPurgeCache !== false) {
|
|
63
|
-
await this.cache.clear();
|
|
64
|
-
}
|
|
37
|
+
await this.clearCaches(opts);
|
|
65
38
|
return res;
|
|
66
39
|
}
|
|
67
40
|
async upsertMany(payloads, opts) {
|
|
68
41
|
const res = await super.upsertMany(payloads, opts);
|
|
69
|
-
await
|
|
70
|
-
if (this.cache && opts?.autoPurgeCache !== false) {
|
|
71
|
-
await this.cache.clear();
|
|
72
|
-
}
|
|
42
|
+
await this.clearCaches(opts);
|
|
73
43
|
return res;
|
|
74
44
|
}
|
|
75
45
|
async deleteMany(keys, opts) {
|
|
76
46
|
const res = await super.deleteMany(keys, opts);
|
|
77
|
-
await
|
|
78
|
-
if (this.cache && opts?.autoPurgeCache !== false) {
|
|
79
|
-
await this.cache.clear();
|
|
80
|
-
}
|
|
47
|
+
await this.clearCaches(opts);
|
|
81
48
|
return res;
|
|
82
49
|
}
|
|
83
50
|
async getItemPermissions(collection, primaryKey) {
|
|
@@ -115,16 +82,25 @@ export class PermissionsService extends ItemsService {
|
|
|
115
82
|
updateAction = 'create';
|
|
116
83
|
}
|
|
117
84
|
}
|
|
118
|
-
const authorizationService = new AuthorizationService({
|
|
119
|
-
knex: this.knex,
|
|
120
|
-
accountability: this.accountability,
|
|
121
|
-
schema: this.schema,
|
|
122
|
-
});
|
|
123
85
|
await Promise.all(Object.keys(itemPermissions).map((key) => {
|
|
124
86
|
const action = key;
|
|
125
87
|
const checkAction = action === 'update' ? updateAction : action;
|
|
126
|
-
|
|
127
|
-
.
|
|
88
|
+
if (!this.accountability) {
|
|
89
|
+
itemPermissions[action].access = true;
|
|
90
|
+
return Promise.resolve();
|
|
91
|
+
}
|
|
92
|
+
const opts = {
|
|
93
|
+
accountability: this.accountability,
|
|
94
|
+
action: checkAction,
|
|
95
|
+
collection,
|
|
96
|
+
};
|
|
97
|
+
if (primaryKey) {
|
|
98
|
+
opts.primaryKeys = [primaryKey];
|
|
99
|
+
}
|
|
100
|
+
return validateAccess(opts, {
|
|
101
|
+
schema: this.schema,
|
|
102
|
+
knex: this.knex,
|
|
103
|
+
})
|
|
128
104
|
.then(() => (itemPermissions[action].access = true))
|
|
129
105
|
.catch(() => { });
|
|
130
106
|
}));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Policy, PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
|
+
import { ItemsService } from './items.js';
|
|
4
|
+
export declare class PoliciesService extends ItemsService<Policy> {
|
|
5
|
+
constructor(options: AbstractServiceOptions);
|
|
6
|
+
private clearCaches;
|
|
7
|
+
private isIpAccessValid;
|
|
8
|
+
private assertValidIpAccess;
|
|
9
|
+
createOne(data: Partial<Policy>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
10
|
+
updateMany(keys: PrimaryKey[], data: Partial<Policy>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
11
|
+
deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { InvalidPayloadError } from '@directus/errors';
|
|
2
|
+
import { getMatch } from 'ip-matching';
|
|
3
|
+
import { clearSystemCache } from '../cache.js';
|
|
4
|
+
import { clearCache as clearPermissionsCache } from '../permissions/cache.js';
|
|
5
|
+
import { UserIntegrityCheckFlag } from '../utils/validate-user-count-integrity.js';
|
|
6
|
+
import { ItemsService } from './items.js';
|
|
7
|
+
export class PoliciesService extends ItemsService {
|
|
8
|
+
constructor(options) {
|
|
9
|
+
super('directus_policies', options);
|
|
10
|
+
}
|
|
11
|
+
async clearCaches(opts) {
|
|
12
|
+
await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
|
|
13
|
+
if (this.cache && opts?.autoPurgeCache !== false) {
|
|
14
|
+
await this.cache.clear();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
isIpAccessValid(value) {
|
|
18
|
+
if (value === undefined)
|
|
19
|
+
return false;
|
|
20
|
+
if (value === null)
|
|
21
|
+
return true;
|
|
22
|
+
if (Array.isArray(value) && value.length === 0)
|
|
23
|
+
return true;
|
|
24
|
+
for (const ip of value) {
|
|
25
|
+
if (typeof ip !== 'string' || ip.includes('*'))
|
|
26
|
+
return false;
|
|
27
|
+
try {
|
|
28
|
+
const match = getMatch(ip);
|
|
29
|
+
if (match.type == 'IPMask')
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
assertValidIpAccess(partialItem) {
|
|
39
|
+
if ('ip_access' in partialItem && !this.isIpAccessValid(partialItem['ip_access'])) {
|
|
40
|
+
throw new InvalidPayloadError({
|
|
41
|
+
reason: 'IP Access contains an incorrect value. Valid values are: IP addresses, IP ranges and CIDR blocks',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async createOne(data, opts = {}) {
|
|
46
|
+
this.assertValidIpAccess(data);
|
|
47
|
+
// A policy has been created, but the attachment to a user/role happens in the AccessService,
|
|
48
|
+
// so no need to check user integrity
|
|
49
|
+
const result = await super.createOne(data, opts);
|
|
50
|
+
// TODO is this necessary? Since the attachment should be handled in the AccessService
|
|
51
|
+
// A new policy has created, clear the permissions cache
|
|
52
|
+
await clearPermissionsCache();
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
async updateMany(keys, data, opts = {}) {
|
|
56
|
+
this.assertValidIpAccess(data);
|
|
57
|
+
if ('admin_access' in data) {
|
|
58
|
+
let flags = UserIntegrityCheckFlag.RemainingAdmins;
|
|
59
|
+
if (data['admin_access'] === true) {
|
|
60
|
+
// Only need to perform a full user count if the policy allows admin access
|
|
61
|
+
flags |= UserIntegrityCheckFlag.All;
|
|
62
|
+
}
|
|
63
|
+
opts.userIntegrityCheckFlags = (opts.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None) | flags;
|
|
64
|
+
}
|
|
65
|
+
if ('app_access' in data) {
|
|
66
|
+
opts.userIntegrityCheckFlags =
|
|
67
|
+
(opts.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None) | UserIntegrityCheckFlag.UserLimits;
|
|
68
|
+
}
|
|
69
|
+
if (opts.userIntegrityCheckFlags)
|
|
70
|
+
opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
|
|
71
|
+
const result = await super.updateMany(keys, data, opts);
|
|
72
|
+
if ('admin_access' in data || 'app_access' in data || 'ip_access' in data || 'enforce_tfa' in data) {
|
|
73
|
+
// Some relevant properties on policies have been updated, clear the caches
|
|
74
|
+
await this.clearCaches(opts);
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
async deleteMany(keys, opts = {}) {
|
|
79
|
+
opts.userIntegrityCheckFlags = UserIntegrityCheckFlag.All;
|
|
80
|
+
opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
|
|
81
|
+
const result = await super.deleteMany(keys, opts);
|
|
82
|
+
// TODO is this necessary? Since the detachment should be handled in the AccessService
|
|
83
|
+
// Some policies have been deleted, clear the permissions cache
|
|
84
|
+
await this.clearCaches(opts);
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -5,10 +5,8 @@ import type { Knex } from 'knex';
|
|
|
5
5
|
import type { Helpers } from '../database/helpers/index.js';
|
|
6
6
|
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
7
7
|
import { ItemsService, type QueryOptions } from './items.js';
|
|
8
|
-
import { PermissionsService } from './permissions/index.js';
|
|
9
8
|
export declare class RelationsService {
|
|
10
9
|
knex: Knex;
|
|
11
|
-
permissionsService: PermissionsService;
|
|
12
10
|
schemaInspector: SchemaInspector;
|
|
13
11
|
accountability: Accountability | null;
|
|
14
12
|
schema: SchemaOverview;
|
|
@@ -34,10 +32,6 @@ export declare class RelationsService {
|
|
|
34
32
|
* Delete an existing relationship
|
|
35
33
|
*/
|
|
36
34
|
deleteOne(collection: string, field: string, opts?: MutationOptions): Promise<void>;
|
|
37
|
-
/**
|
|
38
|
-
* Whether or not the current user has read access to relations
|
|
39
|
-
*/
|
|
40
|
-
private get hasReadAccess();
|
|
41
35
|
/**
|
|
42
36
|
* Combine raw schema foreign key information with Directus relations meta rows to form final
|
|
43
37
|
* Relation objects
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
|
|
2
3
|
import { createInspector } from '@directus/schema';
|
|
3
4
|
import { systemRelationRows } from '@directus/system-data';
|
|
@@ -6,16 +7,16 @@ import { clearSystemCache, getCache, getCacheValue, setCacheValue } from '../cac
|
|
|
6
7
|
import { getHelpers } from '../database/helpers/index.js';
|
|
7
8
|
import getDatabase, { getSchemaInspector } from '../database/index.js';
|
|
8
9
|
import emitter from '../emitter.js';
|
|
10
|
+
import { fetchAllowedFieldMap } from '../permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js';
|
|
11
|
+
import { fetchAllowedFields } from '../permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js';
|
|
12
|
+
import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
|
|
9
13
|
import { getDefaultIndexName } from '../utils/get-default-index-name.js';
|
|
10
14
|
import { getSchema } from '../utils/get-schema.js';
|
|
11
15
|
import { transaction } from '../utils/transaction.js';
|
|
12
16
|
import { ItemsService } from './items.js';
|
|
13
|
-
import { PermissionsService } from './permissions/index.js';
|
|
14
|
-
import { useEnv } from '@directus/env';
|
|
15
17
|
const env = useEnv();
|
|
16
18
|
export class RelationsService {
|
|
17
19
|
knex;
|
|
18
|
-
permissionsService;
|
|
19
20
|
schemaInspector;
|
|
20
21
|
accountability;
|
|
21
22
|
schema;
|
|
@@ -25,7 +26,6 @@ export class RelationsService {
|
|
|
25
26
|
helpers;
|
|
26
27
|
constructor(options) {
|
|
27
28
|
this.knex = options.knex || getDatabase();
|
|
28
|
-
this.permissionsService = new PermissionsService(options);
|
|
29
29
|
this.schemaInspector = options.knex ? createInspector(options.knex) : getSchemaInspector();
|
|
30
30
|
this.schema = options.schema;
|
|
31
31
|
this.accountability = options.accountability || null;
|
|
@@ -59,8 +59,15 @@ export class RelationsService {
|
|
|
59
59
|
return foreignKeys;
|
|
60
60
|
}
|
|
61
61
|
async readAll(collection, opts) {
|
|
62
|
-
if (this.accountability
|
|
63
|
-
|
|
62
|
+
if (this.accountability) {
|
|
63
|
+
await validateAccess({
|
|
64
|
+
accountability: this.accountability,
|
|
65
|
+
action: 'read',
|
|
66
|
+
collection: 'directus_relations',
|
|
67
|
+
}, {
|
|
68
|
+
knex: this.knex,
|
|
69
|
+
schema: this.schema,
|
|
70
|
+
});
|
|
64
71
|
}
|
|
65
72
|
const metaReadQuery = {
|
|
66
73
|
limit: -1,
|
|
@@ -86,18 +93,17 @@ export class RelationsService {
|
|
|
86
93
|
}
|
|
87
94
|
async readOne(collection, field) {
|
|
88
95
|
if (this.accountability && this.accountability.admin !== true) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
await validateAccess({
|
|
97
|
+
accountability: this.accountability,
|
|
98
|
+
action: 'read',
|
|
99
|
+
collection: 'directus_relations',
|
|
100
|
+
}, {
|
|
101
|
+
schema: this.schema,
|
|
102
|
+
knex: this.knex,
|
|
94
103
|
});
|
|
95
|
-
|
|
104
|
+
const allowedFields = await fetchAllowedFields({ collection, action: 'read', accountability: this.accountability }, { schema: this.schema, knex: this.knex });
|
|
105
|
+
if (allowedFields.includes('*') === false && allowedFields.includes(field) === false) {
|
|
96
106
|
throw new ForbiddenError();
|
|
97
|
-
if (permissions.fields.includes('*') === false) {
|
|
98
|
-
const allowedFields = permissions.fields;
|
|
99
|
-
if (allowedFields.includes(field) === false)
|
|
100
|
-
throw new ForbiddenError();
|
|
101
107
|
}
|
|
102
108
|
}
|
|
103
109
|
const metaRow = await this.relationsItemService.readByQuery({
|
|
@@ -379,14 +385,6 @@ export class RelationsService {
|
|
|
379
385
|
}
|
|
380
386
|
}
|
|
381
387
|
}
|
|
382
|
-
/**
|
|
383
|
-
* Whether or not the current user has read access to relations
|
|
384
|
-
*/
|
|
385
|
-
get hasReadAccess() {
|
|
386
|
-
return !!this.accountability?.permissions?.find((permission) => {
|
|
387
|
-
return permission.collection === 'directus_relations' && permission.action === 'read';
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
388
|
/**
|
|
391
389
|
* Combine raw schema foreign key information with Directus relations meta rows to form final
|
|
392
390
|
* Relation objects
|
|
@@ -435,12 +433,11 @@ export class RelationsService {
|
|
|
435
433
|
async filterForbidden(relations) {
|
|
436
434
|
if (this.accountability === null || this.accountability?.admin === true)
|
|
437
435
|
return relations;
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
})
|
|
442
|
-
|
|
443
|
-
const allowedFields = this.permissionsService.getAllowedFields('read');
|
|
436
|
+
const allowedFields = await fetchAllowedFieldMap({
|
|
437
|
+
accountability: this.accountability,
|
|
438
|
+
action: 'read',
|
|
439
|
+
}, { schema: this.schema, knex: this.knex });
|
|
440
|
+
const allowedCollections = Object.keys(allowedFields);
|
|
444
441
|
relations = toArray(relations);
|
|
445
442
|
return relations.filter((relation) => {
|
|
446
443
|
let collectionsAllowed = true;
|
package/dist/services/roles.d.ts
CHANGED
|
@@ -1,18 +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
|
-
|
|
7
|
+
deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
8
|
+
private validateRoleNesting;
|
|
9
|
+
private clearCaches;
|
|
18
10
|
}
|