@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,17 +1,14 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import formatTitle from '@directus/format-title';
|
|
3
3
|
import { spec } from '@directus/specs';
|
|
4
|
-
import { isSystemCollection } from '@directus/system-data';
|
|
5
4
|
import { version } from 'directus/version';
|
|
6
5
|
import { cloneDeep, mergeWith } from 'lodash-es';
|
|
7
6
|
import { OAS_REQUIRED_SCHEMAS } from '../constants.js';
|
|
8
7
|
import getDatabase from '../database/index.js';
|
|
9
|
-
import { fetchPermissions } from '../permissions/lib/fetch-permissions.js';
|
|
10
|
-
import { fetchPolicies } from '../permissions/lib/fetch-policies.js';
|
|
11
|
-
import { fetchAllowedFieldMap } from '../permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js';
|
|
12
8
|
import { getRelationType } from '../utils/get-relation-type.js';
|
|
13
9
|
import { reduceSchema } from '../utils/reduce-schema.js';
|
|
14
10
|
import { GraphQLService } from './graphql/index.js';
|
|
11
|
+
import { isSystemCollection } from '@directus/system-data';
|
|
15
12
|
const env = useEnv();
|
|
16
13
|
export class SpecificationService {
|
|
17
14
|
accountability;
|
|
@@ -19,38 +16,29 @@ export class SpecificationService {
|
|
|
19
16
|
schema;
|
|
20
17
|
oas;
|
|
21
18
|
graphql;
|
|
22
|
-
constructor(
|
|
23
|
-
this.accountability =
|
|
24
|
-
this.knex =
|
|
25
|
-
this.schema =
|
|
26
|
-
this.oas = new OASSpecsService(
|
|
27
|
-
this.graphql = new GraphQLSpecsService(
|
|
19
|
+
constructor({ accountability, knex, schema }) {
|
|
20
|
+
this.accountability = accountability || null;
|
|
21
|
+
this.knex = knex || getDatabase();
|
|
22
|
+
this.schema = schema;
|
|
23
|
+
this.oas = new OASSpecsService({ knex, schema, accountability });
|
|
24
|
+
this.graphql = new GraphQLSpecsService({ knex, schema, accountability });
|
|
28
25
|
}
|
|
29
26
|
}
|
|
30
27
|
class OASSpecsService {
|
|
31
28
|
accountability;
|
|
32
29
|
knex;
|
|
33
30
|
schema;
|
|
34
|
-
constructor(
|
|
35
|
-
this.accountability =
|
|
36
|
-
this.knex =
|
|
37
|
-
this.schema =
|
|
31
|
+
constructor({ knex, schema, accountability }) {
|
|
32
|
+
this.accountability = accountability || null;
|
|
33
|
+
this.knex = knex || getDatabase();
|
|
34
|
+
this.schema =
|
|
35
|
+
this.accountability?.admin === true ? schema : reduceSchema(schema, accountability?.permissions || null);
|
|
38
36
|
}
|
|
39
37
|
async generate(host) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (this.accountability && this.accountability.admin !== true) {
|
|
43
|
-
const allowedFields = await fetchAllowedFieldMap({
|
|
44
|
-
accountability: this.accountability,
|
|
45
|
-
action: 'read',
|
|
46
|
-
}, { schema, knex: this.knex });
|
|
47
|
-
schema = reduceSchema(schema, allowedFields);
|
|
48
|
-
const policies = await fetchPolicies(this.accountability, { schema, knex: this.knex });
|
|
49
|
-
permissions = await fetchPermissions({ action: 'read', policies, accountability: this.accountability }, { schema, knex: this.knex });
|
|
50
|
-
}
|
|
51
|
-
const tags = await this.generateTags(schema);
|
|
38
|
+
const permissions = this.accountability?.permissions ?? [];
|
|
39
|
+
const tags = await this.generateTags();
|
|
52
40
|
const paths = await this.generatePaths(permissions, tags);
|
|
53
|
-
const components = await this.generateComponents(
|
|
41
|
+
const components = await this.generateComponents(tags);
|
|
54
42
|
const isDefaultPublicUrl = env['PUBLIC_URL'] === '/';
|
|
55
43
|
const url = isDefaultPublicUrl && host ? host : env['PUBLIC_URL'];
|
|
56
44
|
const spec = {
|
|
@@ -74,9 +62,9 @@ class OASSpecsService {
|
|
|
74
62
|
spec.components = components;
|
|
75
63
|
return spec;
|
|
76
64
|
}
|
|
77
|
-
async generateTags(
|
|
65
|
+
async generateTags() {
|
|
78
66
|
const systemTags = cloneDeep(spec.tags);
|
|
79
|
-
const collections = Object.values(schema.collections);
|
|
67
|
+
const collections = Object.values(this.schema.collections);
|
|
80
68
|
const tags = [];
|
|
81
69
|
for (const systemTag of systemTags) {
|
|
82
70
|
// Check if necessary authentication level is given
|
|
@@ -258,7 +246,7 @@ class OASSpecsService {
|
|
|
258
246
|
}
|
|
259
247
|
return paths;
|
|
260
248
|
}
|
|
261
|
-
async generateComponents(
|
|
249
|
+
async generateComponents(tags) {
|
|
262
250
|
if (!tags)
|
|
263
251
|
return;
|
|
264
252
|
let components = cloneDeep(spec.components);
|
|
@@ -276,7 +264,7 @@ class OASSpecsService {
|
|
|
276
264
|
};
|
|
277
265
|
}
|
|
278
266
|
}
|
|
279
|
-
const collections = Object.values(schema.collections);
|
|
267
|
+
const collections = Object.values(this.schema.collections);
|
|
280
268
|
for (const collection of collections) {
|
|
281
269
|
const tag = tags.find((tag) => tag['x-collection'] === collection.collection);
|
|
282
270
|
if (!tag)
|
|
@@ -289,7 +277,7 @@ class OASSpecsService {
|
|
|
289
277
|
schemaComponent['x-collection'] = collection.collection;
|
|
290
278
|
for (const field of fieldsInCollection) {
|
|
291
279
|
schemaComponent.properties[field.field] =
|
|
292
|
-
cloneDeep(spec.components.schemas[tag.name].properties[field.field]) || this.generateField(
|
|
280
|
+
cloneDeep(spec.components.schemas[tag.name].properties[field.field]) || this.generateField(collection.collection, field, tags);
|
|
293
281
|
}
|
|
294
282
|
components.schemas[tag.name] = schemaComponent;
|
|
295
283
|
}
|
|
@@ -300,7 +288,7 @@ class OASSpecsService {
|
|
|
300
288
|
'x-collection': collection.collection,
|
|
301
289
|
};
|
|
302
290
|
for (const field of fieldsInCollection) {
|
|
303
|
-
schemaComponent.properties[field.field] = this.generateField(
|
|
291
|
+
schemaComponent.properties[field.field] = this.generateField(collection.collection, field, tags);
|
|
304
292
|
}
|
|
305
293
|
components.schemas[tag.name] = schemaComponent;
|
|
306
294
|
}
|
|
@@ -323,13 +311,13 @@ class OASSpecsService {
|
|
|
323
311
|
return 'read';
|
|
324
312
|
}
|
|
325
313
|
}
|
|
326
|
-
generateField(
|
|
314
|
+
generateField(collection, field, tags) {
|
|
327
315
|
let propertyObject = {};
|
|
328
316
|
propertyObject.nullable = field.nullable;
|
|
329
317
|
if (field.note) {
|
|
330
318
|
propertyObject.description = field.note;
|
|
331
319
|
}
|
|
332
|
-
const relation = schema.relations.find((relation) => (relation.collection === collection && relation.field === field.field) ||
|
|
320
|
+
const relation = this.schema.relations.find((relation) => (relation.collection === collection && relation.field === field.field) ||
|
|
333
321
|
(relation.related_collection === collection && relation.meta?.one_field === field.field));
|
|
334
322
|
if (!relation) {
|
|
335
323
|
propertyObject = {
|
|
@@ -347,10 +335,10 @@ class OASSpecsService {
|
|
|
347
335
|
const relatedTag = tags.find((tag) => tag['x-collection'] === relation.related_collection);
|
|
348
336
|
if (!relatedTag ||
|
|
349
337
|
!relation.related_collection ||
|
|
350
|
-
relation.related_collection in schema.collections === false) {
|
|
338
|
+
relation.related_collection in this.schema.collections === false) {
|
|
351
339
|
return propertyObject;
|
|
352
340
|
}
|
|
353
|
-
const relatedCollection = schema.collections[relation.related_collection];
|
|
341
|
+
const relatedCollection = this.schema.collections[relation.related_collection];
|
|
354
342
|
const relatedPrimaryKeyField = relatedCollection.fields[relatedCollection.primary];
|
|
355
343
|
propertyObject.oneOf = [
|
|
356
344
|
{
|
|
@@ -363,10 +351,10 @@ class OASSpecsService {
|
|
|
363
351
|
}
|
|
364
352
|
else if (relationType === 'o2m') {
|
|
365
353
|
const relatedTag = tags.find((tag) => tag['x-collection'] === relation.collection);
|
|
366
|
-
if (!relatedTag || !relation.related_collection || relation.collection in schema.collections === false) {
|
|
354
|
+
if (!relatedTag || !relation.related_collection || relation.collection in this.schema.collections === false) {
|
|
367
355
|
return propertyObject;
|
|
368
356
|
}
|
|
369
|
-
const relatedCollection = schema.collections[relation.collection];
|
|
357
|
+
const relatedCollection = this.schema.collections[relation.collection];
|
|
370
358
|
const relatedPrimaryKeyField = relatedCollection.fields[relatedCollection.primary];
|
|
371
359
|
if (!relatedTag || !relatedPrimaryKeyField)
|
|
372
360
|
return propertyObject;
|
|
@@ -79,16 +79,15 @@ export class TusDataStore extends DataStore {
|
|
|
79
79
|
}
|
|
80
80
|
// If this is a new file upload, we need to generate a new primary key and DB record
|
|
81
81
|
const primaryKey = await itemsService.createOne(fileData, { emitEvents: false });
|
|
82
|
+
// Set the file id, so it is available to be sent as a header on upload creation / resume
|
|
83
|
+
if (!upload.metadata['id']) {
|
|
84
|
+
upload.metadata['id'] = primaryKey;
|
|
85
|
+
}
|
|
82
86
|
const fileExtension = extname(upload.metadata['filename_download']) ||
|
|
83
87
|
(upload.metadata['type'] && '.' + extension(upload.metadata['type'])) ||
|
|
84
88
|
'';
|
|
85
89
|
// The filename_disk is the FINAL filename on disk
|
|
86
90
|
fileData.filename_disk ||= primaryKey + (fileExtension || '');
|
|
87
|
-
// Temp filename is used for replacements
|
|
88
|
-
// const tempFilenameDisk = fileData.tus_id! + (fileExtension || '');
|
|
89
|
-
// if (isReplacement) {
|
|
90
|
-
// upload.metadata['temp_file'] = tempFilenameDisk;
|
|
91
|
-
// }
|
|
92
91
|
try {
|
|
93
92
|
// If this is a replacement, we'll write the file to a temp location first to ensure we don't overwrite the existing file if something goes wrong
|
|
94
93
|
upload = (await this.storageDriver.createChunkedUpload(fileData.filename_disk, upload));
|
|
@@ -4,5 +4,5 @@ type Context = {
|
|
|
4
4
|
schema: SchemaOverview;
|
|
5
5
|
accountability?: Accountability | undefined;
|
|
6
6
|
};
|
|
7
|
-
export declare function createTusServer(context: Context): Promise<Server>;
|
|
7
|
+
export declare function createTusServer(context: Context): Promise<[Server, () => void]>;
|
|
8
8
|
export {};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { useEnv } from '@directus/env';
|
|
7
7
|
import { supportsTus } from '@directus/storage';
|
|
8
8
|
import { toArray } from '@directus/utils';
|
|
9
|
-
import { Server } from '@tus/server';
|
|
9
|
+
import { Server, EVENTS } from '@tus/server';
|
|
10
10
|
import { RESUMABLE_UPLOADS } from '../../constants.js';
|
|
11
11
|
import { getStorage } from '../../storage/index.js';
|
|
12
12
|
import { extractMetadata } from '../files/lib/extract-metadata.js';
|
|
@@ -33,7 +33,7 @@ async function createTusStore(context) {
|
|
|
33
33
|
export async function createTusServer(context) {
|
|
34
34
|
const env = useEnv();
|
|
35
35
|
const store = await createTusStore(context);
|
|
36
|
-
|
|
36
|
+
const server = new Server({
|
|
37
37
|
path: '/files/tus',
|
|
38
38
|
datastore: store,
|
|
39
39
|
locker: getTusLocker(),
|
|
@@ -77,4 +77,11 @@ export async function createTusServer(context) {
|
|
|
77
77
|
},
|
|
78
78
|
relativeLocation: String(env['PUBLIC_URL']).startsWith('http'),
|
|
79
79
|
});
|
|
80
|
+
server.on(EVENTS.POST_CREATE, async (_req, res, upload) => {
|
|
81
|
+
res.setHeader('Directus-File-Id', upload.metadata['id']);
|
|
82
|
+
});
|
|
83
|
+
return [server, cleanup];
|
|
84
|
+
function cleanup() {
|
|
85
|
+
server.removeAllListeners();
|
|
86
|
+
}
|
|
80
87
|
}
|
package/dist/services/users.d.ts
CHANGED
|
@@ -13,6 +13,11 @@ export declare class UsersService extends ItemsService {
|
|
|
13
13
|
* directus_settings.auth_password_policy
|
|
14
14
|
*/
|
|
15
15
|
private checkPasswordPolicy;
|
|
16
|
+
private checkRemainingAdminExistence;
|
|
17
|
+
/**
|
|
18
|
+
* Make sure there's at least one active admin user when updating user status
|
|
19
|
+
*/
|
|
20
|
+
private checkRemainingActiveAdmin;
|
|
16
21
|
/**
|
|
17
22
|
* Get basic information of user identified by email
|
|
18
23
|
*/
|
|
@@ -47,5 +52,4 @@ export declare class UsersService extends ItemsService {
|
|
|
47
52
|
verifyRegistration(token: string): Promise<string>;
|
|
48
53
|
requestPasswordReset(email: string, url: string | null, subject?: string | null): Promise<void>;
|
|
49
54
|
resetPassword(token: string, password: string): Promise<void>;
|
|
50
|
-
private clearCaches;
|
|
51
55
|
}
|
package/dist/services/users.js
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
|
-
import { ForbiddenError, InvalidPayloadError, RecordNotUniqueError } from '@directus/errors';
|
|
3
|
-
import { getSimpleHash, toArray, validatePayload } from '@directus/utils';
|
|
2
|
+
import { ForbiddenError, InvalidPayloadError, RecordNotUniqueError, UnprocessableContentError } from '@directus/errors';
|
|
3
|
+
import { getSimpleHash, toArray, toBoolean, validatePayload } from '@directus/utils';
|
|
4
4
|
import { FailedValidationError, joiValidationErrorItemToErrorExtensions } from '@directus/validation';
|
|
5
5
|
import Joi from 'joi';
|
|
6
6
|
import jwt from 'jsonwebtoken';
|
|
7
|
-
import { isEmpty } from 'lodash-es';
|
|
7
|
+
import { isEmpty, mergeWith } from 'lodash-es';
|
|
8
8
|
import { performance } from 'perf_hooks';
|
|
9
|
-
import { clearSystemCache } from '../cache.js';
|
|
10
9
|
import getDatabase from '../database/index.js';
|
|
11
10
|
import { useLogger } from '../logger/index.js';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { checkIncreasedUserLimits } from '../telemetry/utils/check-increased-user-limits.js';
|
|
12
|
+
import { getRoleCountsByRoles } from '../telemetry/utils/get-role-counts-by-roles.js';
|
|
13
|
+
import { getRoleCountsByUsers } from '../telemetry/utils/get-role-counts-by-users.js';
|
|
14
|
+
import {} from '../telemetry/utils/get-user-count.js';
|
|
15
|
+
import { shouldCheckUserLimits } from '../telemetry/utils/should-check-user-limits.js';
|
|
14
16
|
import { getSecret } from '../utils/get-secret.js';
|
|
15
17
|
import isUrlAllowed from '../utils/is-url-allowed.js';
|
|
16
18
|
import { verifyJWT } from '../utils/jwt.js';
|
|
17
19
|
import { stall } from '../utils/stall.js';
|
|
18
20
|
import { Url } from '../utils/url.js';
|
|
19
|
-
import { UserIntegrityCheckFlag } from '../utils/validate-user-count-integrity.js';
|
|
20
21
|
import { ItemsService } from './items.js';
|
|
21
22
|
import { MailService } from './mail/index.js';
|
|
22
23
|
import { SettingsService } from './settings.js';
|
|
@@ -87,11 +88,42 @@ export class UsersService extends ItemsService {
|
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
}
|
|
91
|
+
async checkRemainingAdminExistence(excludeKeys) {
|
|
92
|
+
// Make sure there's at least one admin user left after this deletion is done
|
|
93
|
+
const otherAdminUsers = await this.knex
|
|
94
|
+
.count('*', { as: 'count' })
|
|
95
|
+
.from('directus_users')
|
|
96
|
+
.whereNotIn('directus_users.id', excludeKeys)
|
|
97
|
+
.andWhere({ 'directus_roles.admin_access': true })
|
|
98
|
+
.leftJoin('directus_roles', 'directus_users.role', 'directus_roles.id')
|
|
99
|
+
.first();
|
|
100
|
+
const otherAdminUsersCount = +(otherAdminUsers?.count || 0);
|
|
101
|
+
if (otherAdminUsersCount === 0) {
|
|
102
|
+
throw new UnprocessableContentError({ reason: `You can't remove the last admin user from the role` });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Make sure there's at least one active admin user when updating user status
|
|
107
|
+
*/
|
|
108
|
+
async checkRemainingActiveAdmin(excludeKeys) {
|
|
109
|
+
const otherAdminUsers = await this.knex
|
|
110
|
+
.count('*', { as: 'count' })
|
|
111
|
+
.from('directus_users')
|
|
112
|
+
.whereNotIn('directus_users.id', excludeKeys)
|
|
113
|
+
.andWhere({ 'directus_roles.admin_access': true })
|
|
114
|
+
.andWhere({ 'directus_users.status': 'active' })
|
|
115
|
+
.leftJoin('directus_roles', 'directus_users.role', 'directus_roles.id')
|
|
116
|
+
.first();
|
|
117
|
+
const otherAdminUsersCount = +(otherAdminUsers?.count || 0);
|
|
118
|
+
if (otherAdminUsersCount === 0) {
|
|
119
|
+
throw new UnprocessableContentError({ reason: `You can't change the active status of the last admin user` });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
90
122
|
/**
|
|
91
123
|
* Get basic information of user identified by email
|
|
92
124
|
*/
|
|
93
125
|
async getUserByEmail(email) {
|
|
94
|
-
return this.knex
|
|
126
|
+
return await this.knex
|
|
95
127
|
.select('id', 'role', 'status', 'password', 'email')
|
|
96
128
|
.from('directus_users')
|
|
97
129
|
.whereRaw(`LOWER(??) = ?`, ['email', email.toLowerCase()])
|
|
@@ -126,34 +158,51 @@ export class UsersService extends ItemsService {
|
|
|
126
158
|
/**
|
|
127
159
|
* Create a new user
|
|
128
160
|
*/
|
|
129
|
-
async createOne(data, opts
|
|
161
|
+
async createOne(data, opts) {
|
|
130
162
|
try {
|
|
131
|
-
if ('email'
|
|
163
|
+
if (data['email']) {
|
|
132
164
|
this.validateEmail(data['email']);
|
|
133
165
|
await this.checkUniqueEmails([data['email']]);
|
|
134
166
|
}
|
|
135
|
-
if ('password'
|
|
167
|
+
if (data['password']) {
|
|
136
168
|
await this.checkPasswordPolicy([data['password']]);
|
|
137
169
|
}
|
|
170
|
+
if (shouldCheckUserLimits() && data['role']) {
|
|
171
|
+
const increasedCounts = {
|
|
172
|
+
admin: 0,
|
|
173
|
+
app: 0,
|
|
174
|
+
api: 0,
|
|
175
|
+
};
|
|
176
|
+
if (typeof data['role'] === 'object') {
|
|
177
|
+
if ('admin_access' in data['role'] && data['role']['admin_access'] === true) {
|
|
178
|
+
increasedCounts.admin++;
|
|
179
|
+
}
|
|
180
|
+
else if ('app_access' in data['role'] && data['role']['app_access'] === true) {
|
|
181
|
+
increasedCounts.app++;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
increasedCounts.api++;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
const existingRoleCounts = await getRoleCountsByRoles(this.knex, [data['role']]);
|
|
189
|
+
mergeWith(increasedCounts, existingRoleCounts, (x, y) => x + y);
|
|
190
|
+
}
|
|
191
|
+
await checkIncreasedUserLimits(this.knex, increasedCounts);
|
|
192
|
+
}
|
|
138
193
|
}
|
|
139
194
|
catch (err) {
|
|
140
|
-
opts.preMutationError = err;
|
|
141
|
-
}
|
|
142
|
-
if (!('status' in data) || data['status'] === 'active') {
|
|
143
|
-
// Creating a user only requires checking user limits if the user is active, no need to care about the role
|
|
144
|
-
opts.userIntegrityCheckFlags =
|
|
145
|
-
(opts.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None) | UserIntegrityCheckFlag.UserLimits;
|
|
146
|
-
opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
|
|
195
|
+
(opts || (opts = {})).preMutationError = err;
|
|
147
196
|
}
|
|
148
197
|
return await super.createOne(data, opts);
|
|
149
198
|
}
|
|
150
199
|
/**
|
|
151
200
|
* Create multiple new users
|
|
152
201
|
*/
|
|
153
|
-
async createMany(data, opts
|
|
154
|
-
const emails = data
|
|
155
|
-
const passwords = data
|
|
156
|
-
const
|
|
202
|
+
async createMany(data, opts) {
|
|
203
|
+
const emails = data['map']((payload) => payload['email']).filter((email) => email);
|
|
204
|
+
const passwords = data['map']((payload) => payload['password']).filter((password) => password);
|
|
205
|
+
const roles = data['map']((payload) => payload['role']).filter((role) => role);
|
|
157
206
|
try {
|
|
158
207
|
if (emails.length) {
|
|
159
208
|
this.validateEmail(emails);
|
|
@@ -162,30 +211,96 @@ export class UsersService extends ItemsService {
|
|
|
162
211
|
if (passwords.length) {
|
|
163
212
|
await this.checkPasswordPolicy(passwords);
|
|
164
213
|
}
|
|
214
|
+
if (shouldCheckUserLimits() && roles.length) {
|
|
215
|
+
const increasedCounts = {
|
|
216
|
+
admin: 0,
|
|
217
|
+
app: 0,
|
|
218
|
+
api: 0,
|
|
219
|
+
};
|
|
220
|
+
const existingRoles = [];
|
|
221
|
+
for (const role of roles) {
|
|
222
|
+
if (typeof role === 'object') {
|
|
223
|
+
if ('admin_access' in role && role['admin_access'] === true) {
|
|
224
|
+
increasedCounts.admin++;
|
|
225
|
+
}
|
|
226
|
+
else if ('app_access' in role && role['app_access'] === true) {
|
|
227
|
+
increasedCounts.app++;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
increasedCounts.api++;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
existingRoles.push(role);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const existingRoleCounts = await getRoleCountsByRoles(this.knex, existingRoles);
|
|
238
|
+
mergeWith(increasedCounts, existingRoleCounts, (x, y) => x + y);
|
|
239
|
+
await checkIncreasedUserLimits(this.knex, increasedCounts);
|
|
240
|
+
}
|
|
165
241
|
}
|
|
166
242
|
catch (err) {
|
|
167
|
-
opts.preMutationError = err;
|
|
168
|
-
}
|
|
169
|
-
if (someActive) {
|
|
170
|
-
// Creating users only requires checking user limits if the users are active, no need to care about the role
|
|
171
|
-
opts.userIntegrityCheckFlags =
|
|
172
|
-
(opts.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None) | UserIntegrityCheckFlag.UserLimits;
|
|
173
|
-
opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
|
|
243
|
+
(opts || (opts = {})).preMutationError = err;
|
|
174
244
|
}
|
|
175
|
-
|
|
176
|
-
// as this requires one query per email if done in `createOne`
|
|
177
|
-
const itemsService = new ItemsService(this.collection, {
|
|
178
|
-
schema: this.schema,
|
|
179
|
-
accountability: this.accountability,
|
|
180
|
-
knex: this.knex,
|
|
181
|
-
});
|
|
182
|
-
return await itemsService.createMany(data, opts);
|
|
245
|
+
return await super.createMany(data, opts);
|
|
183
246
|
}
|
|
184
247
|
/**
|
|
185
248
|
* Update many users by primary key
|
|
186
249
|
*/
|
|
187
|
-
async updateMany(keys, data, opts
|
|
250
|
+
async updateMany(keys, data, opts) {
|
|
188
251
|
try {
|
|
252
|
+
const needsUserLimitCheck = shouldCheckUserLimits();
|
|
253
|
+
if (data['role']) {
|
|
254
|
+
/*
|
|
255
|
+
* data['role'] has the following cases:
|
|
256
|
+
* - a string with existing role id
|
|
257
|
+
* - an object with existing role id for GraphQL mutations
|
|
258
|
+
* - an object with data for new role
|
|
259
|
+
*/
|
|
260
|
+
const role = data['role']?.id ?? data['role'];
|
|
261
|
+
let newRole;
|
|
262
|
+
if (typeof role === 'string') {
|
|
263
|
+
newRole = await this.knex
|
|
264
|
+
.select('admin_access', 'app_access')
|
|
265
|
+
.from('directus_roles')
|
|
266
|
+
.where('id', role)
|
|
267
|
+
.first();
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
newRole = role;
|
|
271
|
+
}
|
|
272
|
+
if (!newRole?.admin_access) {
|
|
273
|
+
await this.checkRemainingAdminExistence(keys);
|
|
274
|
+
}
|
|
275
|
+
if (needsUserLimitCheck && newRole) {
|
|
276
|
+
const existingCounts = await getRoleCountsByUsers(this.knex, keys);
|
|
277
|
+
const increasedCounts = {
|
|
278
|
+
admin: 0,
|
|
279
|
+
app: 0,
|
|
280
|
+
api: 0,
|
|
281
|
+
};
|
|
282
|
+
if (toBoolean(newRole.admin_access)) {
|
|
283
|
+
increasedCounts.admin = keys.length - existingCounts.admin;
|
|
284
|
+
}
|
|
285
|
+
else if (toBoolean(newRole.app_access)) {
|
|
286
|
+
increasedCounts.app = keys.length - existingCounts.app;
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
increasedCounts.api = keys.length - existingCounts.api;
|
|
290
|
+
}
|
|
291
|
+
await checkIncreasedUserLimits(this.knex, increasedCounts);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (needsUserLimitCheck && data['role'] === null) {
|
|
295
|
+
await checkIncreasedUserLimits(this.knex, { admin: 0, app: 0, api: 1 });
|
|
296
|
+
}
|
|
297
|
+
if (data['status'] !== undefined && data['status'] !== 'active') {
|
|
298
|
+
await this.checkRemainingActiveAdmin(keys);
|
|
299
|
+
}
|
|
300
|
+
if (needsUserLimitCheck && data['status'] === 'active') {
|
|
301
|
+
const increasedCounts = await getRoleCountsByUsers(this.knex, keys, { inactiveUsers: true });
|
|
302
|
+
await checkIncreasedUserLimits(this.knex, increasedCounts);
|
|
303
|
+
}
|
|
189
304
|
if (data['email']) {
|
|
190
305
|
if (keys.length > 1) {
|
|
191
306
|
throw new RecordNotUniqueError({
|
|
@@ -216,45 +331,19 @@ export class UsersService extends ItemsService {
|
|
|
216
331
|
}
|
|
217
332
|
}
|
|
218
333
|
catch (err) {
|
|
219
|
-
opts.preMutationError = err;
|
|
220
|
-
}
|
|
221
|
-
if ('role' in data) {
|
|
222
|
-
opts.userIntegrityCheckFlags = UserIntegrityCheckFlag.All;
|
|
334
|
+
(opts || (opts = {})).preMutationError = err;
|
|
223
335
|
}
|
|
224
|
-
|
|
225
|
-
if (data['status'] === 'active') {
|
|
226
|
-
// User are being activated, no need to check if there are enough admins
|
|
227
|
-
opts.userIntegrityCheckFlags =
|
|
228
|
-
(opts.userIntegrityCheckFlags ?? UserIntegrityCheckFlag.None) | UserIntegrityCheckFlag.UserLimits;
|
|
229
|
-
}
|
|
230
|
-
else {
|
|
231
|
-
opts.userIntegrityCheckFlags = UserIntegrityCheckFlag.All;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
if (opts.userIntegrityCheckFlags) {
|
|
235
|
-
opts.onRequireUserIntegrityCheck?.(opts.userIntegrityCheckFlags);
|
|
236
|
-
}
|
|
237
|
-
const result = await super.updateMany(keys, data, opts);
|
|
238
|
-
// Only clear the caches if the role has been updated
|
|
239
|
-
if ('role' in data) {
|
|
240
|
-
await this.clearCaches(opts);
|
|
241
|
-
}
|
|
242
|
-
return result;
|
|
336
|
+
return await super.updateMany(keys, data, opts);
|
|
243
337
|
}
|
|
244
338
|
/**
|
|
245
339
|
* Delete multiple users by primary key
|
|
246
340
|
*/
|
|
247
|
-
async deleteMany(keys, opts
|
|
248
|
-
|
|
249
|
-
|
|
341
|
+
async deleteMany(keys, opts) {
|
|
342
|
+
try {
|
|
343
|
+
await this.checkRemainingAdminExistence(keys);
|
|
250
344
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
await validateRemainingAdminUsers({ excludeUsers: keys }, { knex: this.knex, schema: this.schema });
|
|
254
|
-
}
|
|
255
|
-
catch (err) {
|
|
256
|
-
opts.preMutationError = err;
|
|
257
|
-
}
|
|
345
|
+
catch (err) {
|
|
346
|
+
(opts || (opts = {})).preMutationError = err;
|
|
258
347
|
}
|
|
259
348
|
// Manual constraint, see https://github.com/directus/directus/pull/19912
|
|
260
349
|
await this.knex('directus_notifications').update({ sender: null }).whereIn('sender', keys);
|
|
@@ -477,16 +566,10 @@ export class UsersService extends ItemsService {
|
|
|
477
566
|
knex: this.knex,
|
|
478
567
|
schema: this.schema,
|
|
479
568
|
accountability: {
|
|
480
|
-
...(this.accountability ??
|
|
569
|
+
...(this.accountability ?? { role: null }),
|
|
481
570
|
admin: true, // We need to skip permissions checks for the update call below
|
|
482
571
|
},
|
|
483
572
|
});
|
|
484
573
|
await service.updateOne(user.id, { password, status: 'active' }, opts);
|
|
485
574
|
}
|
|
486
|
-
async clearCaches(opts) {
|
|
487
|
-
await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
|
|
488
|
-
if (this.cache && opts?.autoPurgeCache !== false) {
|
|
489
|
-
await this.cache.clear();
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
575
|
}
|
package/dist/services/utils.js
CHANGED
|
@@ -3,8 +3,6 @@ import { systemCollectionRows } from '@directus/system-data';
|
|
|
3
3
|
import { clearSystemCache, getCache } from '../cache.js';
|
|
4
4
|
import getDatabase from '../database/index.js';
|
|
5
5
|
import emitter from '../emitter.js';
|
|
6
|
-
import { fetchAllowedFields } from '../permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js';
|
|
7
|
-
import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
|
|
8
6
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
9
7
|
export class UtilsService {
|
|
10
8
|
knex;
|
|
@@ -22,16 +20,14 @@ export class UtilsService {
|
|
|
22
20
|
if (!sortField) {
|
|
23
21
|
throw new InvalidPayloadError({ reason: `Collection "${collection}" doesn't have a sort field` });
|
|
24
22
|
}
|
|
25
|
-
if (this.accountability
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
action: 'update',
|
|
29
|
-
collection,
|
|
30
|
-
}, {
|
|
31
|
-
schema: this.schema,
|
|
32
|
-
knex: this.knex,
|
|
23
|
+
if (this.accountability?.admin !== true) {
|
|
24
|
+
const permissions = this.accountability?.permissions?.find((permission) => {
|
|
25
|
+
return permission.collection === collection && permission.action === 'update';
|
|
33
26
|
});
|
|
34
|
-
|
|
27
|
+
if (!permissions) {
|
|
28
|
+
throw new ForbiddenError();
|
|
29
|
+
}
|
|
30
|
+
const allowedFields = permissions.fields ?? [];
|
|
35
31
|
if (allowedFields[0] !== '*' && allowedFields.includes(sortField) === false) {
|
|
36
32
|
throw new ForbiddenError();
|
|
37
33
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Item, PrimaryKey, Query } from '@directus/types';
|
|
2
2
|
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
|
+
import { AuthorizationService } from './authorization.js';
|
|
3
4
|
import { ItemsService } from './items.js';
|
|
4
5
|
export declare class VersionsService extends ItemsService {
|
|
6
|
+
authorizationService: AuthorizationService;
|
|
5
7
|
constructor(options: AbstractServiceOptions);
|
|
6
8
|
private validateCreateData;
|
|
7
9
|
getMainItem(collection: string, item: PrimaryKey, query?: Query): Promise<Item>;
|