@directus/api 21.0.0-rc.0 → 21.0.1
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 +4 -4
- package/dist/auth/drivers/local.js +4 -4
- package/dist/auth/drivers/oauth2.js +4 -4
- package/dist/auth/drivers/openid.js +4 -2
- package/dist/cache.d.ts +2 -3
- package/dist/cache.js +9 -27
- package/dist/cli/commands/bootstrap/index.js +2 -8
- package/dist/cli/commands/init/index.js +10 -9
- package/dist/cli/utils/defaults.d.ts +11 -4
- package/dist/cli/utils/defaults.js +1 -7
- package/dist/constants.d.ts +1 -1
- package/dist/controllers/auth.js +16 -5
- package/dist/controllers/permissions.js +2 -14
- package/dist/controllers/roles.js +1 -22
- package/dist/controllers/tus.js +27 -13
- package/dist/controllers/users.js +55 -0
- package/dist/database/helpers/fn/types.d.ts +1 -2
- 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 +2 -4
- 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 +3 -5
- package/dist/database/helpers/geometry/types.d.ts +1 -1
- package/dist/database/helpers/geometry/types.js +2 -4
- package/dist/database/index.js +11 -8
- package/dist/database/migrations/20240305A-change-useragent-type.js +1 -1
- package/dist/database/migrations/20240716A-update-files-date-fields.js +33 -0
- package/dist/database/{run-ast/types.d.ts → run-ast.d.ts} +9 -3
- package/dist/database/run-ast.js +458 -0
- package/dist/flows.js +4 -3
- package/dist/logger/index.js +1 -1
- package/dist/middleware/authenticate.js +7 -2
- package/dist/middleware/cache.js +1 -1
- package/dist/middleware/check-ip.d.ts +2 -0
- package/dist/middleware/check-ip.js +37 -0
- package/dist/middleware/error-handler.d.ts +2 -2
- package/dist/middleware/error-handler.js +54 -51
- package/dist/middleware/get-permissions.d.ts +3 -0
- package/dist/middleware/get-permissions.js +10 -0
- package/dist/middleware/respond.js +2 -1
- package/dist/request/is-denied-ip.js +7 -1
- package/dist/services/activity.js +10 -22
- package/dist/services/assets.d.ts +3 -2
- package/dist/services/assets.js +7 -15
- package/dist/services/authentication.js +18 -18
- package/dist/services/authorization.d.ts +17 -0
- package/dist/services/authorization.js +456 -0
- package/dist/services/collections.js +17 -18
- package/dist/services/fields.d.ts +4 -0
- package/dist/services/fields.js +53 -58
- package/dist/services/files/lib/get-sharp-instance.d.ts +2 -0
- package/dist/services/files/lib/get-sharp-instance.js +10 -0
- package/dist/services/files/utils/get-metadata.js +7 -6
- package/dist/services/files.js +8 -10
- package/dist/services/graphql/index.d.ts +3 -3
- package/dist/services/graphql/index.js +22 -126
- package/dist/services/graphql/subscription.js +4 -2
- package/dist/services/import-export.js +4 -18
- package/dist/services/index.d.ts +2 -3
- package/dist/services/index.js +2 -3
- package/dist/services/items.js +44 -115
- package/dist/services/mail/index.d.ts +1 -1
- package/dist/services/mail/index.js +9 -1
- package/dist/services/meta.js +23 -60
- package/dist/services/notifications.js +6 -14
- package/dist/services/payload.d.ts +10 -9
- package/dist/services/payload.js +3 -18
- package/dist/services/{permissions.d.ts → permissions/index.d.ts} +7 -5
- package/dist/services/{permissions.js → permissions/index.js} +54 -30
- package/dist/{permissions → services/permissions}/lib/with-app-minimal-permissions.d.ts +1 -1
- package/dist/services/permissions/lib/with-app-minimal-permissions.js +13 -0
- package/dist/services/relations.d.ts +9 -1
- package/dist/services/relations.js +56 -31
- package/dist/services/roles.d.ts +12 -4
- package/dist/services/roles.js +424 -57
- package/dist/services/shares.d.ts +2 -0
- package/dist/services/shares.js +8 -12
- package/dist/services/specifications.d.ts +2 -2
- package/dist/services/specifications.js +27 -39
- package/dist/services/tus/data-store.js +4 -5
- package/dist/services/tus/server.d.ts +1 -1
- package/dist/services/tus/server.js +9 -2
- package/dist/services/users.d.ts +5 -1
- package/dist/services/users.js +161 -78
- package/dist/services/utils.js +7 -11
- package/dist/services/versions.d.ts +2 -0
- package/dist/services/versions.js +10 -34
- package/dist/telemetry/lib/get-report.js +2 -2
- package/dist/telemetry/utils/check-increased-user-limits.d.ts +7 -0
- package/dist/telemetry/utils/check-increased-user-limits.js +25 -0
- package/dist/telemetry/utils/get-role-counts-by-roles.d.ts +6 -0
- package/dist/telemetry/utils/get-role-counts-by-roles.js +27 -0
- package/dist/telemetry/utils/get-role-counts-by-users.d.ts +11 -0
- package/dist/telemetry/utils/get-role-counts-by-users.js +34 -0
- package/dist/telemetry/utils/get-user-count.d.ts +8 -0
- package/dist/telemetry/utils/get-user-count.js +33 -0
- package/dist/telemetry/utils/get-user-counts-by-roles.d.ts +7 -0
- package/dist/telemetry/utils/get-user-counts-by-roles.js +35 -0
- package/dist/types/ast.d.ts +1 -43
- package/dist/types/items.d.ts +0 -11
- package/dist/utils/apply-query.d.ts +3 -4
- package/dist/utils/apply-query.js +16 -39
- package/dist/utils/get-accountability-for-role.js +25 -16
- package/dist/utils/get-accountability-for-token.js +16 -17
- package/dist/utils/get-ast-from-query.d.ts +13 -0
- package/dist/utils/get-ast-from-query.js +297 -0
- package/dist/utils/get-cache-key.d.ts +1 -1
- package/dist/utils/get-cache-key.js +1 -12
- package/dist/utils/get-column.d.ts +1 -2
- package/dist/utils/get-column.js +0 -1
- package/dist/utils/get-permissions.d.ts +2 -0
- package/dist/utils/get-permissions.js +150 -0
- package/dist/utils/get-schema.js +22 -27
- package/dist/utils/get-service.js +1 -5
- package/dist/utils/merge-permissions-for-share.d.ts +4 -0
- package/dist/utils/merge-permissions-for-share.js +109 -0
- package/dist/utils/merge-permissions.d.ts +3 -0
- package/dist/utils/merge-permissions.js +95 -0
- package/dist/utils/reduce-schema.d.ts +6 -4
- package/dist/utils/reduce-schema.js +32 -16
- package/dist/websocket/authenticate.d.ts +2 -0
- package/dist/websocket/authenticate.js +12 -0
- package/dist/websocket/controllers/graphql.js +4 -1
- package/dist/websocket/controllers/hooks.js +0 -4
- package/dist/websocket/controllers/rest.js +2 -0
- package/dist/websocket/handlers/subscribe.js +2 -0
- package/dist/websocket/utils/items.d.ts +1 -1
- package/package.json +35 -36
- package/dist/controllers/access.d.ts +0 -2
- package/dist/controllers/access.js +0 -148
- package/dist/controllers/policies.d.ts +0 -2
- package/dist/controllers/policies.js +0 -169
- package/dist/database/get-ast-from-query/get-ast-from-query.d.ts +0 -16
- package/dist/database/get-ast-from-query/get-ast-from-query.js +0 -82
- package/dist/database/get-ast-from-query/lib/convert-wildcards.d.ts +0 -13
- package/dist/database/get-ast-from-query/lib/convert-wildcards.js +0 -69
- package/dist/database/get-ast-from-query/lib/parse-fields.d.ts +0 -15
- package/dist/database/get-ast-from-query/lib/parse-fields.js +0 -190
- package/dist/database/get-ast-from-query/utils/get-deep-query.d.ts +0 -14
- package/dist/database/get-ast-from-query/utils/get-deep-query.js +0 -17
- package/dist/database/get-ast-from-query/utils/get-related-collection.d.ts +0 -2
- package/dist/database/get-ast-from-query/utils/get-related-collection.js +0 -13
- package/dist/database/get-ast-from-query/utils/get-relation.d.ts +0 -2
- package/dist/database/get-ast-from-query/utils/get-relation.js +0 -7
- package/dist/database/migrations/20240710A-permissions-policies.js +0 -169
- package/dist/database/run-ast/lib/get-db-query.d.ts +0 -4
- package/dist/database/run-ast/lib/get-db-query.js +0 -208
- package/dist/database/run-ast/lib/parse-current-level.d.ts +0 -7
- package/dist/database/run-ast/lib/parse-current-level.js +0 -41
- package/dist/database/run-ast/run-ast.d.ts +0 -7
- package/dist/database/run-ast/run-ast.js +0 -107
- package/dist/database/run-ast/types.js +0 -1
- package/dist/database/run-ast/utils/apply-case-when.d.ts +0 -16
- package/dist/database/run-ast/utils/apply-case-when.js +0 -26
- package/dist/database/run-ast/utils/apply-parent-filters.d.ts +0 -3
- package/dist/database/run-ast/utils/apply-parent-filters.js +0 -55
- package/dist/database/run-ast/utils/get-column-pre-processor.d.ts +0 -10
- package/dist/database/run-ast/utils/get-column-pre-processor.js +0 -57
- package/dist/database/run-ast/utils/get-field-alias.d.ts +0 -2
- package/dist/database/run-ast/utils/get-field-alias.js +0 -4
- package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.d.ts +0 -5
- package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.js +0 -23
- package/dist/database/run-ast/utils/merge-with-parent-items.d.ts +0 -3
- package/dist/database/run-ast/utils/merge-with-parent-items.js +0 -87
- package/dist/database/run-ast/utils/remove-temporary-fields.d.ts +0 -3
- package/dist/database/run-ast/utils/remove-temporary-fields.js +0 -73
- package/dist/permissions/cache.d.ts +0 -2
- package/dist/permissions/cache.js +0 -23
- package/dist/permissions/lib/fetch-permissions.d.ts +0 -10
- package/dist/permissions/lib/fetch-permissions.js +0 -55
- package/dist/permissions/lib/fetch-policies.d.ts +0 -7
- package/dist/permissions/lib/fetch-policies.js +0 -28
- package/dist/permissions/lib/fetch-roles-tree.d.ts +0 -3
- package/dist/permissions/lib/fetch-roles-tree.js +0 -28
- package/dist/permissions/lib/with-app-minimal-permissions.js +0 -10
- package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.d.ts +0 -7
- package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js +0 -56
- package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.d.ts +0 -3
- package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js +0 -16
- package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.d.ts +0 -8
- package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +0 -24
- package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.d.ts +0 -9
- package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +0 -31
- package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.d.ts +0 -16
- package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js +0 -27
- package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +0 -10
- package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +0 -23
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +0 -5
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +0 -7
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +0 -5
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +0 -10
- package/dist/permissions/modules/fetch-global-access/types.d.ts +0 -4
- package/dist/permissions/modules/fetch-global-access/types.js +0 -1
- package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +0 -4
- package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +0 -27
- package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.d.ts +0 -12
- package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +0 -32
- package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +0 -4
- package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.js +0 -29
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.d.ts +0 -4
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.js +0 -49
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.d.ts +0 -3
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.js +0 -56
- package/dist/permissions/modules/process-ast/lib/field-map-from-ast.d.ts +0 -4
- package/dist/permissions/modules/process-ast/lib/field-map-from-ast.js +0 -8
- package/dist/permissions/modules/process-ast/lib/inject-cases.d.ts +0 -9
- package/dist/permissions/modules/process-ast/lib/inject-cases.js +0 -93
- package/dist/permissions/modules/process-ast/process-ast.d.ts +0 -9
- package/dist/permissions/modules/process-ast/process-ast.js +0 -39
- package/dist/permissions/modules/process-ast/types.d.ts +0 -24
- package/dist/permissions/modules/process-ast/types.js +0 -1
- package/dist/permissions/modules/process-ast/utils/collections-in-field-map.d.ts +0 -2
- package/dist/permissions/modules/process-ast/utils/collections-in-field-map.js +0 -7
- package/dist/permissions/modules/process-ast/utils/dedupe-access.d.ts +0 -12
- package/dist/permissions/modules/process-ast/utils/dedupe-access.js +0 -30
- package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.d.ts +0 -15
- package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +0 -50
- package/dist/permissions/modules/process-ast/utils/find-related-collection.d.ts +0 -3
- package/dist/permissions/modules/process-ast/utils/find-related-collection.js +0 -9
- package/dist/permissions/modules/process-ast/utils/flatten-filter.d.ts +0 -3
- package/dist/permissions/modules/process-ast/utils/flatten-filter.js +0 -34
- package/dist/permissions/modules/process-ast/utils/format-a2o-key.d.ts +0 -1
- package/dist/permissions/modules/process-ast/utils/format-a2o-key.js +0 -3
- package/dist/permissions/modules/process-ast/utils/get-info-for-path.d.ts +0 -5
- package/dist/permissions/modules/process-ast/utils/get-info-for-path.js +0 -7
- package/dist/permissions/modules/process-ast/utils/has-item-permissions.d.ts +0 -2
- package/dist/permissions/modules/process-ast/utils/has-item-permissions.js +0 -3
- package/dist/permissions/modules/process-ast/utils/stringify-query-path.d.ts +0 -2
- package/dist/permissions/modules/process-ast/utils/stringify-query-path.js +0 -3
- package/dist/permissions/modules/process-ast/utils/validate-path/create-error.d.ts +0 -3
- package/dist/permissions/modules/process-ast/utils/validate-path/create-error.js +0 -16
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.d.ts +0 -2
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.js +0 -12
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.d.ts +0 -2
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.js +0 -28
- package/dist/permissions/modules/process-payload/lib/is-field-nullable.d.ts +0 -5
- package/dist/permissions/modules/process-payload/lib/is-field-nullable.js +0 -12
- package/dist/permissions/modules/process-payload/process-payload.d.ts +0 -13
- package/dist/permissions/modules/process-payload/process-payload.js +0 -77
- package/dist/permissions/modules/validate-access/lib/validate-collection-access.d.ts +0 -12
- package/dist/permissions/modules/validate-access/lib/validate-collection-access.js +0 -11
- package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +0 -9
- package/dist/permissions/modules/validate-access/lib/validate-item-access.js +0 -33
- package/dist/permissions/modules/validate-access/validate-access.d.ts +0 -14
- package/dist/permissions/modules/validate-access/validate-access.js +0 -28
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.d.ts +0 -1
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.js +0 -8
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.d.ts +0 -5
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.js +0 -10
- package/dist/permissions/types.d.ts +0 -6
- package/dist/permissions/types.js +0 -1
- package/dist/permissions/utils/create-default-accountability.d.ts +0 -2
- package/dist/permissions/utils/create-default-accountability.js +0 -11
- package/dist/permissions/utils/extract-required-dynamic-variable-context.d.ts +0 -8
- package/dist/permissions/utils/extract-required-dynamic-variable-context.js +0 -27
- package/dist/permissions/utils/fetch-dynamic-variable-context.d.ts +0 -9
- package/dist/permissions/utils/fetch-dynamic-variable-context.js +0 -43
- package/dist/permissions/utils/filter-policies-by-ip.d.ts +0 -2
- package/dist/permissions/utils/filter-policies-by-ip.js +0 -15
- package/dist/permissions/utils/get-unaliased-field-key.d.ts +0 -5
- package/dist/permissions/utils/get-unaliased-field-key.js +0 -17
- package/dist/permissions/utils/process-permissions.d.ts +0 -7
- package/dist/permissions/utils/process-permissions.js +0 -9
- package/dist/permissions/utils/with-cache.d.ts +0 -10
- package/dist/permissions/utils/with-cache.js +0 -25
- package/dist/services/access.d.ts +0 -10
- package/dist/services/access.js +0 -43
- package/dist/services/policies.d.ts +0 -12
- package/dist/services/policies.js +0 -87
- package/dist/telemetry/utils/check-user-limits.d.ts +0 -5
- package/dist/telemetry/utils/check-user-limits.js +0 -19
- package/dist/utils/fetch-user-count/fetch-access-lookup.d.ts +0 -17
- package/dist/utils/fetch-user-count/fetch-access-lookup.js +0 -22
- package/dist/utils/fetch-user-count/fetch-access-roles.d.ts +0 -16
- package/dist/utils/fetch-user-count/fetch-access-roles.js +0 -37
- package/dist/utils/fetch-user-count/fetch-active-users.d.ts +0 -6
- package/dist/utils/fetch-user-count/fetch-active-users.js +0 -3
- package/dist/utils/fetch-user-count/fetch-user-count.d.ts +0 -12
- package/dist/utils/fetch-user-count/fetch-user-count.js +0 -57
- package/dist/utils/fetch-user-count/get-user-count-query.d.ts +0 -20
- package/dist/utils/fetch-user-count/get-user-count-query.js +0 -17
- package/dist/utils/validate-user-count-integrity.d.ts +0 -13
- package/dist/utils/validate-user-count-integrity.js +0 -29
- /package/dist/database/migrations/{20240710A-permissions-policies.d.ts → 20240716A-update-files-date-fields.d.ts} +0 -0
|
@@ -0,0 +1,456 @@
|
|
|
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
|
+
}
|
|
@@ -9,8 +9,6 @@ import { ALIAS_TYPES } from '../constants.js';
|
|
|
9
9
|
import { getHelpers } from '../database/helpers/index.js';
|
|
10
10
|
import getDatabase, { getSchemaInspector } from '../database/index.js';
|
|
11
11
|
import emitter from '../emitter.js';
|
|
12
|
-
import { fetchAllowedCollections } from '../permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js';
|
|
13
|
-
import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
|
|
14
12
|
import { getSchema } from '../utils/get-schema.js';
|
|
15
13
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
16
14
|
import { transaction } from '../utils/transaction.js';
|
|
@@ -225,13 +223,11 @@ export class CollectionsService {
|
|
|
225
223
|
...meta,
|
|
226
224
|
[item.collection]: item.group,
|
|
227
225
|
}), {});
|
|
228
|
-
let collectionsYouHavePermissionToRead =
|
|
229
|
-
|
|
230
|
-
action
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
schema: this.schema,
|
|
234
|
-
});
|
|
226
|
+
let collectionsYouHavePermissionToRead = this.accountability
|
|
227
|
+
.permissions.filter((permission) => {
|
|
228
|
+
return permission.action === 'read';
|
|
229
|
+
})
|
|
230
|
+
.map(({ collection }) => collection);
|
|
235
231
|
for (const collection of collectionsYouHavePermissionToRead) {
|
|
236
232
|
const group = collectionsGroups[collection];
|
|
237
233
|
if (group)
|
|
@@ -283,15 +279,18 @@ export class CollectionsService {
|
|
|
283
279
|
* Read many collections by name
|
|
284
280
|
*/
|
|
285
281
|
async readMany(collectionKeys) {
|
|
286
|
-
if (this.accountability) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
282
|
+
if (this.accountability && this.accountability.admin !== true) {
|
|
283
|
+
const permissions = this.accountability.permissions.filter((permission) => {
|
|
284
|
+
return permission.action === 'read' && collectionKeys.includes(permission.collection);
|
|
285
|
+
});
|
|
286
|
+
if (collectionKeys.length !== permissions.length) {
|
|
287
|
+
const collectionsYouHavePermissionToRead = permissions.map(({ collection }) => collection);
|
|
288
|
+
for (const collectionKey of collectionKeys) {
|
|
289
|
+
if (collectionsYouHavePermissionToRead.includes(collectionKey) === false) {
|
|
290
|
+
throw new ForbiddenError();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
295
294
|
}
|
|
296
295
|
const collections = await this.readByQuery();
|
|
297
296
|
return collections.filter(({ collection }) => collectionKeys.includes(collection));
|
|
@@ -16,7 +16,11 @@ export declare class FieldsService {
|
|
|
16
16
|
schema: SchemaOverview;
|
|
17
17
|
cache: Keyv<any> | null;
|
|
18
18
|
systemCache: Keyv<any>;
|
|
19
|
+
schemaCache: Keyv<any>;
|
|
19
20
|
constructor(options: AbstractServiceOptions);
|
|
21
|
+
private get hasReadAccess();
|
|
22
|
+
columnInfo(collection?: string): Promise<Column[]>;
|
|
23
|
+
columnInfo(collection: string, field: string): Promise<Column>;
|
|
20
24
|
readAll(collection?: string): Promise<Field[]>;
|
|
21
25
|
readOne(collection: string, field: string): Promise<Record<string, any>>;
|
|
22
26
|
createField(collection: string, field: Partial<Field> & {
|