@directus/api 20.1.0 → 21.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 +5 -5
- package/dist/auth/drivers/ldap.js +5 -5
- package/dist/auth/drivers/local.js +4 -4
- package/dist/auth/drivers/oauth2.js +5 -5
- package/dist/auth/drivers/openid.js +3 -5
- package/dist/auth/drivers/saml.js +1 -1
- package/dist/auth.js +1 -1
- package/dist/cache.js +4 -1
- package/dist/cli/commands/bootstrap/index.js +9 -3
- package/dist/cli/commands/count/index.js +1 -1
- package/dist/cli/commands/database/install.js +1 -1
- package/dist/cli/commands/database/migrate.js +1 -1
- package/dist/cli/commands/init/index.js +9 -10
- package/dist/cli/commands/roles/create.js +1 -1
- package/dist/cli/commands/schema/apply.js +1 -1
- package/dist/cli/commands/schema/snapshot.js +1 -1
- package/dist/cli/commands/users/create.js +1 -1
- package/dist/cli/commands/users/passwd.js +1 -1
- package/dist/cli/load-extensions.js +1 -1
- 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/constants.js +2 -2
- package/dist/controllers/access.d.ts +2 -0
- package/dist/controllers/access.js +148 -0
- package/dist/controllers/assets.js +1 -1
- package/dist/controllers/auth.js +6 -17
- package/dist/controllers/files.js +1 -1
- 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/schema.js +1 -1
- package/dist/controllers/tus.js +11 -23
- 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 +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 +3 -2
- package/dist/database/migrations/20210518A-add-foreign-key-constraints.js +1 -1
- package/dist/database/migrations/20210519A-add-system-fk-triggers.js +1 -1
- package/dist/database/migrations/20210802A-replace-groups.js +1 -1
- package/dist/database/migrations/20230721A-require-shares-fields.js +1 -1
- package/dist/database/migrations/20240710A-permissions-policies.d.ts +3 -0
- package/dist/database/migrations/20240710A-permissions-policies.js +169 -0
- package/dist/database/migrations/run.js +1 -1
- package/dist/database/run-ast/lib/get-db-query.d.ts +4 -0
- package/dist/database/run-ast/lib/get-db-query.js +208 -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/emitter.js +1 -1
- package/dist/extensions/lib/get-shared-deps-mapping.js +1 -1
- package/dist/extensions/lib/installation/manager.js +1 -1
- package/dist/extensions/lib/sandbox/register/call-reference.js +1 -1
- package/dist/extensions/lib/sandbox/sdk/generators/log.js +1 -1
- package/dist/extensions/lib/sync-extensions.js +1 -1
- package/dist/extensions/manager.js +1 -1
- package/dist/flows.js +4 -5
- package/dist/{logger.js → logger/index.js} +2 -8
- package/dist/logger/redact-query.d.ts +1 -0
- package/dist/logger/redact-query.js +13 -0
- package/dist/mailer.js +1 -1
- package/dist/middleware/authenticate.js +2 -7
- package/dist/middleware/cache.js +2 -2
- package/dist/middleware/error-handler.js +1 -1
- package/dist/middleware/rate-limiter-global.js +1 -1
- package/dist/middleware/respond.js +2 -2
- package/dist/operations/log/index.js +1 -1
- package/dist/operations/mail/index.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 +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/request/is-denied-ip.js +1 -1
- package/dist/server.js +1 -1
- package/dist/services/access.d.ts +10 -0
- package/dist/services/access.js +43 -0
- package/dist/services/activity.js +23 -11
- package/dist/services/assets.d.ts +2 -3
- package/dist/services/assets.js +11 -6
- 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/utils/get-metadata.js +1 -1
- package/dist/services/files.js +11 -4
- 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/graphql/utils/process-error.js +1 -1
- package/dist/services/graphql/utils/sanitize-gql-schema.js +1 -1
- package/dist/services/import-export.js +19 -5
- 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/mail/index.js +1 -1
- package/dist/services/meta.js +60 -23
- package/dist/services/notifications.js +15 -7
- 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 +26 -29
- package/dist/services/roles.d.ts +4 -12
- package/dist/services/roles.js +57 -424
- package/dist/services/server.js +1 -1
- package/dist/services/shares.d.ts +0 -2
- package/dist/services/shares.js +13 -9
- package/dist/services/specifications.d.ts +2 -2
- package/dist/services/specifications.js +39 -27
- package/dist/services/tus/data-store.js +1 -1
- package/dist/services/users.d.ts +1 -5
- package/dist/services/users.js +79 -162
- package/dist/services/utils.js +11 -7
- package/dist/services/versions.d.ts +0 -2
- package/dist/services/versions.js +34 -10
- package/dist/services/webhooks.js +1 -1
- package/dist/telemetry/lib/get-report.js +2 -2
- package/dist/telemetry/lib/track.js +1 -1
- 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-diff.js +1 -1
- package/dist/utils/apply-query.d.ts +4 -3
- package/dist/utils/apply-query.js +37 -8
- package/dist/utils/delete-from-require-cache.js +1 -1
- 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-default-value.js +1 -1
- package/dist/utils/get-ip-from-req.js +1 -1
- package/dist/utils/get-schema.js +1 -1
- package/dist/utils/get-service.js +5 -1
- package/dist/utils/is-url-allowed.js +1 -1
- package/dist/utils/reduce-schema.d.ts +4 -6
- package/dist/utils/reduce-schema.js +16 -32
- package/dist/utils/sanitize-query.js +1 -1
- package/dist/utils/transaction.js +1 -1
- package/dist/utils/validate-env.js +1 -1
- package/dist/utils/validate-storage.js +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/base.js +1 -1
- package/dist/websocket/controllers/graphql.js +2 -5
- package/dist/websocket/controllers/hooks.js +4 -0
- package/dist/websocket/controllers/rest.js +1 -3
- package/dist/websocket/errors.js +1 -1
- package/dist/websocket/handlers/subscribe.js +0 -2
- package/dist/websocket/utils/items.d.ts +1 -1
- package/package.json +24 -23
- 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.d.ts +0 -3
- package/dist/utils/merge-permissions.js +0 -95
- /package/dist/{logger.d.ts → logger/index.d.ts} +0 -0
|
@@ -1,456 +0,0 @@
|
|
|
1
|
-
import { ForbiddenError } from '@directus/errors';
|
|
2
|
-
import { validatePayload } from '@directus/utils';
|
|
3
|
-
import { FailedValidationError, joiValidationErrorItemToErrorExtensions } from '@directus/validation';
|
|
4
|
-
import { cloneDeep, flatten, isArray, isNil, merge, reduce, uniq, uniqWith } from 'lodash-es';
|
|
5
|
-
import { GENERATE_SPECIAL } from '../constants.js';
|
|
6
|
-
import getDatabase from '../database/index.js';
|
|
7
|
-
import { getRelationInfo } from '../utils/get-relation-info.js';
|
|
8
|
-
import { parseFilterKey } from '../utils/parse-filter-key.js';
|
|
9
|
-
import { ItemsService } from './items.js';
|
|
10
|
-
import { PayloadService } from './payload.js';
|
|
11
|
-
export class AuthorizationService {
|
|
12
|
-
knex;
|
|
13
|
-
accountability;
|
|
14
|
-
payloadService;
|
|
15
|
-
schema;
|
|
16
|
-
constructor(options) {
|
|
17
|
-
this.knex = options.knex || getDatabase();
|
|
18
|
-
this.accountability = options.accountability || null;
|
|
19
|
-
this.schema = options.schema;
|
|
20
|
-
this.payloadService = new PayloadService('directus_permissions', {
|
|
21
|
-
knex: this.knex,
|
|
22
|
-
schema: this.schema,
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
async processAST(ast, action = 'read') {
|
|
26
|
-
const collectionsRequested = getCollectionsFromAST(ast);
|
|
27
|
-
const permissionsForCollections = uniqWith(this.accountability?.permissions?.filter((permission) => {
|
|
28
|
-
return (permission.action === action &&
|
|
29
|
-
collectionsRequested.map(({ collection }) => collection).includes(permission.collection));
|
|
30
|
-
}), (curr, prev) => curr.collection === prev.collection && curr.action === prev.action && curr.role === prev.role) ?? [];
|
|
31
|
-
// If the permissions don't match the collections, you don't have permission to read all of them
|
|
32
|
-
const uniqueCollectionsRequestedCount = uniq(collectionsRequested.map(({ collection }) => collection)).length;
|
|
33
|
-
if (uniqueCollectionsRequestedCount !== permissionsForCollections.length) {
|
|
34
|
-
throw new ForbiddenError();
|
|
35
|
-
}
|
|
36
|
-
validateFields(ast);
|
|
37
|
-
validateFilterPermissions(ast, this.schema, action, this.accountability);
|
|
38
|
-
applyFilters(ast, this.accountability);
|
|
39
|
-
return ast;
|
|
40
|
-
/**
|
|
41
|
-
* Traverses the AST and returns an array of all collections that are being fetched
|
|
42
|
-
*/
|
|
43
|
-
function getCollectionsFromAST(ast) {
|
|
44
|
-
const collections = [];
|
|
45
|
-
if (ast.type === 'a2o') {
|
|
46
|
-
collections.push(...ast.names.map((name) => ({ collection: name, field: ast.fieldKey })));
|
|
47
|
-
for (const children of Object.values(ast.children)) {
|
|
48
|
-
for (const nestedNode of children) {
|
|
49
|
-
if (nestedNode.type !== 'field' && nestedNode.type !== 'functionField') {
|
|
50
|
-
collections.push(...getCollectionsFromAST(nestedNode));
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
collections.push({
|
|
57
|
-
collection: ast.name,
|
|
58
|
-
field: ast.type === 'root' ? null : ast.fieldKey,
|
|
59
|
-
});
|
|
60
|
-
for (const nestedNode of ast.children) {
|
|
61
|
-
if (nestedNode.type === 'functionField') {
|
|
62
|
-
collections.push({
|
|
63
|
-
collection: nestedNode.relatedCollection,
|
|
64
|
-
field: null,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
else if (nestedNode.type !== 'field') {
|
|
68
|
-
collections.push(...getCollectionsFromAST(nestedNode));
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return collections;
|
|
73
|
-
}
|
|
74
|
-
function validateFields(ast) {
|
|
75
|
-
if (ast.type !== 'field' && ast.type !== 'functionField') {
|
|
76
|
-
if (ast.type === 'a2o') {
|
|
77
|
-
for (const [collection, children] of Object.entries(ast.children)) {
|
|
78
|
-
checkFields(collection, children, ast.query?.[collection]?.aggregate);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
checkFields(ast.name, ast.children, ast.query?.aggregate);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
function checkFields(collection, children, aggregate) {
|
|
86
|
-
// We check the availability of the permissions in the step before this is run
|
|
87
|
-
const permissions = permissionsForCollections.find((permission) => permission.collection === collection);
|
|
88
|
-
const allowedFields = permissions.fields || [];
|
|
89
|
-
if (aggregate && allowedFields.includes('*') === false) {
|
|
90
|
-
for (const aliasMap of Object.values(aggregate)) {
|
|
91
|
-
if (!aliasMap)
|
|
92
|
-
continue;
|
|
93
|
-
for (const column of Object.values(aliasMap)) {
|
|
94
|
-
if (column === '*')
|
|
95
|
-
continue;
|
|
96
|
-
if (allowedFields.includes(column) === false)
|
|
97
|
-
throw new ForbiddenError();
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
for (const childNode of children) {
|
|
102
|
-
if (childNode.type !== 'field') {
|
|
103
|
-
validateFields(childNode);
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
if (allowedFields.includes('*'))
|
|
107
|
-
continue;
|
|
108
|
-
const { fieldName } = parseFilterKey(childNode.name);
|
|
109
|
-
if (allowedFields.includes(fieldName) === false) {
|
|
110
|
-
throw new ForbiddenError();
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function validateFilterPermissions(ast, schema, action, accountability) {
|
|
116
|
-
let requiredFieldPermissions = {};
|
|
117
|
-
if (ast.type !== 'field' && ast.type !== 'functionField') {
|
|
118
|
-
if (ast.type === 'a2o') {
|
|
119
|
-
for (const collection of Object.keys(ast.children)) {
|
|
120
|
-
requiredFieldPermissions = mergeRequiredFieldPermissions(requiredFieldPermissions, extractRequiredFieldPermissions(collection, ast.query?.[collection]?.filter ?? {}));
|
|
121
|
-
for (const child of ast.children[collection]) {
|
|
122
|
-
const childPermissions = validateFilterPermissions(child, schema, action, accountability);
|
|
123
|
-
if (Object.keys(childPermissions).length > 0) {
|
|
124
|
-
//Only add relational field if deep child has a filter
|
|
125
|
-
if (child.type !== 'field') {
|
|
126
|
-
(requiredFieldPermissions[collection] || (requiredFieldPermissions[collection] = new Set())).add(child.fieldKey);
|
|
127
|
-
}
|
|
128
|
-
requiredFieldPermissions = mergeRequiredFieldPermissions(requiredFieldPermissions, childPermissions);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
requiredFieldPermissions = mergeRequiredFieldPermissions(requiredFieldPermissions, extractRequiredFieldPermissions(ast.name, ast.query?.filter ?? {}));
|
|
135
|
-
for (const child of ast.children) {
|
|
136
|
-
const childPermissions = validateFilterPermissions(child, schema, action, accountability);
|
|
137
|
-
if (Object.keys(childPermissions).length > 0) {
|
|
138
|
-
// Only add relational field if deep child has a filter
|
|
139
|
-
if (child.type !== 'field') {
|
|
140
|
-
(requiredFieldPermissions[ast.name] || (requiredFieldPermissions[ast.name] = new Set())).add(child.fieldKey);
|
|
141
|
-
}
|
|
142
|
-
requiredFieldPermissions = mergeRequiredFieldPermissions(requiredFieldPermissions, childPermissions);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (ast.type === 'root') {
|
|
148
|
-
// Validate all required permissions once at the root level
|
|
149
|
-
checkFieldPermissions(ast.name, schema, action, requiredFieldPermissions, ast.query.alias);
|
|
150
|
-
}
|
|
151
|
-
return requiredFieldPermissions;
|
|
152
|
-
function extractRequiredFieldPermissions(collection, filter, parentCollection, parentField) {
|
|
153
|
-
return reduce(filter, function (result, filterValue, filterKey) {
|
|
154
|
-
if (filterKey.startsWith('_')) {
|
|
155
|
-
if (filterKey === '_and' || filterKey === '_or') {
|
|
156
|
-
if (isArray(filterValue)) {
|
|
157
|
-
for (const filter of filterValue) {
|
|
158
|
-
const requiredPermissions = extractRequiredFieldPermissions(collection, filter, parentCollection, parentField);
|
|
159
|
-
result = mergeRequiredFieldPermissions(result, requiredPermissions);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return result;
|
|
163
|
-
}
|
|
164
|
-
// Filter value is not a filter, so we should skip it
|
|
165
|
-
return result;
|
|
166
|
-
}
|
|
167
|
-
// virtual o2m/o2a filter in the form of `$FOLLOW(...)`
|
|
168
|
-
else if (collection && filterKey.startsWith('$FOLLOW')) {
|
|
169
|
-
(result[collection] || (result[collection] = new Set())).add(filterKey);
|
|
170
|
-
// add virtual relation to the required permissions
|
|
171
|
-
const { relation } = getRelationInfo([], collection, filterKey);
|
|
172
|
-
if (relation?.collection && relation?.field) {
|
|
173
|
-
(result[relation.collection] || (result[relation.collection] = new Set())).add(relation.field);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
// a2o filter in the form of `item:collection`
|
|
177
|
-
else if (filterKey.includes(':')) {
|
|
178
|
-
const [field, collectionScope] = filterKey.split(':');
|
|
179
|
-
if (collection) {
|
|
180
|
-
// Add the `item` field to the required permissions
|
|
181
|
-
(result[collection] || (result[collection] = new Set())).add(field);
|
|
182
|
-
// Add the `collection` field to the required permissions
|
|
183
|
-
result[collection].add('collection');
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
const relation = schema.relations.find((relation) => {
|
|
187
|
-
return ((relation.collection === parentCollection && relation.field === parentField) ||
|
|
188
|
-
(relation.related_collection === parentCollection && relation.meta?.one_field === parentField));
|
|
189
|
-
});
|
|
190
|
-
// Filter key not found in parent collection
|
|
191
|
-
if (!relation)
|
|
192
|
-
throw new ForbiddenError();
|
|
193
|
-
const relatedCollectionName = relation.related_collection === parentCollection ? relation.collection : relation.related_collection;
|
|
194
|
-
// Add the `item` field to the required permissions
|
|
195
|
-
(result[relatedCollectionName] || (result[relatedCollectionName] = new Set())).add(field);
|
|
196
|
-
// Add the `collection` field to the required permissions
|
|
197
|
-
result[relatedCollectionName].add('collection');
|
|
198
|
-
}
|
|
199
|
-
// Continue to parse the filter for nested `collection` afresh
|
|
200
|
-
const requiredPermissions = extractRequiredFieldPermissions(collectionScope, filterValue);
|
|
201
|
-
result = mergeRequiredFieldPermissions(result, requiredPermissions);
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
204
|
-
if (collection) {
|
|
205
|
-
(result[collection] || (result[collection] = new Set())).add(filterKey);
|
|
206
|
-
}
|
|
207
|
-
else {
|
|
208
|
-
const relation = schema.relations.find((relation) => {
|
|
209
|
-
return ((relation.collection === parentCollection && relation.field === parentField) ||
|
|
210
|
-
(relation.related_collection === parentCollection && relation.meta?.one_field === parentField));
|
|
211
|
-
});
|
|
212
|
-
// Filter key not found in parent collection
|
|
213
|
-
if (!relation)
|
|
214
|
-
throw new ForbiddenError();
|
|
215
|
-
parentCollection =
|
|
216
|
-
relation.related_collection === parentCollection ? relation.collection : relation.related_collection;
|
|
217
|
-
(result[parentCollection] || (result[parentCollection] = new Set())).add(filterKey);
|
|
218
|
-
}
|
|
219
|
-
if (typeof filterValue === 'object') {
|
|
220
|
-
// Parent collection is undefined when we process the top level filter
|
|
221
|
-
if (!parentCollection)
|
|
222
|
-
parentCollection = collection;
|
|
223
|
-
for (const [childFilterKey, childFilterValue] of Object.entries(filterValue)) {
|
|
224
|
-
if (childFilterKey.startsWith('_')) {
|
|
225
|
-
if (childFilterKey === '_and' || childFilterKey === '_or') {
|
|
226
|
-
if (isArray(childFilterValue)) {
|
|
227
|
-
for (const filter of childFilterValue) {
|
|
228
|
-
const requiredPermissions = extractRequiredFieldPermissions('', filter, parentCollection, filterKey);
|
|
229
|
-
result = mergeRequiredFieldPermissions(result, requiredPermissions);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
const requiredPermissions = extractRequiredFieldPermissions('', filterValue, parentCollection, filterKey);
|
|
236
|
-
result = mergeRequiredFieldPermissions(result, requiredPermissions);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return result;
|
|
242
|
-
}, {});
|
|
243
|
-
}
|
|
244
|
-
function mergeRequiredFieldPermissions(current, child) {
|
|
245
|
-
for (const collection of Object.keys(child)) {
|
|
246
|
-
if (!current[collection]) {
|
|
247
|
-
current[collection] = child[collection];
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
current[collection] = new Set([...current[collection], ...child[collection]]);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
return current;
|
|
254
|
-
}
|
|
255
|
-
function checkFieldPermissions(rootCollection, schema, action, requiredPermissions, aliasMap) {
|
|
256
|
-
if (accountability?.admin === true)
|
|
257
|
-
return;
|
|
258
|
-
for (const collection of Object.keys(requiredPermissions)) {
|
|
259
|
-
const permission = accountability?.permissions?.find((permission) => permission.collection === collection && permission.action === 'read');
|
|
260
|
-
let allowedFields;
|
|
261
|
-
// Allow the filtering of top level ID for actions such as update and delete
|
|
262
|
-
if (action !== 'read' && collection === rootCollection) {
|
|
263
|
-
const actionPermission = accountability?.permissions?.find((permission) => permission.collection === collection && permission.action === action);
|
|
264
|
-
if (!actionPermission || !actionPermission.fields) {
|
|
265
|
-
throw new ForbiddenError();
|
|
266
|
-
}
|
|
267
|
-
allowedFields = permission?.fields
|
|
268
|
-
? [...permission.fields, schema.collections[collection].primary]
|
|
269
|
-
: [schema.collections[collection].primary];
|
|
270
|
-
}
|
|
271
|
-
else if (!permission || !permission.fields) {
|
|
272
|
-
throw new ForbiddenError();
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
allowedFields = permission.fields;
|
|
276
|
-
}
|
|
277
|
-
if (allowedFields.includes('*'))
|
|
278
|
-
continue;
|
|
279
|
-
// Allow legacy permissions with an empty fields array, where id can be accessed
|
|
280
|
-
if (allowedFields.length === 0)
|
|
281
|
-
allowedFields.push(schema.collections[collection].primary);
|
|
282
|
-
for (const field of requiredPermissions[collection]) {
|
|
283
|
-
if (field.startsWith('$FOLLOW'))
|
|
284
|
-
continue;
|
|
285
|
-
const { fieldName } = parseFilterKey(field);
|
|
286
|
-
let originalFieldName = fieldName;
|
|
287
|
-
if (collection === rootCollection && aliasMap?.[fieldName]) {
|
|
288
|
-
originalFieldName = aliasMap[fieldName];
|
|
289
|
-
}
|
|
290
|
-
if (!allowedFields.includes(originalFieldName)) {
|
|
291
|
-
throw new ForbiddenError();
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
function applyFilters(ast, accountability) {
|
|
298
|
-
if (ast.type === 'functionField') {
|
|
299
|
-
const collection = ast.relatedCollection;
|
|
300
|
-
updateFilterQuery(collection, ast.query);
|
|
301
|
-
}
|
|
302
|
-
else if (ast.type !== 'field') {
|
|
303
|
-
if (ast.type === 'a2o') {
|
|
304
|
-
const collections = Object.keys(ast.children);
|
|
305
|
-
for (const collection of collections) {
|
|
306
|
-
updateFilterQuery(collection, ast.query[collection]);
|
|
307
|
-
}
|
|
308
|
-
for (const [collection, children] of Object.entries(ast.children)) {
|
|
309
|
-
ast.children[collection] = children.map((child) => applyFilters(child, accountability));
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
const collection = ast.name;
|
|
314
|
-
updateFilterQuery(collection, ast.query);
|
|
315
|
-
ast.children = ast.children.map((child) => applyFilters(child, accountability));
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
return ast;
|
|
319
|
-
function updateFilterQuery(collection, query) {
|
|
320
|
-
// We check the availability of the permissions in the step before this is run
|
|
321
|
-
const permissions = permissionsForCollections.find((permission) => permission.collection === collection);
|
|
322
|
-
if (!query.filter || Object.keys(query.filter).length === 0) {
|
|
323
|
-
query.filter = { _and: [] };
|
|
324
|
-
}
|
|
325
|
-
else {
|
|
326
|
-
query.filter = { _and: [query.filter] };
|
|
327
|
-
}
|
|
328
|
-
if (permissions.permissions && Object.keys(permissions.permissions).length > 0) {
|
|
329
|
-
query.filter._and.push(permissions.permissions);
|
|
330
|
-
}
|
|
331
|
-
if (query.filter._and.length === 0)
|
|
332
|
-
delete query.filter;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Checks if the provided payload matches the configured permissions, and adds the presets to the payload.
|
|
338
|
-
*/
|
|
339
|
-
validatePayload(action, collection, data) {
|
|
340
|
-
const payload = cloneDeep(data);
|
|
341
|
-
let permission;
|
|
342
|
-
if (this.accountability?.admin === true) {
|
|
343
|
-
permission = {
|
|
344
|
-
id: 0,
|
|
345
|
-
role: this.accountability?.role,
|
|
346
|
-
collection,
|
|
347
|
-
action,
|
|
348
|
-
permissions: {},
|
|
349
|
-
validation: {},
|
|
350
|
-
fields: ['*'],
|
|
351
|
-
presets: {},
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
355
|
-
permission = this.accountability?.permissions?.find((permission) => {
|
|
356
|
-
return permission.collection === collection && permission.action === action;
|
|
357
|
-
});
|
|
358
|
-
if (!permission)
|
|
359
|
-
throw new ForbiddenError();
|
|
360
|
-
// Check if you have permission to access the fields you're trying to access
|
|
361
|
-
const allowedFields = permission.fields || [];
|
|
362
|
-
if (allowedFields.includes('*') === false) {
|
|
363
|
-
const keysInData = Object.keys(payload);
|
|
364
|
-
const invalidKeys = keysInData.filter((fieldKey) => allowedFields.includes(fieldKey) === false);
|
|
365
|
-
if (invalidKeys.length > 0) {
|
|
366
|
-
throw new ForbiddenError();
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
const preset = permission.presets ?? {};
|
|
371
|
-
const payloadWithPresets = merge({}, preset, payload);
|
|
372
|
-
const fieldValidationRules = Object.values(this.schema.collections[collection].fields)
|
|
373
|
-
.map((field) => field.validation)
|
|
374
|
-
.filter((v) => v);
|
|
375
|
-
const hasValidationRules = isNil(permission.validation) === false && Object.keys(permission.validation ?? {}).length > 0;
|
|
376
|
-
const hasFieldValidationRules = fieldValidationRules && fieldValidationRules.length > 0;
|
|
377
|
-
const requiredColumns = [];
|
|
378
|
-
for (const field of Object.values(this.schema.collections[collection].fields)) {
|
|
379
|
-
const specials = field?.special ?? [];
|
|
380
|
-
const hasGenerateSpecial = GENERATE_SPECIAL.some((name) => specials.includes(name));
|
|
381
|
-
const nullable = field.nullable || hasGenerateSpecial || field.generated;
|
|
382
|
-
if (!nullable) {
|
|
383
|
-
requiredColumns.push(field);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
if (hasValidationRules === false && hasFieldValidationRules === false && requiredColumns.length === 0) {
|
|
387
|
-
return payloadWithPresets;
|
|
388
|
-
}
|
|
389
|
-
if (requiredColumns.length > 0) {
|
|
390
|
-
permission.validation = hasValidationRules ? { _and: [permission.validation] } : { _and: [] };
|
|
391
|
-
for (const field of requiredColumns) {
|
|
392
|
-
if (action === 'create' && field.defaultValue === null) {
|
|
393
|
-
permission.validation._and.push({
|
|
394
|
-
[field.field]: {
|
|
395
|
-
_submitted: true,
|
|
396
|
-
},
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
permission.validation._and.push({
|
|
400
|
-
[field.field]: {
|
|
401
|
-
_nnull: true,
|
|
402
|
-
},
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
if (hasFieldValidationRules) {
|
|
407
|
-
if (permission.validation && Object.keys(permission.validation).length > 0) {
|
|
408
|
-
permission.validation = { _and: [permission.validation, ...fieldValidationRules] };
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
permission.validation = { _and: fieldValidationRules };
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
const validationErrors = [];
|
|
415
|
-
validationErrors.push(...flatten(validatePayload(permission.validation, payloadWithPresets).map((error) => error.details.map((details) => new FailedValidationError(joiValidationErrorItemToErrorExtensions(details))))));
|
|
416
|
-
if (validationErrors.length > 0)
|
|
417
|
-
throw validationErrors;
|
|
418
|
-
return payloadWithPresets;
|
|
419
|
-
}
|
|
420
|
-
async checkAccess(action, collection, pk) {
|
|
421
|
-
if (this.accountability?.admin === true)
|
|
422
|
-
return;
|
|
423
|
-
const itemsService = new ItemsService(collection, {
|
|
424
|
-
accountability: this.accountability,
|
|
425
|
-
knex: this.knex,
|
|
426
|
-
schema: this.schema,
|
|
427
|
-
});
|
|
428
|
-
const query = {
|
|
429
|
-
fields: ['*'],
|
|
430
|
-
};
|
|
431
|
-
if (Array.isArray(pk)) {
|
|
432
|
-
const result = await itemsService.readMany(pk, { ...query, limit: pk.length }, { permissionsAction: action });
|
|
433
|
-
// for the unexpected case that the result is not an array (for example due to filter hook)
|
|
434
|
-
if (!isArray(result))
|
|
435
|
-
throw new ForbiddenError();
|
|
436
|
-
if (result.length !== pk.length)
|
|
437
|
-
throw new ForbiddenError();
|
|
438
|
-
}
|
|
439
|
-
else if (pk) {
|
|
440
|
-
const result = await itemsService.readOne(pk, query, { permissionsAction: action });
|
|
441
|
-
if (!result)
|
|
442
|
-
throw new ForbiddenError();
|
|
443
|
-
}
|
|
444
|
-
else {
|
|
445
|
-
query.limit = 1;
|
|
446
|
-
const result = await itemsService.readByQuery(query, { permissionsAction: action });
|
|
447
|
-
// for the unexpected case that the result is not an array (for example due to filter hook)
|
|
448
|
-
if (!isArray(result))
|
|
449
|
-
throw new ForbiddenError();
|
|
450
|
-
// for create action, an empty array is expected - for other actions, the first item is expected to be available
|
|
451
|
-
const access = action === 'create' ? result.length === 0 : !!result[0];
|
|
452
|
-
if (!access)
|
|
453
|
-
throw new ForbiddenError();
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { appAccessMinimalPermissions } from '@directus/system-data';
|
|
2
|
-
import { filterItems } from '../../../utils/filter-items.js';
|
|
3
|
-
import { mergePermissions } from '../../../utils/merge-permissions.js';
|
|
4
|
-
export function withAppMinimalPermissions(accountability, permissions, filter) {
|
|
5
|
-
if (accountability?.app === true) {
|
|
6
|
-
const filteredAppMinimalPermissions = filterItems(appAccessMinimalPermissions.map((permission) => ({
|
|
7
|
-
...permission,
|
|
8
|
-
role: accountability.role,
|
|
9
|
-
})), filter);
|
|
10
|
-
return mergePermissions('or', permissions, filteredAppMinimalPermissions);
|
|
11
|
-
}
|
|
12
|
-
return permissions;
|
|
13
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { PrimaryKey } from '@directus/types';
|
|
2
|
-
import type { Knex } from 'knex';
|
|
3
|
-
import { type AccessTypeCount } from './get-user-count.js';
|
|
4
|
-
/**
|
|
5
|
-
* Ensure that user limits are not reached
|
|
6
|
-
*/
|
|
7
|
-
export declare function checkIncreasedUserLimits(db: Knex, increasedUserCounts: AccessTypeCount, ignoreIds?: PrimaryKey[]): Promise<void>;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { useEnv } from '@directus/env';
|
|
2
|
-
import { LimitExceededError } from '@directus/errors';
|
|
3
|
-
import { getUserCount } from './get-user-count.js';
|
|
4
|
-
const env = useEnv();
|
|
5
|
-
/**
|
|
6
|
-
* Ensure that user limits are not reached
|
|
7
|
-
*/
|
|
8
|
-
export async function checkIncreasedUserLimits(db, increasedUserCounts, ignoreIds = []) {
|
|
9
|
-
if (!increasedUserCounts.admin && !increasedUserCounts.app && !increasedUserCounts.api)
|
|
10
|
-
return;
|
|
11
|
-
const userCounts = await getUserCount(db, ignoreIds);
|
|
12
|
-
// Admins have full permissions, therefore should count under app access limit
|
|
13
|
-
const existingAppUsersCount = userCounts.admin + userCounts.app;
|
|
14
|
-
const newAppUsersCount = increasedUserCounts.admin + increasedUserCounts.app;
|
|
15
|
-
if (increasedUserCounts.admin > 0 &&
|
|
16
|
-
increasedUserCounts.admin + userCounts.admin > Number(env['USERS_ADMIN_ACCESS_LIMIT'])) {
|
|
17
|
-
throw new LimitExceededError({ category: 'Active Admin users' });
|
|
18
|
-
}
|
|
19
|
-
if (newAppUsersCount > 0 && newAppUsersCount + existingAppUsersCount > Number(env['USERS_APP_ACCESS_LIMIT'])) {
|
|
20
|
-
throw new LimitExceededError({ category: 'Active App users' });
|
|
21
|
-
}
|
|
22
|
-
if (increasedUserCounts.api > 0 && increasedUserCounts.api + userCounts.api > Number(env['USERS_API_ACCESS_LIMIT'])) {
|
|
23
|
-
throw new LimitExceededError({ category: 'Active API users' });
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { toBoolean } from '@directus/utils';
|
|
2
|
-
import {} from './get-user-count.js';
|
|
3
|
-
/**
|
|
4
|
-
* Get the role type counts by role IDs
|
|
5
|
-
*/
|
|
6
|
-
export async function getRoleCountsByRoles(db, roles) {
|
|
7
|
-
const counts = {
|
|
8
|
-
admin: 0,
|
|
9
|
-
app: 0,
|
|
10
|
-
api: 0,
|
|
11
|
-
};
|
|
12
|
-
const result = (await db.select('id', 'admin_access', 'app_access').from('directus_roles').whereIn('id', roles));
|
|
13
|
-
for (const role of result) {
|
|
14
|
-
const adminAccess = toBoolean(role.admin_access);
|
|
15
|
-
const appAccess = toBoolean(role.app_access);
|
|
16
|
-
if (adminAccess) {
|
|
17
|
-
counts.admin++;
|
|
18
|
-
}
|
|
19
|
-
else if (appAccess) {
|
|
20
|
-
counts.app++;
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
counts.api++;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return counts;
|
|
27
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { PrimaryKey } from '@directus/types';
|
|
2
|
-
import type { Knex } from 'knex';
|
|
3
|
-
import type { AccessTypeCount } from './get-user-count.js';
|
|
4
|
-
type CountOptions = {
|
|
5
|
-
inactiveUsers?: boolean;
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* Get the role type counts by user IDs
|
|
9
|
-
*/
|
|
10
|
-
export declare function getRoleCountsByUsers(db: Knex, userIds: PrimaryKey[], options?: CountOptions): Promise<AccessTypeCount>;
|
|
11
|
-
export {};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { toBoolean } from '@directus/utils';
|
|
2
|
-
/**
|
|
3
|
-
* Get the role type counts by user IDs
|
|
4
|
-
*/
|
|
5
|
-
export async function getRoleCountsByUsers(db, userIds, options = {}) {
|
|
6
|
-
const counts = {
|
|
7
|
-
admin: 0,
|
|
8
|
-
app: 0,
|
|
9
|
-
api: 0,
|
|
10
|
-
};
|
|
11
|
-
const result = await db
|
|
12
|
-
.count('directus_users.id', { as: 'count' })
|
|
13
|
-
.select('directus_roles.admin_access', 'directus_roles.app_access')
|
|
14
|
-
.from('directus_users')
|
|
15
|
-
.whereIn('directus_users.id', userIds)
|
|
16
|
-
.andWhere('directus_users.status', options.inactiveUsers ? '!=' : '=', 'active')
|
|
17
|
-
.leftJoin('directus_roles', 'directus_users.role', '=', 'directus_roles.id')
|
|
18
|
-
.groupBy('directus_roles.admin_access', 'directus_roles.app_access');
|
|
19
|
-
for (const record of result) {
|
|
20
|
-
const adminAccess = toBoolean(record.admin_access);
|
|
21
|
-
const appAccess = toBoolean(record.app_access);
|
|
22
|
-
const count = Number(record.count);
|
|
23
|
-
if (adminAccess) {
|
|
24
|
-
counts.admin += count;
|
|
25
|
-
}
|
|
26
|
-
else if (appAccess) {
|
|
27
|
-
counts.app += count;
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
counts.api += count;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return counts;
|
|
34
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { PrimaryKey } from '@directus/types';
|
|
2
|
-
import { type Knex } from 'knex';
|
|
3
|
-
export interface AccessTypeCount {
|
|
4
|
-
admin: number;
|
|
5
|
-
app: number;
|
|
6
|
-
api: number;
|
|
7
|
-
}
|
|
8
|
-
export declare const getUserCount: (db: Knex, ignoreIds?: PrimaryKey[]) => Promise<AccessTypeCount>;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { toBoolean } from '@directus/utils';
|
|
2
|
-
import {} from 'knex';
|
|
3
|
-
export const getUserCount = async (db, ignoreIds = []) => {
|
|
4
|
-
const counts = {
|
|
5
|
-
admin: 0,
|
|
6
|
-
app: 0,
|
|
7
|
-
api: 0,
|
|
8
|
-
};
|
|
9
|
-
const result = (await db
|
|
10
|
-
.count('directus_users.id', { as: 'count' })
|
|
11
|
-
.select('directus_roles.admin_access', 'directus_roles.app_access')
|
|
12
|
-
.from('directus_users')
|
|
13
|
-
.whereNotIn('directus_users.id', ignoreIds)
|
|
14
|
-
.andWhere('directus_users.status', 'active')
|
|
15
|
-
.leftJoin('directus_roles', 'directus_users.role', '=', 'directus_roles.id')
|
|
16
|
-
.where('directus_users.status', '=', 'active')
|
|
17
|
-
.groupBy('directus_roles.admin_access', 'directus_roles.app_access'));
|
|
18
|
-
for (const record of result) {
|
|
19
|
-
const adminAccess = toBoolean(record.admin_access);
|
|
20
|
-
const appAccess = toBoolean(record.app_access);
|
|
21
|
-
const count = Number(record.count);
|
|
22
|
-
if (adminAccess) {
|
|
23
|
-
counts.admin += count;
|
|
24
|
-
}
|
|
25
|
-
else if (appAccess) {
|
|
26
|
-
counts.app += count;
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
counts.api += count;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return counts;
|
|
33
|
-
};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { PrimaryKey } from '@directus/types';
|
|
2
|
-
import type { Knex } from 'knex';
|
|
3
|
-
import { type AccessTypeCount } from './get-user-count.js';
|
|
4
|
-
/**
|
|
5
|
-
* Get the user type counts by role IDs
|
|
6
|
-
*/
|
|
7
|
-
export declare function getUserCountsByRoles(db: Knex, roleIds: PrimaryKey[]): Promise<AccessTypeCount>;
|