@directus/api 11.1.0 → 12.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 +3 -4
- package/dist/auth/auth.d.ts +4 -4
- package/dist/auth/auth.js +2 -2
- package/dist/auth/drivers/ldap.js +20 -17
- package/dist/auth/drivers/local.js +5 -5
- package/dist/auth/drivers/oauth2.js +16 -16
- package/dist/auth/drivers/openid.js +18 -17
- package/dist/auth/drivers/saml.js +6 -7
- package/dist/auth.js +3 -2
- package/dist/cache.js +3 -13
- package/dist/cli/utils/create-env/env-stub.liquid +5 -7
- package/dist/controllers/activity.js +7 -6
- package/dist/controllers/assets.js +25 -12
- package/dist/controllers/auth.js +8 -7
- package/dist/controllers/collections.js +4 -3
- package/dist/controllers/dashboards.js +5 -4
- package/dist/controllers/extensions.js +3 -3
- package/dist/controllers/fields.js +9 -8
- package/dist/controllers/files.js +11 -11
- package/dist/controllers/flows.js +5 -4
- package/dist/controllers/folders.js +5 -4
- package/dist/controllers/items.js +14 -13
- package/dist/controllers/not-found.js +2 -2
- package/dist/controllers/notifications.js +5 -4
- package/dist/controllers/operations.js +5 -4
- package/dist/controllers/panels.js +5 -4
- package/dist/controllers/permissions.js +5 -4
- package/dist/controllers/presets.js +5 -4
- package/dist/controllers/relations.js +6 -5
- package/dist/controllers/roles.js +5 -4
- package/dist/controllers/schema.js +8 -8
- package/dist/controllers/server.js +2 -2
- package/dist/controllers/settings.js +3 -2
- package/dist/controllers/shares.js +7 -6
- package/dist/controllers/translations.js +6 -5
- package/dist/controllers/users.js +22 -21
- package/dist/controllers/utils.js +10 -10
- package/dist/controllers/webhooks.js +5 -4
- package/dist/{exceptions/database → database/errors}/dialects/mssql.js +8 -18
- package/dist/{exceptions/database → database/errors}/dialects/mysql.js +9 -19
- package/dist/{exceptions/database → database/errors}/dialects/oracle.js +2 -2
- package/dist/{exceptions/database → database/errors}/dialects/postgres.js +7 -18
- package/dist/{exceptions/database → database/errors}/dialects/sqlite.js +7 -10
- package/dist/{exceptions/database → database/errors}/translate.js +1 -1
- package/dist/database/migrations/run.js +10 -1
- package/dist/env.js +6 -13
- package/dist/errors/codes.d.ts +29 -0
- package/dist/errors/codes.js +30 -0
- package/dist/errors/contains-null-values.d.ts +7 -0
- package/dist/errors/contains-null-values.js +4 -0
- package/dist/errors/content-too-large.d.ts +1 -0
- package/dist/errors/content-too-large.js +3 -0
- package/dist/errors/forbidden.d.ts +1 -0
- package/dist/errors/forbidden.js +3 -0
- package/dist/errors/hit-rate-limit.d.ts +6 -0
- package/dist/errors/hit-rate-limit.js +8 -0
- package/dist/errors/illegal-asset-transformation.d.ts +4 -0
- package/dist/errors/illegal-asset-transformation.js +3 -0
- package/dist/errors/index.d.ts +28 -0
- package/dist/errors/index.js +28 -0
- package/dist/errors/invalid-credentials.d.ts +1 -0
- package/dist/errors/invalid-credentials.js +3 -0
- package/dist/errors/invalid-foreign-key.d.ts +6 -0
- package/dist/errors/invalid-foreign-key.js +14 -0
- package/dist/errors/invalid-ip.d.ts +1 -0
- package/dist/errors/invalid-ip.js +3 -0
- package/dist/errors/invalid-otp.d.ts +1 -0
- package/dist/errors/invalid-otp.js +3 -0
- package/dist/errors/invalid-payload.d.ts +5 -0
- package/dist/errors/invalid-payload.js +4 -0
- package/dist/errors/invalid-provider-config.d.ts +5 -0
- package/dist/errors/invalid-provider-config.js +3 -0
- package/dist/errors/invalid-provider.d.ts +1 -0
- package/dist/errors/invalid-provider.js +3 -0
- package/dist/errors/invalid-query.d.ts +5 -0
- package/dist/errors/invalid-query.js +4 -0
- package/dist/errors/invalid-token.d.ts +1 -0
- package/dist/errors/invalid-token.js +3 -0
- package/dist/errors/method-not-allowed.d.ts +6 -0
- package/dist/errors/method-not-allowed.js +6 -0
- package/dist/errors/not-null-violation.d.ts +6 -0
- package/dist/errors/not-null-violation.js +14 -0
- package/dist/errors/range-not-satisfiable.d.ts +7 -0
- package/dist/errors/range-not-satisfiable.js +7 -0
- package/dist/errors/record-not-unique.d.ts +6 -0
- package/dist/errors/record-not-unique.js +14 -0
- package/dist/errors/route-not-found.d.ts +5 -0
- package/dist/errors/route-not-found.js +4 -0
- package/dist/errors/service-unavailable.d.ts +7 -0
- package/dist/errors/service-unavailable.js +4 -0
- package/dist/errors/token-expired.d.ts +1 -0
- package/dist/errors/token-expired.js +3 -0
- package/dist/errors/unexpected-response.d.ts +1 -0
- package/dist/errors/unexpected-response.js +3 -0
- package/dist/errors/unprocessable-content.d.ts +5 -0
- package/dist/errors/unprocessable-content.js +4 -0
- package/dist/errors/unsupported-media-type.d.ts +6 -0
- package/dist/errors/unsupported-media-type.js +4 -0
- package/dist/errors/user-suspended.d.ts +1 -0
- package/dist/errors/user-suspended.js +3 -0
- package/dist/errors/value-out-of-range.d.ts +6 -0
- package/dist/errors/value-out-of-range.js +14 -0
- package/dist/errors/value-too-long.d.ts +6 -0
- package/dist/errors/value-too-long.js +14 -0
- package/dist/extensions.js +0 -4
- package/dist/flows.js +6 -8
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/messenger.js +4 -4
- package/dist/middleware/authenticate.js +1 -1
- package/dist/middleware/check-ip.js +2 -2
- package/dist/middleware/collection-exists.js +2 -2
- package/dist/middleware/error-handler.js +7 -7
- package/dist/middleware/graphql.js +11 -9
- package/dist/middleware/rate-limiter-global.d.ts +2 -2
- package/dist/middleware/rate-limiter-global.js +2 -3
- package/dist/middleware/rate-limiter-ip.d.ts +2 -2
- package/dist/middleware/rate-limiter-ip.js +2 -3
- package/dist/middleware/validate-batch.js +3 -4
- package/dist/rate-limiter.js +2 -9
- package/dist/services/activity.js +3 -2
- package/dist/services/assets.js +9 -10
- package/dist/services/authentication.js +12 -11
- package/dist/services/authorization.d.ts +1 -1
- package/dist/services/authorization.js +16 -16
- package/dist/services/collections.js +17 -16
- package/dist/services/fields.js +16 -14
- package/dist/services/files.js +7 -6
- package/dist/services/graphql/errors/execution.d.ts +6 -0
- package/dist/services/graphql/errors/execution.js +2 -0
- package/dist/services/graphql/errors/index.d.ts +2 -0
- package/dist/services/graphql/errors/index.js +2 -0
- package/dist/services/graphql/errors/validation.d.ts +6 -0
- package/dist/services/graphql/errors/validation.js +2 -0
- package/dist/services/graphql/index.d.ts +2 -2
- package/dist/services/graphql/index.js +30 -12
- package/dist/services/graphql/utils/process-error.js +3 -3
- package/dist/services/import-export.js +7 -7
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/items.js +14 -13
- package/dist/services/mail/index.js +3 -3
- package/dist/services/meta.js +3 -3
- package/dist/services/payload.js +11 -7
- package/dist/services/relations.js +32 -22
- package/dist/services/revisions.js +3 -3
- package/dist/services/roles.js +10 -9
- package/dist/services/schema.js +5 -5
- package/dist/services/shares.js +4 -4
- package/dist/services/tfa.js +6 -6
- package/dist/services/translations.d.ts +2 -2
- package/dist/services/translations.js +4 -4
- package/dist/services/users.js +26 -29
- package/dist/services/utils.js +4 -4
- package/dist/synchronization.js +3 -3
- package/dist/types/items.d.ts +2 -2
- package/dist/utils/apply-diff.js +2 -2
- package/dist/utils/apply-query.js +17 -7
- package/dist/utils/get-accountability-for-role.js +1 -2
- package/dist/utils/get-accountability-for-token.js +3 -3
- package/dist/utils/get-column-path.js +5 -3
- package/dist/utils/get-column.js +3 -3
- package/dist/utils/get-service.d.ts +1 -1
- package/dist/utils/get-service.js +1 -1
- package/dist/utils/jwt.js +5 -5
- package/dist/utils/validate-diff.js +23 -9
- package/dist/utils/validate-keys.js +3 -3
- package/dist/utils/validate-query.d.ts +2 -0
- package/dist/utils/validate-query.js +27 -21
- package/dist/utils/validate-snapshot.js +11 -5
- package/dist/websocket/authenticate.js +12 -15
- package/dist/websocket/controllers/base.js +18 -15
- package/dist/websocket/controllers/graphql.js +2 -2
- package/dist/websocket/controllers/index.js +3 -7
- package/dist/websocket/controllers/rest.js +3 -3
- package/dist/websocket/{exceptions.d.ts → errors.d.ts} +5 -5
- package/dist/websocket/{exceptions.js → errors.js} +10 -10
- package/dist/websocket/handlers/items.js +5 -5
- package/dist/websocket/handlers/subscribe.js +8 -8
- package/package.json +15 -15
- package/dist/exceptions/content-too-large.d.ts +0 -4
- package/dist/exceptions/content-too-large.js +0 -6
- package/dist/exceptions/database/contains-null-values.d.ts +0 -9
- package/dist/exceptions/database/contains-null-values.js +0 -6
- package/dist/exceptions/database/invalid-foreign-key.d.ts +0 -10
- package/dist/exceptions/database/invalid-foreign-key.js +0 -11
- package/dist/exceptions/database/not-null-violation.d.ts +0 -9
- package/dist/exceptions/database/not-null-violation.js +0 -6
- package/dist/exceptions/database/record-not-unique.d.ts +0 -10
- package/dist/exceptions/database/record-not-unique.js +0 -11
- package/dist/exceptions/database/value-out-of-range.d.ts +0 -10
- package/dist/exceptions/database/value-out-of-range.js +0 -11
- package/dist/exceptions/database/value-too-long.d.ts +0 -9
- package/dist/exceptions/database/value-too-long.js +0 -11
- package/dist/exceptions/forbidden.d.ts +0 -6
- package/dist/exceptions/forbidden.js +0 -13
- package/dist/exceptions/graphql-validation.d.ts +0 -4
- package/dist/exceptions/graphql-validation.js +0 -6
- package/dist/exceptions/hit-rate-limit.d.ts +0 -9
- package/dist/exceptions/hit-rate-limit.js +0 -6
- package/dist/exceptions/illegal-asset-transformation.d.ts +0 -4
- package/dist/exceptions/illegal-asset-transformation.js +0 -6
- package/dist/exceptions/index.d.ts +0 -21
- package/dist/exceptions/index.js +0 -21
- package/dist/exceptions/invalid-config.d.ts +0 -4
- package/dist/exceptions/invalid-config.js +0 -6
- package/dist/exceptions/invalid-credentials.d.ts +0 -4
- package/dist/exceptions/invalid-credentials.js +0 -6
- package/dist/exceptions/invalid-ip.d.ts +0 -4
- package/dist/exceptions/invalid-ip.js +0 -6
- package/dist/exceptions/invalid-otp.d.ts +0 -4
- package/dist/exceptions/invalid-otp.js +0 -6
- package/dist/exceptions/invalid-payload.d.ts +0 -4
- package/dist/exceptions/invalid-payload.js +0 -6
- package/dist/exceptions/invalid-provider.d.ts +0 -4
- package/dist/exceptions/invalid-provider.js +0 -6
- package/dist/exceptions/invalid-query.d.ts +0 -4
- package/dist/exceptions/invalid-query.js +0 -6
- package/dist/exceptions/invalid-token.d.ts +0 -4
- package/dist/exceptions/invalid-token.js +0 -6
- package/dist/exceptions/method-not-allowed.d.ts +0 -8
- package/dist/exceptions/method-not-allowed.js +0 -6
- package/dist/exceptions/range-not-satisfiable.d.ts +0 -5
- package/dist/exceptions/range-not-satisfiable.js +0 -9
- package/dist/exceptions/route-not-found.d.ts +0 -4
- package/dist/exceptions/route-not-found.js +0 -6
- package/dist/exceptions/service-unavailable.d.ts +0 -9
- package/dist/exceptions/service-unavailable.js +0 -6
- package/dist/exceptions/token-expired.d.ts +0 -4
- package/dist/exceptions/token-expired.js +0 -6
- package/dist/exceptions/unexpected-response.d.ts +0 -4
- package/dist/exceptions/unexpected-response.js +0 -6
- package/dist/exceptions/unprocessable-entity.d.ts +0 -4
- package/dist/exceptions/unprocessable-entity.js +0 -6
- package/dist/exceptions/unsupported-media-type.d.ts +0 -4
- package/dist/exceptions/unsupported-media-type.js +0 -6
- package/dist/exceptions/user-suspended.d.ts +0 -4
- package/dist/exceptions/user-suspended.js +0 -6
- /package/dist/{exceptions/database → database/errors}/dialects/mssql.d.ts +0 -0
- /package/dist/{exceptions/database → database/errors}/dialects/mysql.d.ts +0 -0
- /package/dist/{exceptions/database → database/errors}/dialects/oracle.d.ts +0 -0
- /package/dist/{exceptions/database → database/errors}/dialects/postgres.d.ts +0 -0
- /package/dist/{exceptions/database → database/errors}/dialects/sqlite.d.ts +0 -0
- /package/dist/{exceptions/database → database/errors}/dialects/types.d.ts +0 -0
- /package/dist/{exceptions/database → database/errors}/dialects/types.js +0 -0
- /package/dist/{exceptions/database → database/errors}/translate.d.ts +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import ms from 'ms';
|
|
2
1
|
import env from '../env.js';
|
|
3
|
-
import {
|
|
2
|
+
import { HitRateLimitError } from '../errors/index.js';
|
|
4
3
|
import logger from '../logger.js';
|
|
5
4
|
import { createRateLimiter } from '../rate-limiter.js';
|
|
6
5
|
import asyncHandler from '../utils/async-handler.js';
|
|
@@ -20,7 +19,7 @@ if (env['RATE_LIMITER_GLOBAL_ENABLED'] === true) {
|
|
|
20
19
|
if (rateLimiterRes instanceof Error)
|
|
21
20
|
throw rateLimiterRes;
|
|
22
21
|
res.set('Retry-After', String(Math.round(rateLimiterRes.msBeforeNext / 1000)));
|
|
23
|
-
throw new
|
|
22
|
+
throw new HitRateLimitError({
|
|
24
23
|
limit: +env['RATE_LIMITER_GLOBAL_POINTS'],
|
|
25
24
|
reset: new Date(Date.now() + rateLimiterRes.msBeforeNext),
|
|
26
25
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RequestHandler } from 'express';
|
|
2
|
-
import type {
|
|
2
|
+
import type { RateLimiterMemory, RateLimiterRedis } from 'rate-limiter-flexible';
|
|
3
3
|
declare let checkRateLimit: RequestHandler;
|
|
4
|
-
export declare let rateLimiter: RateLimiterRedis |
|
|
4
|
+
export declare let rateLimiter: RateLimiterRedis | RateLimiterMemory;
|
|
5
5
|
export default checkRateLimit;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import ms from 'ms';
|
|
2
1
|
import env from '../env.js';
|
|
3
|
-
import {
|
|
2
|
+
import { HitRateLimitError } from '../errors/index.js';
|
|
4
3
|
import { createRateLimiter } from '../rate-limiter.js';
|
|
5
4
|
import asyncHandler from '../utils/async-handler.js';
|
|
6
5
|
import { getIPFromReq } from '../utils/get-ip-from-req.js';
|
|
@@ -18,7 +17,7 @@ if (env['RATE_LIMITER_ENABLED'] === true) {
|
|
|
18
17
|
if (rateLimiterRes instanceof Error)
|
|
19
18
|
throw rateLimiterRes;
|
|
20
19
|
res.set('Retry-After', String(Math.round(rateLimiterRes.msBeforeNext / 1000)));
|
|
21
|
-
throw new
|
|
20
|
+
throw new HitRateLimitError({
|
|
22
21
|
limit: +env['RATE_LIMITER_POINTS'],
|
|
23
22
|
reset: new Date(Date.now() + rateLimiterRes.msBeforeNext),
|
|
24
23
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { FailedValidationException } from '@directus/exceptions';
|
|
2
1
|
import Joi from 'joi';
|
|
3
|
-
import {
|
|
2
|
+
import { InvalidPayloadError } from '../errors/index.js';
|
|
4
3
|
import asyncHandler from '../utils/async-handler.js';
|
|
5
4
|
import { sanitizeQuery } from '../utils/sanitize-query.js';
|
|
6
5
|
export const validateBatch = (scope) => asyncHandler(async (req, _res, next) => {
|
|
@@ -12,7 +11,7 @@ export const validateBatch = (scope) => asyncHandler(async (req, _res, next) =>
|
|
|
12
11
|
return next();
|
|
13
12
|
}
|
|
14
13
|
if (!req.body)
|
|
15
|
-
throw new
|
|
14
|
+
throw new InvalidPayloadError({ reason: 'Payload in body is required' });
|
|
16
15
|
if (['update', 'delete'].includes(scope) && Array.isArray(req.body)) {
|
|
17
16
|
return next();
|
|
18
17
|
}
|
|
@@ -36,7 +35,7 @@ export const validateBatch = (scope) => asyncHandler(async (req, _res, next) =>
|
|
|
36
35
|
}
|
|
37
36
|
const { error } = batchSchema.validate(req.body);
|
|
38
37
|
if (error) {
|
|
39
|
-
throw new
|
|
38
|
+
throw new InvalidPayloadError({ reason: error.details[0].message });
|
|
40
39
|
}
|
|
41
40
|
return next();
|
|
42
41
|
});
|
package/dist/rate-limiter.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { merge } from 'lodash-es';
|
|
2
|
-
import {
|
|
2
|
+
import { RateLimiterMemory, RateLimiterRedis } from 'rate-limiter-flexible';
|
|
3
3
|
import env from './env.js';
|
|
4
4
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
@@ -8,8 +8,6 @@ export function createRateLimiter(configPrefix = 'RATE_LIMITER', configOverrides
|
|
|
8
8
|
switch (env['RATE_LIMITER_STORE']) {
|
|
9
9
|
case 'redis':
|
|
10
10
|
return new RateLimiterRedis(getConfig('redis', configPrefix, configOverrides));
|
|
11
|
-
case 'memcache':
|
|
12
|
-
return new RateLimiterMemcache(getConfig('memcache', configPrefix, configOverrides));
|
|
13
11
|
case 'memory':
|
|
14
12
|
default:
|
|
15
13
|
return new RateLimiterMemory(getConfig('memory', configPrefix, configOverrides));
|
|
@@ -19,12 +17,7 @@ function getConfig(store = 'memory', configPrefix = 'RATE_LIMITER', overrides) {
|
|
|
19
17
|
const config = getConfigFromEnv(`${configPrefix}_`, `${configPrefix}_${store}_`);
|
|
20
18
|
if (store === 'redis') {
|
|
21
19
|
const Redis = require('ioredis');
|
|
22
|
-
|
|
23
|
-
config.storeClient = new Redis(env[`${configPrefix}_REDIS`] || getConfigFromEnv(`${configPrefix}_REDIS_`));
|
|
24
|
-
}
|
|
25
|
-
if (store === 'memcache') {
|
|
26
|
-
const Memcached = require('memcached');
|
|
27
|
-
config.storeClient = new Memcached(env[`${configPrefix}_MEMCACHE`], getConfigFromEnv(`${configPrefix}_MEMCACHE_`));
|
|
20
|
+
config.storeClient = new Redis(env[`REDIS`] || getConfigFromEnv(`REDIS_`));
|
|
28
21
|
}
|
|
29
22
|
delete config.enabled;
|
|
30
23
|
delete config.store;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Action } from '@directus/constants';
|
|
2
|
+
import { isDirectusError } from '@directus/errors';
|
|
2
3
|
import { uniq } from 'lodash-es';
|
|
3
4
|
import validateUUID from 'uuid-validate';
|
|
4
5
|
import env from '../env.js';
|
|
5
|
-
import {
|
|
6
|
+
import { ErrorCode } from '../errors/index.js';
|
|
6
7
|
import logger from '../logger.js';
|
|
7
8
|
import { getPermissions } from '../utils/get-permissions.js';
|
|
8
9
|
import { Url } from '../utils/url.js';
|
|
@@ -80,7 +81,7 @@ ${comment}
|
|
|
80
81
|
});
|
|
81
82
|
}
|
|
82
83
|
catch (err) {
|
|
83
|
-
if (err
|
|
84
|
+
if (isDirectusError(err, ErrorCode.Forbidden)) {
|
|
84
85
|
logger.warn(`User ${userID} doesn't have proper permissions to receive notification for this item.`);
|
|
85
86
|
}
|
|
86
87
|
else {
|
package/dist/services/assets.js
CHANGED
|
@@ -7,10 +7,7 @@ import validateUUID from 'uuid-validate';
|
|
|
7
7
|
import { SUPPORTED_IMAGE_TRANSFORM_FORMATS } from '../constants.js';
|
|
8
8
|
import getDatabase from '../database/index.js';
|
|
9
9
|
import env from '../env.js';
|
|
10
|
-
import {
|
|
11
|
-
import { IllegalAssetTransformation } from '../exceptions/illegal-asset-transformation.js';
|
|
12
|
-
import { RangeNotSatisfiableException } from '../exceptions/range-not-satisfiable.js';
|
|
13
|
-
import { ServiceUnavailableException } from '../exceptions/service-unavailable.js';
|
|
10
|
+
import { ForbiddenError, IllegalAssetTransformationError, RangeNotSatisfiableError, ServiceUnavailableError, } from '../errors/index.js';
|
|
14
11
|
import logger from '../logger.js';
|
|
15
12
|
import { getStorage } from '../storage/index.js';
|
|
16
13
|
import { getMilliseconds } from '../utils/get-milliseconds.js';
|
|
@@ -39,23 +36,23 @@ export class AssetsService {
|
|
|
39
36
|
*/
|
|
40
37
|
const isValidUUID = validateUUID(id, 4);
|
|
41
38
|
if (isValidUUID === false)
|
|
42
|
-
throw new
|
|
39
|
+
throw new ForbiddenError();
|
|
43
40
|
if (systemPublicKeys.includes(id) === false && this.accountability?.admin !== true) {
|
|
44
41
|
await this.authorizationService.checkAccess('read', 'directus_files', id);
|
|
45
42
|
}
|
|
46
43
|
const file = (await this.knex.select('*').from('directus_files').where({ id }).first());
|
|
47
44
|
if (!file)
|
|
48
|
-
throw new
|
|
45
|
+
throw new ForbiddenError();
|
|
49
46
|
const exists = await storage.location(file.storage).exists(file.filename_disk);
|
|
50
47
|
if (!exists)
|
|
51
|
-
throw new
|
|
48
|
+
throw new ForbiddenError();
|
|
52
49
|
if (range) {
|
|
53
50
|
const missingRangeLimits = range.start === undefined && range.end === undefined;
|
|
54
51
|
const endBeforeStart = range.start !== undefined && range.end !== undefined && range.end <= range.start;
|
|
55
52
|
const startOverflow = range.start !== undefined && range.start >= file.filesize;
|
|
56
53
|
const endUnderflow = range.end !== undefined && range.end <= 0;
|
|
57
54
|
if (missingRangeLimits || endBeforeStart || startOverflow || endUnderflow) {
|
|
58
|
-
throw new
|
|
55
|
+
throw new RangeNotSatisfiableError({ range });
|
|
59
56
|
}
|
|
60
57
|
const lastByte = file.filesize - 1;
|
|
61
58
|
if (range.end) {
|
|
@@ -106,12 +103,14 @@ export class AssetsService {
|
|
|
106
103
|
!height ||
|
|
107
104
|
width > env['ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION'] ||
|
|
108
105
|
height > env['ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION']) {
|
|
109
|
-
|
|
106
|
+
logger.warn(`Image is too large to be transformed, or image size couldn't be determined.`);
|
|
107
|
+
throw new IllegalAssetTransformationError({ invalidTransformations: ['width', 'height'] });
|
|
110
108
|
}
|
|
111
109
|
const { queue, process } = sharp.counters();
|
|
112
110
|
if (queue + process > env['ASSETS_TRANSFORM_MAX_CONCURRENT']) {
|
|
113
|
-
throw new
|
|
111
|
+
throw new ServiceUnavailableError({
|
|
114
112
|
service: 'files',
|
|
113
|
+
reason: 'Server too busy',
|
|
115
114
|
});
|
|
116
115
|
}
|
|
117
116
|
const readStream = await storage.location(file.storage).read(file.filename_disk, range);
|
|
@@ -7,7 +7,8 @@ import { DEFAULT_AUTH_PROVIDER } from '../constants.js';
|
|
|
7
7
|
import getDatabase from '../database/index.js';
|
|
8
8
|
import emitter from '../emitter.js';
|
|
9
9
|
import env from '../env.js';
|
|
10
|
-
import {
|
|
10
|
+
import { InvalidCredentialsError, InvalidProviderError, UserSuspendedError } from '../errors/index.js';
|
|
11
|
+
import { InvalidOtpError } from '../errors/index.js';
|
|
11
12
|
import { createRateLimiter } from '../rate-limiter.js';
|
|
12
13
|
import { getMilliseconds } from '../utils/get-milliseconds.js';
|
|
13
14
|
import { stall } from '../utils/stall.js';
|
|
@@ -76,16 +77,16 @@ export class AuthenticationService {
|
|
|
76
77
|
emitStatus('fail');
|
|
77
78
|
if (user?.status === 'suspended') {
|
|
78
79
|
await stall(STALL_TIME, timeStart);
|
|
79
|
-
throw new
|
|
80
|
+
throw new UserSuspendedError();
|
|
80
81
|
}
|
|
81
82
|
else {
|
|
82
83
|
await stall(STALL_TIME, timeStart);
|
|
83
|
-
throw new
|
|
84
|
+
throw new InvalidCredentialsError();
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
else if (user.provider !== providerName) {
|
|
87
88
|
await stall(STALL_TIME, timeStart);
|
|
88
|
-
throw new
|
|
89
|
+
throw new InvalidProviderError();
|
|
89
90
|
}
|
|
90
91
|
const settingsService = new SettingsService({
|
|
91
92
|
knex: this.knex,
|
|
@@ -117,7 +118,7 @@ export class AuthenticationService {
|
|
|
117
118
|
if (user.tfa_secret && !otp) {
|
|
118
119
|
emitStatus('fail');
|
|
119
120
|
await stall(STALL_TIME, timeStart);
|
|
120
|
-
throw new
|
|
121
|
+
throw new InvalidOtpError();
|
|
121
122
|
}
|
|
122
123
|
if (user.tfa_secret && otp) {
|
|
123
124
|
const tfaService = new TFAService({ knex: this.knex, schema: this.schema });
|
|
@@ -125,7 +126,7 @@ export class AuthenticationService {
|
|
|
125
126
|
if (otpValid === false) {
|
|
126
127
|
emitStatus('fail');
|
|
127
128
|
await stall(STALL_TIME, timeStart);
|
|
128
|
-
throw new
|
|
129
|
+
throw new InvalidOtpError();
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
132
|
const tokenPayload = {
|
|
@@ -188,7 +189,7 @@ export class AuthenticationService {
|
|
|
188
189
|
const STALL_TIME = env['LOGIN_STALL_TIME'];
|
|
189
190
|
const timeStart = performance.now();
|
|
190
191
|
if (!refreshToken) {
|
|
191
|
-
throw new
|
|
192
|
+
throw new InvalidCredentialsError();
|
|
192
193
|
}
|
|
193
194
|
const record = await this.knex
|
|
194
195
|
.select({
|
|
@@ -230,17 +231,17 @@ export class AuthenticationService {
|
|
|
230
231
|
})
|
|
231
232
|
.first();
|
|
232
233
|
if (!record || (!record.share_id && !record.user_id)) {
|
|
233
|
-
throw new
|
|
234
|
+
throw new InvalidCredentialsError();
|
|
234
235
|
}
|
|
235
236
|
if (record.user_id && record.user_status !== 'active') {
|
|
236
237
|
await this.knex('directus_sessions').where({ token: refreshToken }).del();
|
|
237
238
|
if (record.user_status === 'suspended') {
|
|
238
239
|
await stall(STALL_TIME, timeStart);
|
|
239
|
-
throw new
|
|
240
|
+
throw new UserSuspendedError();
|
|
240
241
|
}
|
|
241
242
|
else {
|
|
242
243
|
await stall(STALL_TIME, timeStart);
|
|
243
|
-
throw new
|
|
244
|
+
throw new InvalidCredentialsError();
|
|
244
245
|
}
|
|
245
246
|
}
|
|
246
247
|
if (record.user_id) {
|
|
@@ -330,7 +331,7 @@ export class AuthenticationService {
|
|
|
330
331
|
.where('id', userID)
|
|
331
332
|
.first();
|
|
332
333
|
if (!user) {
|
|
333
|
-
throw new
|
|
334
|
+
throw new InvalidCredentialsError();
|
|
334
335
|
}
|
|
335
336
|
const provider = getAuthProvider(user.provider);
|
|
336
337
|
await provider.verify(clone(user), password);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Accountability, PermissionsAction, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
|
-
import type {
|
|
3
|
+
import type { AST, AbstractServiceOptions, Item, PrimaryKey } from '../types/index.js';
|
|
4
4
|
import { PayloadService } from './payload.js';
|
|
5
5
|
export declare class AuthorizationService {
|
|
6
6
|
knex: Knex;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { FailedValidationException } from '@directus/exceptions';
|
|
2
1
|
import { validatePayload } from '@directus/utils';
|
|
2
|
+
import { FailedValidationError, joiValidationErrorItemToErrorExtensions } from '@directus/validation';
|
|
3
3
|
import { cloneDeep, flatten, isArray, isNil, merge, reduce, uniq, uniqWith } from 'lodash-es';
|
|
4
4
|
import { GENERATE_SPECIAL } from '../constants.js';
|
|
5
5
|
import getDatabase from '../database/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { ForbiddenError } from '../errors/forbidden.js';
|
|
7
7
|
import { getRelationInfo } from '../utils/get-relation-info.js';
|
|
8
8
|
import { stripFunction } from '../utils/strip-function.js';
|
|
9
9
|
import { ItemsService } from './items.js';
|
|
@@ -31,7 +31,7 @@ export class AuthorizationService {
|
|
|
31
31
|
// If the permissions don't match the collections, you don't have permission to read all of them
|
|
32
32
|
const uniqueCollectionsRequestedCount = uniq(collectionsRequested.map(({ collection }) => collection)).length;
|
|
33
33
|
if (uniqueCollectionsRequestedCount !== permissionsForCollections.length) {
|
|
34
|
-
throw new
|
|
34
|
+
throw new ForbiddenError();
|
|
35
35
|
}
|
|
36
36
|
validateFields(ast);
|
|
37
37
|
validateFilterPermissions(ast, this.schema, action, this.accountability);
|
|
@@ -94,7 +94,7 @@ export class AuthorizationService {
|
|
|
94
94
|
if (column === '*')
|
|
95
95
|
continue;
|
|
96
96
|
if (allowedFields.includes(column) === false)
|
|
97
|
-
throw new
|
|
97
|
+
throw new ForbiddenError();
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -107,7 +107,7 @@ export class AuthorizationService {
|
|
|
107
107
|
continue;
|
|
108
108
|
const fieldKey = stripFunction(childNode.name);
|
|
109
109
|
if (allowedFields.includes(fieldKey) === false) {
|
|
110
|
-
throw new
|
|
110
|
+
throw new ForbiddenError();
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -189,7 +189,7 @@ export class AuthorizationService {
|
|
|
189
189
|
});
|
|
190
190
|
// Filter key not found in parent collection
|
|
191
191
|
if (!relation)
|
|
192
|
-
throw new
|
|
192
|
+
throw new ForbiddenError();
|
|
193
193
|
const relatedCollectionName = relation.related_collection === parentCollection ? relation.collection : relation.related_collection;
|
|
194
194
|
// Add the `item` field to the required permissions
|
|
195
195
|
(result[relatedCollectionName] || (result[relatedCollectionName] = new Set())).add(field);
|
|
@@ -211,7 +211,7 @@ export class AuthorizationService {
|
|
|
211
211
|
});
|
|
212
212
|
// Filter key not found in parent collection
|
|
213
213
|
if (!relation)
|
|
214
|
-
throw new
|
|
214
|
+
throw new ForbiddenError();
|
|
215
215
|
parentCollection =
|
|
216
216
|
relation.related_collection === parentCollection ? relation.collection : relation.related_collection;
|
|
217
217
|
(result[parentCollection] || (result[parentCollection] = new Set())).add(filterKey);
|
|
@@ -262,14 +262,14 @@ export class AuthorizationService {
|
|
|
262
262
|
if (action !== 'read' && collection === rootCollection) {
|
|
263
263
|
const actionPermission = accountability?.permissions?.find((permission) => permission.collection === collection && permission.action === action);
|
|
264
264
|
if (!actionPermission || !actionPermission.fields) {
|
|
265
|
-
throw new
|
|
265
|
+
throw new ForbiddenError();
|
|
266
266
|
}
|
|
267
267
|
allowedFields = permission?.fields
|
|
268
268
|
? [...permission.fields, schema.collections[collection].primary]
|
|
269
269
|
: [schema.collections[collection].primary];
|
|
270
270
|
}
|
|
271
271
|
else if (!permission || !permission.fields) {
|
|
272
|
-
throw new
|
|
272
|
+
throw new ForbiddenError();
|
|
273
273
|
}
|
|
274
274
|
else {
|
|
275
275
|
allowedFields = permission.fields;
|
|
@@ -288,7 +288,7 @@ export class AuthorizationService {
|
|
|
288
288
|
originalFieldName = aliasMap[fieldName];
|
|
289
289
|
}
|
|
290
290
|
if (!allowedFields.includes(originalFieldName)) {
|
|
291
|
-
throw new
|
|
291
|
+
throw new ForbiddenError();
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
}
|
|
@@ -356,14 +356,14 @@ export class AuthorizationService {
|
|
|
356
356
|
return permission.collection === collection && permission.action === action;
|
|
357
357
|
});
|
|
358
358
|
if (!permission)
|
|
359
|
-
throw new
|
|
359
|
+
throw new ForbiddenError();
|
|
360
360
|
// Check if you have permission to access the fields you're trying to access
|
|
361
361
|
const allowedFields = permission.fields || [];
|
|
362
362
|
if (allowedFields.includes('*') === false) {
|
|
363
363
|
const keysInData = Object.keys(payload);
|
|
364
364
|
const invalidKeys = keysInData.filter((fieldKey) => allowedFields.includes(fieldKey) === false);
|
|
365
365
|
if (invalidKeys.length > 0) {
|
|
366
|
-
throw new
|
|
366
|
+
throw new ForbiddenError();
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
369
|
}
|
|
@@ -412,7 +412,7 @@ export class AuthorizationService {
|
|
|
412
412
|
}
|
|
413
413
|
}
|
|
414
414
|
const validationErrors = [];
|
|
415
|
-
validationErrors.push(...flatten(validatePayload(permission.validation, payloadWithPresets).map((error) => error.details.map((details) => new
|
|
415
|
+
validationErrors.push(...flatten(validatePayload(permission.validation, payloadWithPresets).map((error) => error.details.map((details) => new FailedValidationError(joiValidationErrorItemToErrorExtensions(details))))));
|
|
416
416
|
if (validationErrors.length > 0)
|
|
417
417
|
throw validationErrors;
|
|
418
418
|
return payloadWithPresets;
|
|
@@ -431,14 +431,14 @@ export class AuthorizationService {
|
|
|
431
431
|
if (Array.isArray(pk)) {
|
|
432
432
|
const result = await itemsService.readMany(pk, { ...query, limit: pk.length }, { permissionsAction: action });
|
|
433
433
|
if (!result)
|
|
434
|
-
throw new
|
|
434
|
+
throw new ForbiddenError();
|
|
435
435
|
if (result.length !== pk.length)
|
|
436
|
-
throw new
|
|
436
|
+
throw new ForbiddenError();
|
|
437
437
|
}
|
|
438
438
|
else {
|
|
439
439
|
const result = await itemsService.readOne(pk, query, { permissionsAction: action });
|
|
440
440
|
if (!result)
|
|
441
|
-
throw new
|
|
441
|
+
throw new ForbiddenError();
|
|
442
442
|
}
|
|
443
443
|
}
|
|
444
444
|
}
|
|
@@ -8,7 +8,7 @@ import getDatabase, { getSchemaInspector } from '../database/index.js';
|
|
|
8
8
|
import { systemCollectionRows } from '../database/system-data/collections/index.js';
|
|
9
9
|
import emitter from '../emitter.js';
|
|
10
10
|
import env from '../env.js';
|
|
11
|
-
import {
|
|
11
|
+
import { ForbiddenError, InvalidPayloadError } from '../errors/index.js';
|
|
12
12
|
import { FieldsService } from '../services/fields.js';
|
|
13
13
|
import { ItemsService } from '../services/items.js';
|
|
14
14
|
import { getSchema } from '../utils/get-schema.js';
|
|
@@ -36,12 +36,12 @@ export class CollectionsService {
|
|
|
36
36
|
*/
|
|
37
37
|
async createOne(payload, opts) {
|
|
38
38
|
if (this.accountability && this.accountability.admin !== true) {
|
|
39
|
-
throw new
|
|
39
|
+
throw new ForbiddenError();
|
|
40
40
|
}
|
|
41
41
|
if (!payload.collection)
|
|
42
|
-
throw new
|
|
42
|
+
throw new InvalidPayloadError({ reason: `"collection" is required` });
|
|
43
43
|
if (payload.collection.startsWith('directus_')) {
|
|
44
|
-
throw new
|
|
44
|
+
throw new InvalidPayloadError({ reason: `Collections can't start with "directus_"` });
|
|
45
45
|
}
|
|
46
46
|
const nestedActionEvents = [];
|
|
47
47
|
try {
|
|
@@ -51,7 +51,7 @@ export class CollectionsService {
|
|
|
51
51
|
...Object.keys(this.schema.collections),
|
|
52
52
|
];
|
|
53
53
|
if (existingCollections.includes(payload.collection)) {
|
|
54
|
-
throw new
|
|
54
|
+
throw new InvalidPayloadError({ reason: `Collection "${payload.collection}" already exists` });
|
|
55
55
|
}
|
|
56
56
|
// Create the collection/fields in a transaction so it'll be reverted in case of errors or
|
|
57
57
|
// permission problems. This might not work reliably in MySQL, as it doesn't support DDL in
|
|
@@ -255,7 +255,7 @@ export class CollectionsService {
|
|
|
255
255
|
async readOne(collectionKey) {
|
|
256
256
|
const result = await this.readMany([collectionKey]);
|
|
257
257
|
if (result.length === 0)
|
|
258
|
-
throw new
|
|
258
|
+
throw new ForbiddenError();
|
|
259
259
|
return result[0];
|
|
260
260
|
}
|
|
261
261
|
/**
|
|
@@ -270,7 +270,7 @@ export class CollectionsService {
|
|
|
270
270
|
const collectionsYouHavePermissionToRead = permissions.map(({ collection }) => collection);
|
|
271
271
|
for (const collectionKey of collectionKeys) {
|
|
272
272
|
if (collectionsYouHavePermissionToRead.includes(collectionKey) === false) {
|
|
273
|
-
throw new
|
|
273
|
+
throw new ForbiddenError();
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
}
|
|
@@ -283,7 +283,7 @@ export class CollectionsService {
|
|
|
283
283
|
*/
|
|
284
284
|
async updateOne(collectionKey, data, opts) {
|
|
285
285
|
if (this.accountability && this.accountability.admin !== true) {
|
|
286
|
-
throw new
|
|
286
|
+
throw new ForbiddenError();
|
|
287
287
|
}
|
|
288
288
|
const nestedActionEvents = [];
|
|
289
289
|
try {
|
|
@@ -336,10 +336,10 @@ export class CollectionsService {
|
|
|
336
336
|
*/
|
|
337
337
|
async updateBatch(data, opts) {
|
|
338
338
|
if (this.accountability && this.accountability.admin !== true) {
|
|
339
|
-
throw new
|
|
339
|
+
throw new ForbiddenError();
|
|
340
340
|
}
|
|
341
341
|
if (!Array.isArray(data)) {
|
|
342
|
-
throw new
|
|
342
|
+
throw new InvalidPayloadError({ reason: 'Input should be an array of collection changes' });
|
|
343
343
|
}
|
|
344
344
|
const collectionKey = 'collection';
|
|
345
345
|
const collectionKeys = [];
|
|
@@ -352,8 +352,9 @@ export class CollectionsService {
|
|
|
352
352
|
schema: this.schema,
|
|
353
353
|
});
|
|
354
354
|
for (const payload of data) {
|
|
355
|
-
if (!payload[collectionKey])
|
|
356
|
-
throw new
|
|
355
|
+
if (!payload[collectionKey]) {
|
|
356
|
+
throw new InvalidPayloadError({ reason: `Collection in update misses collection key` });
|
|
357
|
+
}
|
|
357
358
|
await collectionItemsService.updateOne(payload[collectionKey], omit(payload, collectionKey), {
|
|
358
359
|
autoPurgeCache: false,
|
|
359
360
|
autoPurgeSystemCache: false,
|
|
@@ -385,7 +386,7 @@ export class CollectionsService {
|
|
|
385
386
|
*/
|
|
386
387
|
async updateMany(collectionKeys, data, opts) {
|
|
387
388
|
if (this.accountability && this.accountability.admin !== true) {
|
|
388
|
-
throw new
|
|
389
|
+
throw new ForbiddenError();
|
|
389
390
|
}
|
|
390
391
|
const nestedActionEvents = [];
|
|
391
392
|
try {
|
|
@@ -427,14 +428,14 @@ export class CollectionsService {
|
|
|
427
428
|
*/
|
|
428
429
|
async deleteOne(collectionKey, opts) {
|
|
429
430
|
if (this.accountability && this.accountability.admin !== true) {
|
|
430
|
-
throw new
|
|
431
|
+
throw new ForbiddenError();
|
|
431
432
|
}
|
|
432
433
|
const nestedActionEvents = [];
|
|
433
434
|
try {
|
|
434
435
|
const collections = await this.readByQuery();
|
|
435
436
|
const collectionToBeDeleted = collections.find((collection) => collection.collection === collectionKey);
|
|
436
437
|
if (!!collectionToBeDeleted === false) {
|
|
437
|
-
throw new
|
|
438
|
+
throw new ForbiddenError();
|
|
438
439
|
}
|
|
439
440
|
await this.knex.transaction(async (trx) => {
|
|
440
441
|
if (collectionToBeDeleted.schema) {
|
|
@@ -531,7 +532,7 @@ export class CollectionsService {
|
|
|
531
532
|
*/
|
|
532
533
|
async deleteMany(collectionKeys, opts) {
|
|
533
534
|
if (this.accountability && this.accountability.admin !== true) {
|
|
534
|
-
throw new
|
|
535
|
+
throw new ForbiddenError();
|
|
535
536
|
}
|
|
536
537
|
const nestedActionEvents = [];
|
|
537
538
|
try {
|
package/dist/services/fields.js
CHANGED
|
@@ -4,12 +4,12 @@ import { addFieldFlag, toArray } from '@directus/utils';
|
|
|
4
4
|
import { isEqual, isNil } from 'lodash-es';
|
|
5
5
|
import { clearSystemCache, getCache } from '../cache.js';
|
|
6
6
|
import { ALIAS_TYPES } from '../constants.js';
|
|
7
|
+
import { translateDatabaseError } from '../database/errors/translate.js';
|
|
7
8
|
import { getHelpers } from '../database/helpers/index.js';
|
|
8
9
|
import getDatabase, { getSchemaInspector } from '../database/index.js';
|
|
9
10
|
import { systemFieldRows } from '../database/system-data/fields/index.js';
|
|
10
11
|
import emitter from '../emitter.js';
|
|
11
|
-
import {
|
|
12
|
-
import { ForbiddenException, InvalidPayloadException } from '../exceptions/index.js';
|
|
12
|
+
import { ForbiddenError, InvalidPayloadError } from '../errors/index.js';
|
|
13
13
|
import { ItemsService } from '../services/items.js';
|
|
14
14
|
import { PayloadService } from '../services/payload.js';
|
|
15
15
|
import getDefaultValue from '../utils/get-default-value.js';
|
|
@@ -48,7 +48,7 @@ export class FieldsService {
|
|
|
48
48
|
async readAll(collection) {
|
|
49
49
|
let fields;
|
|
50
50
|
if (this.accountability && this.accountability.admin !== true && this.hasReadAccess === false) {
|
|
51
|
-
throw new
|
|
51
|
+
throw new ForbiddenError();
|
|
52
52
|
}
|
|
53
53
|
const nonAuthorizedItemsService = new ItemsService('directus_fields', {
|
|
54
54
|
knex: this.knex,
|
|
@@ -125,7 +125,7 @@ export class FieldsService {
|
|
|
125
125
|
allowedFieldsInCollection[permission.collection] = permission.fields ?? [];
|
|
126
126
|
});
|
|
127
127
|
if (collection && collection in allowedFieldsInCollection === false) {
|
|
128
|
-
throw new
|
|
128
|
+
throw new ForbiddenError();
|
|
129
129
|
}
|
|
130
130
|
return result.filter((field) => {
|
|
131
131
|
if (field.collection in allowedFieldsInCollection === false)
|
|
@@ -151,17 +151,17 @@ export class FieldsService {
|
|
|
151
151
|
async readOne(collection, field) {
|
|
152
152
|
if (this.accountability && this.accountability.admin !== true) {
|
|
153
153
|
if (this.hasReadAccess === false) {
|
|
154
|
-
throw new
|
|
154
|
+
throw new ForbiddenError();
|
|
155
155
|
}
|
|
156
156
|
const permissions = this.accountability.permissions.find((permission) => {
|
|
157
157
|
return permission.action === 'read' && permission.collection === collection;
|
|
158
158
|
});
|
|
159
159
|
if (!permissions || !permissions.fields)
|
|
160
|
-
throw new
|
|
160
|
+
throw new ForbiddenError();
|
|
161
161
|
if (permissions.fields.includes('*') === false) {
|
|
162
162
|
const allowedFields = permissions.fields;
|
|
163
163
|
if (allowedFields.includes(field) === false)
|
|
164
|
-
throw new
|
|
164
|
+
throw new ForbiddenError();
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
let column = undefined;
|
|
@@ -179,7 +179,7 @@ export class FieldsService {
|
|
|
179
179
|
// Do nothing
|
|
180
180
|
}
|
|
181
181
|
if (!column && !fieldInfo)
|
|
182
|
-
throw new
|
|
182
|
+
throw new ForbiddenError();
|
|
183
183
|
const type = getLocalType(column, fieldInfo);
|
|
184
184
|
const columnWithCastDefaultValue = column
|
|
185
185
|
? {
|
|
@@ -199,7 +199,7 @@ export class FieldsService {
|
|
|
199
199
|
async createField(collection, field, table, // allows collection creation to
|
|
200
200
|
opts) {
|
|
201
201
|
if (this.accountability && this.accountability.admin !== true) {
|
|
202
|
-
throw new
|
|
202
|
+
throw new ForbiddenError();
|
|
203
203
|
}
|
|
204
204
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
205
205
|
const nestedActionEvents = [];
|
|
@@ -208,7 +208,9 @@ export class FieldsService {
|
|
|
208
208
|
isNil(await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()) === false;
|
|
209
209
|
// Check if field already exists, either as a column, or as a row in directus_fields
|
|
210
210
|
if (exists) {
|
|
211
|
-
throw new
|
|
211
|
+
throw new InvalidPayloadError({
|
|
212
|
+
reason: `Field "${field.field}" already exists in collection "${collection}"`,
|
|
213
|
+
});
|
|
212
214
|
}
|
|
213
215
|
// Add flag for specific database type overrides
|
|
214
216
|
const flagToAdd = this.helpers.date.fieldFlagForField(field.type);
|
|
@@ -287,7 +289,7 @@ export class FieldsService {
|
|
|
287
289
|
}
|
|
288
290
|
async updateField(collection, field, opts) {
|
|
289
291
|
if (this.accountability && this.accountability.admin !== true) {
|
|
290
|
-
throw new
|
|
292
|
+
throw new ForbiddenError();
|
|
291
293
|
}
|
|
292
294
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
293
295
|
const nestedActionEvents = [];
|
|
@@ -307,7 +309,7 @@ export class FieldsService {
|
|
|
307
309
|
(hookAdjustedField.type === 'alias' ||
|
|
308
310
|
this.schema.collections[collection].fields[field.field]?.type === 'alias') &&
|
|
309
311
|
hookAdjustedField.type !== (this.schema.collections[collection].fields[field.field]?.type ?? 'alias')) {
|
|
310
|
-
throw new
|
|
312
|
+
throw new InvalidPayloadError({ reason: 'Alias type cannot be changed' });
|
|
311
313
|
}
|
|
312
314
|
if (hookAdjustedField.schema) {
|
|
313
315
|
const existingColumn = await this.schemaInspector.columnInfo(collection, hookAdjustedField.field);
|
|
@@ -384,7 +386,7 @@ export class FieldsService {
|
|
|
384
386
|
}
|
|
385
387
|
async deleteField(collection, field, opts) {
|
|
386
388
|
if (this.accountability && this.accountability.admin !== true) {
|
|
387
|
-
throw new
|
|
389
|
+
throw new ForbiddenError();
|
|
388
390
|
}
|
|
389
391
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
390
392
|
const nestedActionEvents = [];
|
|
@@ -551,7 +553,7 @@ export class FieldsService {
|
|
|
551
553
|
column = table[field.type](field.field);
|
|
552
554
|
}
|
|
553
555
|
else {
|
|
554
|
-
throw new
|
|
556
|
+
throw new InvalidPayloadError({ reason: `Illegal type passed: "${field.type}"` });
|
|
555
557
|
}
|
|
556
558
|
if (field.schema?.default_value !== undefined) {
|
|
557
559
|
if (typeof field.schema.default_value === 'string' &&
|