@directus/api 20.0.0-rc.0 → 20.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 +9 -4
- package/dist/auth/drivers/ldap.js +4 -4
- package/dist/auth/drivers/local.js +4 -4
- package/dist/auth/drivers/oauth2.js +4 -4
- package/dist/auth/drivers/openid.js +4 -2
- package/dist/cache.js +0 -3
- 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 +9 -1
- package/dist/constants.js +10 -0
- package/dist/controllers/auth.js +16 -5
- package/dist/controllers/permissions.js +2 -14
- package/dist/controllers/roles.js +1 -22
- package/dist/controllers/{access.d.ts → tus.d.ts} +1 -0
- package/dist/controllers/tus.js +72 -0
- 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 +1 -2
- package/dist/database/migrations/20240701A-add-tus-data.js +12 -0
- package/dist/database/{run-ast/types.d.ts → run-ast.d.ts} +9 -3
- package/dist/database/run-ast.js +450 -0
- package/dist/flows.js +4 -3
- 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/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 +5 -10
- package/dist/services/authentication.js +26 -32
- 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 +1 -0
- package/dist/services/fields.js +24 -53
- package/dist/services/files/lib/extract-metadata.d.ts +3 -0
- package/dist/services/files/lib/extract-metadata.js +32 -0
- package/dist/services/files/utils/get-metadata.d.ts +5 -0
- package/dist/services/files/utils/get-metadata.js +107 -0
- package/dist/services/files.d.ts +4 -6
- package/dist/services/files.js +24 -140
- 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/meta.js +23 -60
- 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 +6 -0
- package/dist/services/relations.js +29 -26
- package/dist/services/roles.d.ts +12 -4
- package/dist/services/roles.js +424 -57
- package/dist/services/server.js +6 -0
- 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.d.ts +36 -0
- package/dist/services/tus/data-store.js +214 -0
- package/dist/services/tus/index.d.ts +2 -0
- package/dist/services/tus/index.js +2 -0
- package/dist/services/tus/lockers.d.ts +36 -0
- package/dist/services/tus/lockers.js +83 -0
- package/dist/services/tus/server.d.ts +8 -0
- package/dist/services/tus/server.js +80 -0
- package/dist/services/tus/utils/wait-timeout.d.ts +1 -0
- package/dist/services/tus/utils/wait-timeout.js +13 -0
- 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/storage/register-locations.js +5 -1
- 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 +8 -37
- 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-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 +34 -14
- package/dist/utils/verify-session-jwt.js +2 -1
- package/dist/websocket/authenticate.d.ts +2 -0
- package/dist/websocket/authenticate.js +12 -0
- package/dist/websocket/controllers/graphql.js +4 -1
- package/dist/websocket/controllers/hooks.js +0 -4
- package/dist/websocket/controllers/rest.js +2 -0
- package/dist/websocket/handlers/subscribe.js +2 -0
- package/dist/websocket/utils/items.d.ts +1 -1
- package/package.json +35 -33
- 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/20240619A-permissions-policies.js +0 -163
- package/dist/database/run-ast/lib/get-db-query.d.ts +0 -4
- package/dist/database/run-ast/lib/get-db-query.js +0 -194
- 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 -24
- 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/{20240619A-permissions-policies.d.ts → 20240701A-add-tus-data.d.ts} +0 -0
- /package/dist/{utils → services/files/utils}/parse-image-metadata.d.ts +0 -0
- /package/dist/{utils → services/files/utils}/parse-image-metadata.js +0 -0
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.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>;
|
|
@@ -6,15 +6,21 @@ import objectHash from 'object-hash';
|
|
|
6
6
|
import { getCache } from '../cache.js';
|
|
7
7
|
import getDatabase from '../database/index.js';
|
|
8
8
|
import emitter from '../emitter.js';
|
|
9
|
-
import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
|
|
10
9
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
11
10
|
import { ActivityService } from './activity.js';
|
|
11
|
+
import { AuthorizationService } from './authorization.js';
|
|
12
12
|
import { ItemsService } from './items.js';
|
|
13
13
|
import { PayloadService } from './payload.js';
|
|
14
14
|
import { RevisionsService } from './revisions.js';
|
|
15
15
|
export class VersionsService extends ItemsService {
|
|
16
|
+
authorizationService;
|
|
16
17
|
constructor(options) {
|
|
17
18
|
super('directus_versions', options);
|
|
19
|
+
this.authorizationService = new AuthorizationService({
|
|
20
|
+
accountability: this.accountability,
|
|
21
|
+
knex: this.knex,
|
|
22
|
+
schema: this.schema,
|
|
23
|
+
});
|
|
18
24
|
}
|
|
19
25
|
async validateCreateData(data) {
|
|
20
26
|
if (!data['key'])
|
|
@@ -49,31 +55,11 @@ export class VersionsService extends ItemsService {
|
|
|
49
55
|
});
|
|
50
56
|
}
|
|
51
57
|
// will throw an error if the accountability does not have permission to read the item
|
|
52
|
-
|
|
53
|
-
await validateAccess({
|
|
54
|
-
accountability: this.accountability,
|
|
55
|
-
action: 'read',
|
|
56
|
-
collection: data['collection'],
|
|
57
|
-
primaryKeys: [data['item']],
|
|
58
|
-
}, {
|
|
59
|
-
schema: this.schema,
|
|
60
|
-
knex: this.knex,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
58
|
+
await this.authorizationService.checkAccess('read', data['collection'], data['item']);
|
|
63
59
|
}
|
|
64
60
|
async getMainItem(collection, item, query) {
|
|
65
61
|
// will throw an error if the accountability does not have permission to read the item
|
|
66
|
-
|
|
67
|
-
await validateAccess({
|
|
68
|
-
accountability: this.accountability,
|
|
69
|
-
action: 'read',
|
|
70
|
-
collection,
|
|
71
|
-
primaryKeys: [item],
|
|
72
|
-
}, {
|
|
73
|
-
schema: this.schema,
|
|
74
|
-
knex: this.knex,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
62
|
+
await this.authorizationService.checkAccess('read', collection, item);
|
|
77
63
|
const itemsService = new ItemsService(collection, {
|
|
78
64
|
knex: this.knex,
|
|
79
65
|
accountability: this.accountability,
|
|
@@ -214,17 +200,7 @@ export class VersionsService extends ItemsService {
|
|
|
214
200
|
async promote(version, mainHash, fields) {
|
|
215
201
|
const { id, collection, item } = (await this.readOne(version));
|
|
216
202
|
// will throw an error if the accountability does not have permission to update the item
|
|
217
|
-
|
|
218
|
-
await validateAccess({
|
|
219
|
-
accountability: this.accountability,
|
|
220
|
-
action: 'update',
|
|
221
|
-
collection,
|
|
222
|
-
primaryKeys: [item],
|
|
223
|
-
}, {
|
|
224
|
-
schema: this.schema,
|
|
225
|
-
knex: this.knex,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
203
|
+
await this.authorizationService.checkAccess('update', collection, item);
|
|
228
204
|
const { outdated } = await this.verifyHash(collection, item, mainHash);
|
|
229
205
|
if (outdated) {
|
|
230
206
|
throw new UnprocessableContentError({
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { toArray } from '@directus/utils';
|
|
3
|
+
import { RESUMABLE_UPLOADS } from '../constants.js';
|
|
3
4
|
import { getConfigFromEnv } from '../utils/get-config-from-env.js';
|
|
4
5
|
export const registerLocations = async (storage) => {
|
|
5
6
|
const env = useEnv();
|
|
6
7
|
const locations = toArray(env['STORAGE_LOCATIONS']);
|
|
8
|
+
const tus = {
|
|
9
|
+
chunkSize: RESUMABLE_UPLOADS.CHUNK_SIZE,
|
|
10
|
+
};
|
|
7
11
|
locations.forEach((location) => {
|
|
8
12
|
location = location.trim();
|
|
9
13
|
const driverConfig = getConfigFromEnv(`STORAGE_${location.toUpperCase()}_`);
|
|
10
14
|
const { driver, ...options } = driverConfig;
|
|
11
|
-
storage.registerLocation(location, { driver, options });
|
|
15
|
+
storage.registerLocation(location, { driver, options: { ...options, tus } });
|
|
12
16
|
});
|
|
13
17
|
};
|
|
@@ -2,11 +2,11 @@ import { useEnv } from '@directus/env';
|
|
|
2
2
|
import { version } from 'directus/version';
|
|
3
3
|
import { getHelpers } from '../../database/helpers/index.js';
|
|
4
4
|
import { getDatabase, getDatabaseClient } from '../../database/index.js';
|
|
5
|
-
import { fetchUserCount } from '../../utils/fetch-user-count/fetch-user-count.js';
|
|
6
5
|
import { getExtensionCount } from '../utils/get-extension-count.js';
|
|
7
6
|
import { getFieldCount } from '../utils/get-field-count.js';
|
|
8
7
|
import { getFilesizeSum } from '../utils/get-filesize-sum.js';
|
|
9
8
|
import { getItemCount } from '../utils/get-item-count.js';
|
|
9
|
+
import { getUserCount } from '../utils/get-user-count.js';
|
|
10
10
|
import { getUserItemCount } from '../utils/get-user-item-count.js';
|
|
11
11
|
const basicCountTasks = [
|
|
12
12
|
{ collection: 'directus_dashboards' },
|
|
@@ -27,7 +27,7 @@ export const getReport = async () => {
|
|
|
27
27
|
const helpers = getHelpers(db);
|
|
28
28
|
const [basicCounts, userCounts, userItemCount, fieldsCounts, extensionsCounts, databaseSize, filesizes] = await Promise.all([
|
|
29
29
|
getItemCount(db, basicCountTasks),
|
|
30
|
-
|
|
30
|
+
getUserCount(db),
|
|
31
31
|
getUserItemCount(db),
|
|
32
32
|
getFieldCount(db),
|
|
33
33
|
getExtensionCount(db),
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
import { type AccessTypeCount } from './get-user-count.js';
|
|
4
|
+
/**
|
|
5
|
+
* Ensure that user limits are not reached
|
|
6
|
+
*/
|
|
7
|
+
export declare function checkIncreasedUserLimits(db: Knex, increasedUserCounts: AccessTypeCount, ignoreIds?: PrimaryKey[]): Promise<void>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { LimitExceededError } from '@directus/errors';
|
|
3
|
+
import { getUserCount } from './get-user-count.js';
|
|
4
|
+
const env = useEnv();
|
|
5
|
+
/**
|
|
6
|
+
* Ensure that user limits are not reached
|
|
7
|
+
*/
|
|
8
|
+
export async function checkIncreasedUserLimits(db, increasedUserCounts, ignoreIds = []) {
|
|
9
|
+
if (!increasedUserCounts.admin && !increasedUserCounts.app && !increasedUserCounts.api)
|
|
10
|
+
return;
|
|
11
|
+
const userCounts = await getUserCount(db, ignoreIds);
|
|
12
|
+
// Admins have full permissions, therefore should count under app access limit
|
|
13
|
+
const existingAppUsersCount = userCounts.admin + userCounts.app;
|
|
14
|
+
const newAppUsersCount = increasedUserCounts.admin + increasedUserCounts.app;
|
|
15
|
+
if (increasedUserCounts.admin > 0 &&
|
|
16
|
+
increasedUserCounts.admin + userCounts.admin > Number(env['USERS_ADMIN_ACCESS_LIMIT'])) {
|
|
17
|
+
throw new LimitExceededError({ category: 'Active Admin users' });
|
|
18
|
+
}
|
|
19
|
+
if (newAppUsersCount > 0 && newAppUsersCount + existingAppUsersCount > Number(env['USERS_APP_ACCESS_LIMIT'])) {
|
|
20
|
+
throw new LimitExceededError({ category: 'Active App users' });
|
|
21
|
+
}
|
|
22
|
+
if (increasedUserCounts.api > 0 && increasedUserCounts.api + userCounts.api > Number(env['USERS_API_ACCESS_LIMIT'])) {
|
|
23
|
+
throw new LimitExceededError({ category: 'Active API users' });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { toBoolean } from '@directus/utils';
|
|
2
|
+
import {} from './get-user-count.js';
|
|
3
|
+
/**
|
|
4
|
+
* Get the role type counts by role IDs
|
|
5
|
+
*/
|
|
6
|
+
export async function getRoleCountsByRoles(db, roles) {
|
|
7
|
+
const counts = {
|
|
8
|
+
admin: 0,
|
|
9
|
+
app: 0,
|
|
10
|
+
api: 0,
|
|
11
|
+
};
|
|
12
|
+
const result = (await db.select('id', 'admin_access', 'app_access').from('directus_roles').whereIn('id', roles));
|
|
13
|
+
for (const role of result) {
|
|
14
|
+
const adminAccess = toBoolean(role.admin_access);
|
|
15
|
+
const appAccess = toBoolean(role.app_access);
|
|
16
|
+
if (adminAccess) {
|
|
17
|
+
counts.admin++;
|
|
18
|
+
}
|
|
19
|
+
else if (appAccess) {
|
|
20
|
+
counts.app++;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
counts.api++;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return counts;
|
|
27
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
import type { AccessTypeCount } from './get-user-count.js';
|
|
4
|
+
type CountOptions = {
|
|
5
|
+
inactiveUsers?: boolean;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Get the role type counts by user IDs
|
|
9
|
+
*/
|
|
10
|
+
export declare function getRoleCountsByUsers(db: Knex, userIds: PrimaryKey[], options?: CountOptions): Promise<AccessTypeCount>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { toBoolean } from '@directus/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Get the role type counts by user IDs
|
|
4
|
+
*/
|
|
5
|
+
export async function getRoleCountsByUsers(db, userIds, options = {}) {
|
|
6
|
+
const counts = {
|
|
7
|
+
admin: 0,
|
|
8
|
+
app: 0,
|
|
9
|
+
api: 0,
|
|
10
|
+
};
|
|
11
|
+
const result = await db
|
|
12
|
+
.count('directus_users.id', { as: 'count' })
|
|
13
|
+
.select('directus_roles.admin_access', 'directus_roles.app_access')
|
|
14
|
+
.from('directus_users')
|
|
15
|
+
.whereIn('directus_users.id', userIds)
|
|
16
|
+
.andWhere('directus_users.status', options.inactiveUsers ? '!=' : '=', 'active')
|
|
17
|
+
.leftJoin('directus_roles', 'directus_users.role', '=', 'directus_roles.id')
|
|
18
|
+
.groupBy('directus_roles.admin_access', 'directus_roles.app_access');
|
|
19
|
+
for (const record of result) {
|
|
20
|
+
const adminAccess = toBoolean(record.admin_access);
|
|
21
|
+
const appAccess = toBoolean(record.app_access);
|
|
22
|
+
const count = Number(record.count);
|
|
23
|
+
if (adminAccess) {
|
|
24
|
+
counts.admin += count;
|
|
25
|
+
}
|
|
26
|
+
else if (appAccess) {
|
|
27
|
+
counts.app += count;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
counts.api += count;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return counts;
|
|
34
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PrimaryKey } from '@directus/types';
|
|
2
|
+
import { type Knex } from 'knex';
|
|
3
|
+
export interface AccessTypeCount {
|
|
4
|
+
admin: number;
|
|
5
|
+
app: number;
|
|
6
|
+
api: number;
|
|
7
|
+
}
|
|
8
|
+
export declare const getUserCount: (db: Knex, ignoreIds?: PrimaryKey[]) => Promise<AccessTypeCount>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { toBoolean } from '@directus/utils';
|
|
2
|
+
import {} from 'knex';
|
|
3
|
+
export const getUserCount = async (db, ignoreIds = []) => {
|
|
4
|
+
const counts = {
|
|
5
|
+
admin: 0,
|
|
6
|
+
app: 0,
|
|
7
|
+
api: 0,
|
|
8
|
+
};
|
|
9
|
+
const result = (await db
|
|
10
|
+
.count('directus_users.id', { as: 'count' })
|
|
11
|
+
.select('directus_roles.admin_access', 'directus_roles.app_access')
|
|
12
|
+
.from('directus_users')
|
|
13
|
+
.whereNotIn('directus_users.id', ignoreIds)
|
|
14
|
+
.andWhere('directus_users.status', 'active')
|
|
15
|
+
.leftJoin('directus_roles', 'directus_users.role', '=', 'directus_roles.id')
|
|
16
|
+
.where('directus_users.status', '=', 'active')
|
|
17
|
+
.groupBy('directus_roles.admin_access', 'directus_roles.app_access'));
|
|
18
|
+
for (const record of result) {
|
|
19
|
+
const adminAccess = toBoolean(record.admin_access);
|
|
20
|
+
const appAccess = toBoolean(record.app_access);
|
|
21
|
+
const count = Number(record.count);
|
|
22
|
+
if (adminAccess) {
|
|
23
|
+
counts.admin += count;
|
|
24
|
+
}
|
|
25
|
+
else if (appAccess) {
|
|
26
|
+
counts.app += count;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
counts.api += count;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return counts;
|
|
33
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
import { type AccessTypeCount } from './get-user-count.js';
|
|
4
|
+
/**
|
|
5
|
+
* Get the user type counts by role IDs
|
|
6
|
+
*/
|
|
7
|
+
export declare function getUserCountsByRoles(db: Knex, roleIds: PrimaryKey[]): Promise<AccessTypeCount>;
|