@directus/api 21.0.0-rc.0 → 21.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +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 +0 -1
- package/dist/cache.js +7 -25
- 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 +1 -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 +3 -3
- 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 +36 -37
- 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
|
@@ -1,40 +1,41 @@
|
|
|
1
1
|
import { InvalidCredentialsError } from '@directus/errors';
|
|
2
2
|
import getDatabase from '../database/index.js';
|
|
3
|
-
import { fetchRolesTree } from '../permissions/lib/fetch-roles-tree.js';
|
|
4
|
-
import { fetchGlobalAccess } from '../permissions/modules/fetch-global-access/fetch-global-access.js';
|
|
5
|
-
import { createDefaultAccountability } from '../permissions/utils/create-default-accountability.js';
|
|
6
3
|
import { getSecret } from './get-secret.js';
|
|
7
4
|
import isDirectusJWT from './is-directus-jwt.js';
|
|
8
|
-
import { verifyAccessJWT } from './jwt.js';
|
|
9
5
|
import { verifySessionJWT } from './verify-session-jwt.js';
|
|
6
|
+
import { verifyAccessJWT } from './jwt.js';
|
|
10
7
|
export async function getAccountabilityForToken(token, accountability) {
|
|
11
8
|
if (!accountability) {
|
|
12
|
-
accountability =
|
|
9
|
+
accountability = {
|
|
10
|
+
user: null,
|
|
11
|
+
role: null,
|
|
12
|
+
admin: false,
|
|
13
|
+
app: false,
|
|
14
|
+
};
|
|
13
15
|
}
|
|
14
|
-
// Try finding the user with the provided token
|
|
15
|
-
const database = getDatabase();
|
|
16
16
|
if (token) {
|
|
17
17
|
if (isDirectusJWT(token)) {
|
|
18
18
|
const payload = verifyAccessJWT(token, getSecret());
|
|
19
19
|
if ('session' in payload) {
|
|
20
20
|
await verifySessionJWT(payload);
|
|
21
21
|
}
|
|
22
|
+
accountability.role = payload.role;
|
|
23
|
+
accountability.admin = payload.admin_access === true || payload.admin_access == 1;
|
|
24
|
+
accountability.app = payload.app_access === true || payload.app_access == 1;
|
|
22
25
|
if (payload.share)
|
|
23
26
|
accountability.share = payload.share;
|
|
24
27
|
if (payload.share_scope)
|
|
25
28
|
accountability.share_scope = payload.share_scope;
|
|
26
29
|
if (payload.id)
|
|
27
30
|
accountability.user = payload.id;
|
|
28
|
-
accountability.role = payload.role;
|
|
29
|
-
accountability.roles = await fetchRolesTree(payload.role, database);
|
|
30
|
-
const { admin, app } = await fetchGlobalAccess(accountability, database);
|
|
31
|
-
accountability.admin = admin;
|
|
32
|
-
accountability.app = app;
|
|
33
31
|
}
|
|
34
32
|
else {
|
|
33
|
+
// Try finding the user with the provided token
|
|
34
|
+
const database = getDatabase();
|
|
35
35
|
const user = await database
|
|
36
|
-
.select('directus_users.id', 'directus_users.role')
|
|
36
|
+
.select('directus_users.id', 'directus_users.role', 'directus_roles.admin_access', 'directus_roles.app_access')
|
|
37
37
|
.from('directus_users')
|
|
38
|
+
.leftJoin('directus_roles', 'directus_users.role', 'directus_roles.id')
|
|
38
39
|
.where({
|
|
39
40
|
'directus_users.token': token,
|
|
40
41
|
status: 'active',
|
|
@@ -45,10 +46,8 @@ export async function getAccountabilityForToken(token, accountability) {
|
|
|
45
46
|
}
|
|
46
47
|
accountability.user = user.id;
|
|
47
48
|
accountability.role = user.role;
|
|
48
|
-
accountability.
|
|
49
|
-
|
|
50
|
-
accountability.admin = admin;
|
|
51
|
-
accountability.app = app;
|
|
49
|
+
accountability.admin = user.admin_access === true || user.admin_access == 1;
|
|
50
|
+
accountability.app = user.app_access === true || user.app_access == 1;
|
|
52
51
|
}
|
|
53
52
|
}
|
|
54
53
|
return accountability;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate an AST based on a given collection and query
|
|
3
|
+
*/
|
|
4
|
+
import type { Accountability, PermissionsAction, Query, SchemaOverview } from '@directus/types';
|
|
5
|
+
import type { Knex } from 'knex';
|
|
6
|
+
import type { AST } from '../types/index.js';
|
|
7
|
+
type GetASTOptions = {
|
|
8
|
+
accountability?: Accountability | null;
|
|
9
|
+
action?: PermissionsAction;
|
|
10
|
+
knex?: Knex;
|
|
11
|
+
};
|
|
12
|
+
export default function getASTFromQuery(collection: string, query: Query, schema: SchemaOverview, options?: GetASTOptions): Promise<AST>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate an AST based on a given collection and query
|
|
3
|
+
*/
|
|
4
|
+
import { REGEX_BETWEEN_PARENS } from '@directus/constants';
|
|
5
|
+
import { cloneDeep, isEmpty, mapKeys, omitBy, uniq } from 'lodash-es';
|
|
6
|
+
import { getRelationType } from './get-relation-type.js';
|
|
7
|
+
export default async function getASTFromQuery(collection, query, schema, options) {
|
|
8
|
+
query = cloneDeep(query);
|
|
9
|
+
const accountability = options?.accountability;
|
|
10
|
+
const action = options?.action || 'read';
|
|
11
|
+
const permissions = accountability && accountability.admin !== true
|
|
12
|
+
? accountability?.permissions?.filter((permission) => {
|
|
13
|
+
return permission.action === action;
|
|
14
|
+
}) ?? []
|
|
15
|
+
: null;
|
|
16
|
+
const ast = {
|
|
17
|
+
type: 'root',
|
|
18
|
+
name: collection,
|
|
19
|
+
query: query,
|
|
20
|
+
children: [],
|
|
21
|
+
};
|
|
22
|
+
let fields = ['*'];
|
|
23
|
+
if (query.fields) {
|
|
24
|
+
fields = query.fields;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* When using aggregate functions, you can't have any other regular fields
|
|
28
|
+
* selected. This makes sure you never end up in a non-aggregate fields selection error
|
|
29
|
+
*/
|
|
30
|
+
if (Object.keys(query.aggregate || {}).length > 0) {
|
|
31
|
+
fields = [];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Similarly, when grouping on a specific field, you can't have other non-aggregated fields.
|
|
35
|
+
* The group query will override the fields query
|
|
36
|
+
*/
|
|
37
|
+
if (query.group) {
|
|
38
|
+
fields = query.group;
|
|
39
|
+
}
|
|
40
|
+
fields = uniq(fields);
|
|
41
|
+
const deep = query.deep || {};
|
|
42
|
+
// Prevent fields/deep from showing up in the query object in further use
|
|
43
|
+
delete query.fields;
|
|
44
|
+
delete query.deep;
|
|
45
|
+
if (!query.sort) {
|
|
46
|
+
// We'll default to the primary key for the standard sort output
|
|
47
|
+
let sortField = schema.collections[collection].primary;
|
|
48
|
+
// If a custom manual sort field is configured, use that
|
|
49
|
+
if (schema.collections[collection]?.sortField) {
|
|
50
|
+
sortField = schema.collections[collection].sortField;
|
|
51
|
+
}
|
|
52
|
+
// When group by is used, default to the first column provided in the group by clause
|
|
53
|
+
if (query.group?.[0]) {
|
|
54
|
+
sortField = query.group[0];
|
|
55
|
+
}
|
|
56
|
+
query.sort = [sortField];
|
|
57
|
+
}
|
|
58
|
+
// When no group by is supplied, but an aggregate function is used, only a single row will be
|
|
59
|
+
// returned. In those cases, we'll ignore the sort field altogether
|
|
60
|
+
if (query.aggregate && Object.keys(query.aggregate).length && !query.group?.[0]) {
|
|
61
|
+
delete query.sort;
|
|
62
|
+
}
|
|
63
|
+
ast.children = await parseFields(collection, fields, deep);
|
|
64
|
+
return ast;
|
|
65
|
+
async function parseFields(parentCollection, fields, deep) {
|
|
66
|
+
if (!fields)
|
|
67
|
+
return [];
|
|
68
|
+
fields = await convertWildcards(parentCollection, fields);
|
|
69
|
+
if (!fields || !Array.isArray(fields))
|
|
70
|
+
return [];
|
|
71
|
+
const children = [];
|
|
72
|
+
const relationalStructure = Object.create(null);
|
|
73
|
+
for (const fieldKey of fields) {
|
|
74
|
+
let name = fieldKey;
|
|
75
|
+
if (query.alias) {
|
|
76
|
+
// check for field alias (is one of the key)
|
|
77
|
+
if (name in query.alias) {
|
|
78
|
+
name = query.alias[fieldKey];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const isRelational = name.includes('.') ||
|
|
82
|
+
// We'll always treat top level o2m fields as a related item. This is an alias field, otherwise it won't return
|
|
83
|
+
// anything
|
|
84
|
+
!!schema.relations.find((relation) => relation.related_collection === parentCollection && relation.meta?.one_field === name);
|
|
85
|
+
if (isRelational) {
|
|
86
|
+
// field is relational
|
|
87
|
+
const parts = fieldKey.split('.');
|
|
88
|
+
let rootField = parts[0];
|
|
89
|
+
let collectionScope = null;
|
|
90
|
+
// a2o related collection scoped field selector `fields=sections.section_id:headings.title`
|
|
91
|
+
if (rootField.includes(':')) {
|
|
92
|
+
const [key, scope] = rootField.split(':');
|
|
93
|
+
rootField = key;
|
|
94
|
+
collectionScope = scope;
|
|
95
|
+
}
|
|
96
|
+
if (rootField in relationalStructure === false) {
|
|
97
|
+
if (collectionScope) {
|
|
98
|
+
relationalStructure[rootField] = { [collectionScope]: [] };
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
relationalStructure[rootField] = [];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (parts.length > 1) {
|
|
105
|
+
const childKey = parts.slice(1).join('.');
|
|
106
|
+
if (collectionScope) {
|
|
107
|
+
if (collectionScope in relationalStructure[rootField] === false) {
|
|
108
|
+
relationalStructure[rootField][collectionScope] = [];
|
|
109
|
+
}
|
|
110
|
+
relationalStructure[rootField][collectionScope].push(childKey);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
relationalStructure[rootField].push(childKey);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
if (fieldKey.includes('(') && fieldKey.includes(')')) {
|
|
119
|
+
const columnName = fieldKey.match(REGEX_BETWEEN_PARENS)[1];
|
|
120
|
+
const foundField = schema.collections[parentCollection].fields[columnName];
|
|
121
|
+
if (foundField && foundField.type === 'alias') {
|
|
122
|
+
const foundRelation = schema.relations.find((relation) => relation.related_collection === parentCollection && relation.meta?.one_field === columnName);
|
|
123
|
+
if (foundRelation) {
|
|
124
|
+
children.push({
|
|
125
|
+
type: 'functionField',
|
|
126
|
+
name,
|
|
127
|
+
fieldKey,
|
|
128
|
+
query: {},
|
|
129
|
+
relatedCollection: foundRelation.collection,
|
|
130
|
+
});
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
children.push({ type: 'field', name, fieldKey });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
for (const [fieldKey, nestedFields] of Object.entries(relationalStructure)) {
|
|
139
|
+
let fieldName = fieldKey;
|
|
140
|
+
if (query.alias && fieldKey in query.alias) {
|
|
141
|
+
fieldName = query.alias[fieldKey];
|
|
142
|
+
}
|
|
143
|
+
const relatedCollection = getRelatedCollection(parentCollection, fieldName);
|
|
144
|
+
const relation = getRelation(parentCollection, fieldName);
|
|
145
|
+
if (!relation)
|
|
146
|
+
continue;
|
|
147
|
+
const relationType = getRelationType({
|
|
148
|
+
relation,
|
|
149
|
+
collection: parentCollection,
|
|
150
|
+
field: fieldName,
|
|
151
|
+
});
|
|
152
|
+
if (!relationType)
|
|
153
|
+
continue;
|
|
154
|
+
let child = null;
|
|
155
|
+
if (relationType === 'a2o') {
|
|
156
|
+
const allowedCollections = relation.meta.one_allowed_collections.filter((collection) => {
|
|
157
|
+
if (!permissions)
|
|
158
|
+
return true;
|
|
159
|
+
return permissions.some((permission) => permission.collection === collection);
|
|
160
|
+
});
|
|
161
|
+
child = {
|
|
162
|
+
type: 'a2o',
|
|
163
|
+
names: allowedCollections,
|
|
164
|
+
children: {},
|
|
165
|
+
query: {},
|
|
166
|
+
relatedKey: {},
|
|
167
|
+
parentKey: schema.collections[parentCollection].primary,
|
|
168
|
+
fieldKey: fieldKey,
|
|
169
|
+
relation: relation,
|
|
170
|
+
};
|
|
171
|
+
for (const relatedCollection of allowedCollections) {
|
|
172
|
+
child.children[relatedCollection] = await parseFields(relatedCollection, Array.isArray(nestedFields) ? nestedFields : nestedFields[relatedCollection] || [], deep?.[`${fieldKey}:${relatedCollection}`]);
|
|
173
|
+
child.query[relatedCollection] = getDeepQuery(deep?.[`${fieldKey}:${relatedCollection}`] || {});
|
|
174
|
+
child.relatedKey[relatedCollection] = schema.collections[relatedCollection].primary;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else if (relatedCollection) {
|
|
178
|
+
if (permissions && permissions.some((permission) => permission.collection === relatedCollection) === false) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
// update query alias for children parseFields
|
|
182
|
+
const deepAlias = getDeepQuery(deep?.[fieldKey] || {})?.['alias'];
|
|
183
|
+
if (!isEmpty(deepAlias))
|
|
184
|
+
query.alias = deepAlias;
|
|
185
|
+
child = {
|
|
186
|
+
type: relationType,
|
|
187
|
+
name: relatedCollection,
|
|
188
|
+
fieldKey: fieldKey,
|
|
189
|
+
parentKey: schema.collections[parentCollection].primary,
|
|
190
|
+
relatedKey: schema.collections[relatedCollection].primary,
|
|
191
|
+
relation: relation,
|
|
192
|
+
query: getDeepQuery(deep?.[fieldKey] || {}),
|
|
193
|
+
children: await parseFields(relatedCollection, nestedFields, deep?.[fieldKey] || {}),
|
|
194
|
+
};
|
|
195
|
+
if (relationType === 'o2m' && !child.query.sort) {
|
|
196
|
+
child.query.sort = [relation.meta?.sort_field || schema.collections[relation.collection].primary];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (child) {
|
|
200
|
+
children.push(child);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Deduplicate any children fields that are included both as a regular field, and as a nested m2o field
|
|
204
|
+
const nestedCollectionNodes = children.filter((childNode) => childNode.type !== 'field');
|
|
205
|
+
return children.filter((childNode) => {
|
|
206
|
+
const existsAsNestedRelational = !!nestedCollectionNodes.find((nestedCollectionNode) => childNode.fieldKey === nestedCollectionNode.fieldKey);
|
|
207
|
+
if (childNode.type === 'field' && existsAsNestedRelational)
|
|
208
|
+
return false;
|
|
209
|
+
return true;
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async function convertWildcards(parentCollection, fields) {
|
|
213
|
+
fields = cloneDeep(fields);
|
|
214
|
+
const fieldsInCollection = Object.entries(schema.collections[parentCollection].fields).map(([name]) => name);
|
|
215
|
+
let allowedFields = fieldsInCollection;
|
|
216
|
+
if (permissions) {
|
|
217
|
+
const permittedFields = permissions.find((permission) => parentCollection === permission.collection)?.fields;
|
|
218
|
+
if (permittedFields !== undefined)
|
|
219
|
+
allowedFields = permittedFields;
|
|
220
|
+
}
|
|
221
|
+
if (!allowedFields || allowedFields.length === 0)
|
|
222
|
+
return [];
|
|
223
|
+
// In case of full read permissions
|
|
224
|
+
if (allowedFields[0] === '*')
|
|
225
|
+
allowedFields = fieldsInCollection;
|
|
226
|
+
for (let index = 0; index < fields.length; index++) {
|
|
227
|
+
const fieldKey = fields[index];
|
|
228
|
+
if (fieldKey.includes('*') === false)
|
|
229
|
+
continue;
|
|
230
|
+
if (fieldKey === '*') {
|
|
231
|
+
const aliases = Object.keys(query.alias ?? {});
|
|
232
|
+
// Set to all fields in collection
|
|
233
|
+
if (allowedFields.includes('*')) {
|
|
234
|
+
fields.splice(index, 1, ...fieldsInCollection, ...aliases);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
// Set to all allowed fields
|
|
238
|
+
const allowedAliases = aliases.filter((fieldKey) => {
|
|
239
|
+
const name = query.alias[fieldKey];
|
|
240
|
+
return allowedFields.includes(name);
|
|
241
|
+
});
|
|
242
|
+
fields.splice(index, 1, ...allowedFields, ...allowedAliases);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Swap *.* case for *,<relational-field>.*,<another-relational>.*
|
|
246
|
+
if (fieldKey.includes('.') && fieldKey.split('.')[0] === '*') {
|
|
247
|
+
const parts = fieldKey.split('.');
|
|
248
|
+
const relationalFields = allowedFields.includes('*')
|
|
249
|
+
? schema.relations
|
|
250
|
+
.filter((relation) => relation.collection === parentCollection || relation.related_collection === parentCollection)
|
|
251
|
+
.map((relation) => {
|
|
252
|
+
const isMany = relation.collection === parentCollection;
|
|
253
|
+
return isMany ? relation.field : relation.meta?.one_field;
|
|
254
|
+
})
|
|
255
|
+
: allowedFields.filter((fieldKey) => !!getRelation(parentCollection, fieldKey));
|
|
256
|
+
const nonRelationalFields = allowedFields.filter((fieldKey) => relationalFields.includes(fieldKey) === false);
|
|
257
|
+
const aliasFields = Object.keys(query.alias ?? {}).map((fieldKey) => {
|
|
258
|
+
const name = query.alias[fieldKey];
|
|
259
|
+
if (relationalFields.includes(name)) {
|
|
260
|
+
return `${fieldKey}.${parts.slice(1).join('.')}`;
|
|
261
|
+
}
|
|
262
|
+
return fieldKey;
|
|
263
|
+
});
|
|
264
|
+
fields.splice(index, 1, ...[
|
|
265
|
+
...relationalFields.map((relationalField) => {
|
|
266
|
+
return `${relationalField}.${parts.slice(1).join('.')}`;
|
|
267
|
+
}),
|
|
268
|
+
...nonRelationalFields,
|
|
269
|
+
...aliasFields,
|
|
270
|
+
]);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return fields;
|
|
274
|
+
}
|
|
275
|
+
function getRelation(collection, field) {
|
|
276
|
+
const relation = schema.relations.find((relation) => {
|
|
277
|
+
return ((relation.collection === collection && relation.field === field) ||
|
|
278
|
+
(relation.related_collection === collection && relation.meta?.one_field === field));
|
|
279
|
+
});
|
|
280
|
+
return relation;
|
|
281
|
+
}
|
|
282
|
+
function getRelatedCollection(collection, field) {
|
|
283
|
+
const relation = getRelation(collection, field);
|
|
284
|
+
if (!relation)
|
|
285
|
+
return null;
|
|
286
|
+
if (relation.collection === collection && relation.field === field) {
|
|
287
|
+
return relation.related_collection || null;
|
|
288
|
+
}
|
|
289
|
+
if (relation.related_collection === collection && relation.meta?.one_field === field) {
|
|
290
|
+
return relation.collection || null;
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function getDeepQuery(query) {
|
|
296
|
+
return mapKeys(omitBy(query, (_value, key) => key.startsWith('_') === false), (_value, key) => key.substring(1));
|
|
297
|
+
}
|
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
import hash from 'object-hash';
|
|
2
2
|
import url from 'url';
|
|
3
|
-
import getDatabase from '../database/index.js';
|
|
4
|
-
import { fetchPoliciesIpAccess } from '../permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.js';
|
|
5
3
|
import { getGraphqlQueryAndVariables } from './get-graphql-query-and-variables.js';
|
|
6
4
|
import { version } from 'directus/version';
|
|
7
|
-
|
|
8
|
-
export async function getCacheKey(req) {
|
|
5
|
+
export function getCacheKey(req) {
|
|
9
6
|
const path = url.parse(req.originalUrl).pathname;
|
|
10
7
|
const isGraphQl = path?.startsWith('/graphql');
|
|
11
|
-
let includeIp = false;
|
|
12
|
-
if (req.accountability && req.accountability.ip) {
|
|
13
|
-
// Check if the IP influences the result of the request, that can be the case if some policies have an ip_access
|
|
14
|
-
// filter and the request IP matches any of those filters
|
|
15
|
-
const ipFilters = await fetchPoliciesIpAccess(req.accountability, getDatabase());
|
|
16
|
-
includeIp = ipFilters.length > 0 && ipFilters.some((networks) => ipInNetworks(req.accountability.ip, networks));
|
|
17
|
-
}
|
|
18
8
|
const info = {
|
|
19
9
|
version,
|
|
20
10
|
user: req.accountability?.user || null,
|
|
21
11
|
path,
|
|
22
12
|
query: isGraphQl ? getGraphqlQueryAndVariables(req) : req.sanitizedQuery,
|
|
23
|
-
...(includeIp && { ip: req.accountability.ip }),
|
|
24
13
|
};
|
|
25
14
|
const key = hash(info);
|
|
26
15
|
return key;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Query, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
3
|
type GetColumnOptions = {
|
|
4
4
|
query?: Query | undefined;
|
|
5
|
-
cases?: Filter[];
|
|
6
5
|
originalCollectionName?: string | undefined;
|
|
7
6
|
};
|
|
8
7
|
/**
|
package/dist/utils/get-column.js
CHANGED
|
@@ -30,7 +30,6 @@ export function getColumn(knex, table, column, alias = applyFunctionToColumnName
|
|
|
30
30
|
const result = fn[functionName](table, columnName, {
|
|
31
31
|
type,
|
|
32
32
|
query: options?.query,
|
|
33
|
-
cases: options?.cases,
|
|
34
33
|
originalCollectionName: options?.originalCollectionName,
|
|
35
34
|
});
|
|
36
35
|
if (alias) {
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { deepMap, parseFilter, parseJSON, parsePreset } from '@directus/utils';
|
|
3
|
+
import { cloneDeep } from 'lodash-es';
|
|
4
|
+
import hash from 'object-hash';
|
|
5
|
+
import { getCache, getCacheValue, getSystemCache, setCacheValue, setSystemCache } from '../cache.js';
|
|
6
|
+
import getDatabase from '../database/index.js';
|
|
7
|
+
import { appAccessMinimalPermissions } from '@directus/system-data';
|
|
8
|
+
import { useLogger } from '../logger/index.js';
|
|
9
|
+
import { RolesService } from '../services/roles.js';
|
|
10
|
+
import { UsersService } from '../services/users.js';
|
|
11
|
+
import { mergePermissionsForShare } from './merge-permissions-for-share.js';
|
|
12
|
+
import { mergePermissions } from './merge-permissions.js';
|
|
13
|
+
export async function getPermissions(accountability, schema) {
|
|
14
|
+
const database = getDatabase();
|
|
15
|
+
const { cache } = getCache();
|
|
16
|
+
const env = useEnv();
|
|
17
|
+
const logger = useLogger();
|
|
18
|
+
let permissions = [];
|
|
19
|
+
const { user, role, app, admin, share_scope } = accountability;
|
|
20
|
+
const cacheKey = `permissions-${hash({ user, role, app, admin, share_scope })}`;
|
|
21
|
+
if (cache && env['CACHE_PERMISSIONS'] !== false) {
|
|
22
|
+
let cachedPermissions;
|
|
23
|
+
try {
|
|
24
|
+
cachedPermissions = await getSystemCache(cacheKey);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
logger.warn(err, `[cache] Couldn't read key ${cacheKey}. ${err.message}`);
|
|
28
|
+
}
|
|
29
|
+
if (cachedPermissions) {
|
|
30
|
+
if (!cachedPermissions['containDynamicData']) {
|
|
31
|
+
return processPermissions(accountability, cachedPermissions['permissions'], {});
|
|
32
|
+
}
|
|
33
|
+
const cachedFilterContext = await getCacheValue(cache, `filterContext-${hash({ user, role, permissions: cachedPermissions['permissions'] })}`);
|
|
34
|
+
if (cachedFilterContext) {
|
|
35
|
+
return processPermissions(accountability, cachedPermissions['permissions'], cachedFilterContext);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const { permissions: parsedPermissions, requiredPermissionData, containDynamicData, } = parsePermissions(cachedPermissions['permissions']);
|
|
39
|
+
permissions = parsedPermissions;
|
|
40
|
+
const filterContext = containDynamicData
|
|
41
|
+
? await getFilterContext(schema, accountability, requiredPermissionData)
|
|
42
|
+
: {};
|
|
43
|
+
if (containDynamicData && env['CACHE_ENABLED'] !== false) {
|
|
44
|
+
await setCacheValue(cache, `filterContext-${hash({ user, role, permissions })}`, filterContext);
|
|
45
|
+
}
|
|
46
|
+
return processPermissions(accountability, permissions, filterContext);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (accountability.admin !== true) {
|
|
51
|
+
const query = database.select('*').from('directus_permissions');
|
|
52
|
+
if (accountability.role) {
|
|
53
|
+
query.where({ role: accountability.role });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
query.whereNull('role');
|
|
57
|
+
}
|
|
58
|
+
const permissionsForRole = await query;
|
|
59
|
+
const { permissions: parsedPermissions, requiredPermissionData, containDynamicData, } = parsePermissions(permissionsForRole);
|
|
60
|
+
permissions = parsedPermissions;
|
|
61
|
+
if (accountability.app === true) {
|
|
62
|
+
permissions = mergePermissions('or', permissions, appAccessMinimalPermissions.map((perm) => ({ ...perm, role: accountability.role })));
|
|
63
|
+
}
|
|
64
|
+
if (accountability.share_scope) {
|
|
65
|
+
permissions = mergePermissionsForShare(permissions, accountability, schema);
|
|
66
|
+
}
|
|
67
|
+
const filterContext = containDynamicData
|
|
68
|
+
? await getFilterContext(schema, accountability, requiredPermissionData)
|
|
69
|
+
: {};
|
|
70
|
+
if (cache && env['CACHE_PERMISSIONS'] !== false) {
|
|
71
|
+
await setSystemCache(cacheKey, { permissions, containDynamicData });
|
|
72
|
+
if (containDynamicData && env['CACHE_ENABLED'] !== false) {
|
|
73
|
+
await setCacheValue(cache, `filterContext-${hash({ user, role, permissions })}`, filterContext);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return processPermissions(accountability, permissions, filterContext);
|
|
77
|
+
}
|
|
78
|
+
return permissions;
|
|
79
|
+
}
|
|
80
|
+
function parsePermissions(permissions) {
|
|
81
|
+
const requiredPermissionData = {
|
|
82
|
+
$CURRENT_USER: [],
|
|
83
|
+
$CURRENT_ROLE: [],
|
|
84
|
+
};
|
|
85
|
+
let containDynamicData = false;
|
|
86
|
+
permissions = permissions.map((permissionRaw) => {
|
|
87
|
+
const permission = cloneDeep(permissionRaw);
|
|
88
|
+
if (permission.permissions && typeof permission.permissions === 'string') {
|
|
89
|
+
permission.permissions = parseJSON(permission.permissions);
|
|
90
|
+
}
|
|
91
|
+
if (permission.validation && typeof permission.validation === 'string') {
|
|
92
|
+
permission.validation = parseJSON(permission.validation);
|
|
93
|
+
}
|
|
94
|
+
else if (permission.validation === null) {
|
|
95
|
+
permission.validation = {};
|
|
96
|
+
}
|
|
97
|
+
if (permission.presets && typeof permission.presets === 'string') {
|
|
98
|
+
permission.presets = parseJSON(permission.presets);
|
|
99
|
+
}
|
|
100
|
+
else if (permission.presets === null) {
|
|
101
|
+
permission.presets = {};
|
|
102
|
+
}
|
|
103
|
+
if (permission.fields && typeof permission.fields === 'string') {
|
|
104
|
+
permission.fields = permission.fields.split(',');
|
|
105
|
+
}
|
|
106
|
+
else if (permission.fields === null) {
|
|
107
|
+
permission.fields = [];
|
|
108
|
+
}
|
|
109
|
+
const extractPermissionData = (val) => {
|
|
110
|
+
if (typeof val === 'string' && val.startsWith('$CURRENT_USER.')) {
|
|
111
|
+
requiredPermissionData.$CURRENT_USER.push(val.replace('$CURRENT_USER.', ''));
|
|
112
|
+
containDynamicData = true;
|
|
113
|
+
}
|
|
114
|
+
if (typeof val === 'string' && val.startsWith('$CURRENT_ROLE.')) {
|
|
115
|
+
requiredPermissionData.$CURRENT_ROLE.push(val.replace('$CURRENT_ROLE.', ''));
|
|
116
|
+
containDynamicData = true;
|
|
117
|
+
}
|
|
118
|
+
return val;
|
|
119
|
+
};
|
|
120
|
+
deepMap(permission.permissions, extractPermissionData);
|
|
121
|
+
deepMap(permission.validation, extractPermissionData);
|
|
122
|
+
deepMap(permission.presets, extractPermissionData);
|
|
123
|
+
return permission;
|
|
124
|
+
});
|
|
125
|
+
return { permissions, requiredPermissionData, containDynamicData };
|
|
126
|
+
}
|
|
127
|
+
async function getFilterContext(schema, accountability, requiredPermissionData) {
|
|
128
|
+
const usersService = new UsersService({ schema });
|
|
129
|
+
const rolesService = new RolesService({ schema });
|
|
130
|
+
const filterContext = {};
|
|
131
|
+
if (accountability.user && requiredPermissionData.$CURRENT_USER.length > 0) {
|
|
132
|
+
filterContext['$CURRENT_USER'] = await usersService.readOne(accountability.user, {
|
|
133
|
+
fields: requiredPermissionData.$CURRENT_USER,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (accountability.role && requiredPermissionData.$CURRENT_ROLE.length > 0) {
|
|
137
|
+
filterContext['$CURRENT_ROLE'] = await rolesService.readOne(accountability.role, {
|
|
138
|
+
fields: requiredPermissionData.$CURRENT_ROLE,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return filterContext;
|
|
142
|
+
}
|
|
143
|
+
function processPermissions(accountability, permissions, filterContext) {
|
|
144
|
+
return permissions.map((permission) => {
|
|
145
|
+
permission.permissions = parseFilter(permission.permissions, accountability, filterContext);
|
|
146
|
+
permission.validation = parseFilter(permission.validation, accountability, filterContext);
|
|
147
|
+
permission.presets = parsePreset(permission.presets, accountability, filterContext);
|
|
148
|
+
return permission;
|
|
149
|
+
});
|
|
150
|
+
}
|
package/dist/utils/get-schema.js
CHANGED
|
@@ -17,9 +17,6 @@ const logger = useLogger();
|
|
|
17
17
|
export async function getSchema(options, attempt = 0) {
|
|
18
18
|
const MAX_ATTEMPTS = 3;
|
|
19
19
|
const env = useEnv();
|
|
20
|
-
if (attempt >= MAX_ATTEMPTS) {
|
|
21
|
-
throw new Error(`Failed to get Schema information: hit infinite loop`);
|
|
22
|
-
}
|
|
23
20
|
if (options?.bypassCache || env['CACHE_SCHEMA'] === false) {
|
|
24
21
|
const database = options?.database || getDatabase();
|
|
25
22
|
const schemaInspector = createInspector(database);
|
|
@@ -29,6 +26,9 @@ export async function getSchema(options, attempt = 0) {
|
|
|
29
26
|
if (cached) {
|
|
30
27
|
return cached;
|
|
31
28
|
}
|
|
29
|
+
if (attempt >= MAX_ATTEMPTS) {
|
|
30
|
+
throw new Error(`Failed to get Schema information: hit infinite loop`);
|
|
31
|
+
}
|
|
32
32
|
const lock = useLock();
|
|
33
33
|
const bus = useBus();
|
|
34
34
|
const lockKey = 'schemaCache--preparing';
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { ForbiddenError } from '@directus/errors';
|
|
2
|
-
import {
|
|
2
|
+
import { ActivityService, DashboardsService, FilesService, FlowsService, FoldersService, ItemsService, NotificationsService, OperationsService, PanelsService, PermissionsService, PresetsService, RevisionsService, RolesService, SettingsService, SharesService, TranslationsService, UsersService, VersionsService, WebhooksService, } from '../services/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* Select the correct service for the given collection. This allows the individual services to run
|
|
5
5
|
* their custom checks (f.e. it allows `UsersService` to prevent updating TFA secret from outside).
|
|
6
6
|
*/
|
|
7
7
|
export function getService(collection, opts) {
|
|
8
8
|
switch (collection) {
|
|
9
|
-
case 'directus_access':
|
|
10
|
-
return new AccessService(opts);
|
|
11
9
|
case 'directus_activity':
|
|
12
10
|
return new ActivityService(opts);
|
|
13
11
|
case 'directus_dashboards':
|
|
@@ -28,8 +26,6 @@ export function getService(collection, opts) {
|
|
|
28
26
|
return new PermissionsService(opts);
|
|
29
27
|
case 'directus_presets':
|
|
30
28
|
return new PresetsService(opts);
|
|
31
|
-
case 'directus_policies':
|
|
32
|
-
return new PoliciesService(opts);
|
|
33
29
|
case 'directus_revisions':
|
|
34
30
|
return new RevisionsService(opts);
|
|
35
31
|
case 'directus_roles':
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Accountability, Filter, Permission, SchemaOverview } from '@directus/types';
|
|
2
|
+
export declare function mergePermissionsForShare(currentPermissions: Permission[], accountability: Accountability, schema: SchemaOverview): Permission[];
|
|
3
|
+
export declare function traverse(schema: SchemaOverview, rootItemPrimaryKeyField: string, rootItemPrimaryKey: string, currentCollection: string, parentCollections?: string[], path?: string[]): Partial<Permission>[];
|
|
4
|
+
export declare function getFilterForPath(type: 'o2m' | 'm2o' | 'a2o', path: string[], rootPrimaryKeyField: string, rootPrimaryKey: string): Filter;
|