directus 9.5.2 → 9.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +3 -1
- package/dist/auth/drivers/ldap.d.ts +0 -1
- package/dist/auth/drivers/ldap.js +54 -60
- package/dist/auth/drivers/oauth2.js +3 -0
- package/dist/auth/drivers/openid.js +3 -0
- package/dist/cache.d.ts +4 -1
- package/dist/cache.js +27 -5
- package/dist/cli/commands/schema/apply.d.ts +1 -0
- package/dist/cli/commands/schema/apply.js +9 -5
- package/dist/cli/index.js +1 -0
- package/dist/cli/utils/create-env/env-stub.liquid +1 -0
- package/dist/controllers/assets.js +9 -1
- package/dist/controllers/utils.js +18 -1
- package/dist/database/index.js +0 -3
- package/dist/database/migrations/20220303A-remove-default-project-color.d.ts +3 -0
- package/dist/database/migrations/20220303A-remove-default-project-color.js +22 -0
- package/dist/database/migrations/20220314A-add-translation-strings.d.ts +3 -0
- package/dist/database/migrations/20220314A-add-translation-strings.js +15 -0
- package/dist/database/migrations/20220322A-rename-field-typecast-flags.d.ts +3 -0
- package/dist/database/migrations/20220322A-rename-field-typecast-flags.js +73 -0
- package/dist/database/migrations/run.js +1 -1
- package/dist/database/run-ast.d.ts +1 -1
- package/dist/database/run-ast.js +48 -35
- package/dist/database/system-data/fields/collections.yaml +4 -4
- package/dist/database/system-data/fields/fields.yaml +8 -8
- package/dist/database/system-data/fields/files.yaml +2 -2
- package/dist/database/system-data/fields/panels.yaml +2 -2
- package/dist/database/system-data/fields/permissions.yaml +4 -4
- package/dist/database/system-data/fields/presets.yaml +3 -3
- package/dist/database/system-data/fields/relations.yaml +1 -1
- package/dist/database/system-data/fields/revisions.yaml +2 -2
- package/dist/database/system-data/fields/roles.yaml +4 -4
- package/dist/database/system-data/fields/settings.yaml +25 -6
- package/dist/database/system-data/fields/users.yaml +2 -2
- package/dist/database/system-data/fields/webhooks.yaml +4 -4
- package/dist/env.js +9 -2
- package/dist/exceptions/database/dialects/mysql.js +23 -17
- package/dist/extensions.js +0 -2
- package/dist/middleware/authenticate.d.ts +5 -3
- package/dist/middleware/authenticate.js +22 -39
- package/dist/middleware/respond.js +7 -28
- package/dist/server.js +5 -2
- package/dist/services/authorization.js +60 -1
- package/dist/services/collections.js +6 -6
- package/dist/services/fields.js +3 -3
- package/dist/services/files.js +69 -3
- package/dist/services/graphql.js +2 -2
- package/dist/services/import-export.d.ts +34 -0
- package/dist/services/import-export.js +270 -0
- package/dist/services/index.d.ts +2 -1
- package/dist/services/index.js +2 -1
- package/dist/services/items.js +1 -0
- package/dist/services/mail/templates/base.liquid +2 -2
- package/dist/services/payload.js +3 -3
- package/dist/services/permissions.js +10 -10
- package/dist/services/relations.js +7 -4
- package/dist/services/utils.js +10 -0
- package/dist/utils/apply-query.js +2 -24
- package/dist/utils/get-date-formatted.d.ts +1 -0
- package/dist/utils/get-date-formatted.js +14 -0
- package/dist/utils/get-local-type.js +2 -2
- package/dist/utils/get-permissions.js +1 -1
- package/dist/utils/get-schema.js +1 -1
- package/dist/utils/is-directus-jwt.js +4 -21
- package/dist/utils/jwt.d.ts +2 -0
- package/dist/utils/jwt.js +49 -0
- package/dist/utils/merge-permissions.js +18 -6
- package/example.env +1 -0
- package/package.json +17 -18
- package/dist/__mocks__/cache.d.ts +0 -6
- package/dist/__mocks__/cache.js +0 -8
- package/dist/services/import.d.ts +0 -13
- package/dist/services/import.js +0 -118
|
@@ -38,6 +38,7 @@ function extractError(error) {
|
|
|
38
38
|
}
|
|
39
39
|
exports.extractError = extractError;
|
|
40
40
|
function uniqueViolation(error) {
|
|
41
|
+
var _a, _b, _c, _d, _e;
|
|
41
42
|
const betweenQuotes = /'([^']+)'/g;
|
|
42
43
|
const matches = error.sqlMessage.match(betweenQuotes);
|
|
43
44
|
if (!matches)
|
|
@@ -49,13 +50,13 @@ function uniqueViolation(error) {
|
|
|
49
50
|
*/
|
|
50
51
|
/** MySQL 8+ style error message */
|
|
51
52
|
if (matches[1].includes('.')) {
|
|
52
|
-
const collection = matches[1].slice(1, -1).split('.')[0];
|
|
53
|
+
const collection = (_a = matches[1]) === null || _a === void 0 ? void 0 : _a.slice(1, -1).split('.')[0];
|
|
53
54
|
let field = null;
|
|
54
|
-
const indexName = matches[1].slice(1, -1).split('.')[1];
|
|
55
|
+
const indexName = (_b = matches[1]) === null || _b === void 0 ? void 0 : _b.slice(1, -1).split('.')[1];
|
|
55
56
|
if ((indexName === null || indexName === void 0 ? void 0 : indexName.startsWith(`${collection}_`)) && indexName.endsWith('_unique')) {
|
|
56
|
-
field = indexName.slice(collection.length + 1, -7);
|
|
57
|
+
field = indexName === null || indexName === void 0 ? void 0 : indexName.slice(collection.length + 1, -7);
|
|
57
58
|
}
|
|
58
|
-
const invalid = matches[0].slice(1, -1);
|
|
59
|
+
const invalid = (_c = matches[0]) === null || _c === void 0 ? void 0 : _c.slice(1, -1);
|
|
59
60
|
return new record_not_unique_1.RecordNotUniqueException(field, {
|
|
60
61
|
collection,
|
|
61
62
|
field,
|
|
@@ -64,13 +65,13 @@ function uniqueViolation(error) {
|
|
|
64
65
|
}
|
|
65
66
|
else {
|
|
66
67
|
/** MySQL 5.7 style error message */
|
|
67
|
-
const indexName = matches[1].slice(1, -1);
|
|
68
|
+
const indexName = (_d = matches[1]) === null || _d === void 0 ? void 0 : _d.slice(1, -1);
|
|
68
69
|
const collection = indexName.split('_')[0];
|
|
69
70
|
let field = null;
|
|
70
71
|
if ((indexName === null || indexName === void 0 ? void 0 : indexName.startsWith(`${collection}_`)) && indexName.endsWith('_unique')) {
|
|
71
|
-
field = indexName.slice(collection.length + 1, -7);
|
|
72
|
+
field = indexName === null || indexName === void 0 ? void 0 : indexName.slice(collection.length + 1, -7);
|
|
72
73
|
}
|
|
73
|
-
const invalid = matches[0].slice(1, -1);
|
|
74
|
+
const invalid = (_e = matches[0]) === null || _e === void 0 ? void 0 : _e.slice(1, -1);
|
|
74
75
|
return new record_not_unique_1.RecordNotUniqueException(field, {
|
|
75
76
|
collection,
|
|
76
77
|
field,
|
|
@@ -79,57 +80,61 @@ function uniqueViolation(error) {
|
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
function numericValueOutOfRange(error) {
|
|
83
|
+
var _a, _b;
|
|
82
84
|
const betweenTicks = /`([^`]+)`/g;
|
|
83
85
|
const betweenQuotes = /'([^']+)'/g;
|
|
84
86
|
const tickMatches = error.sql.match(betweenTicks);
|
|
85
87
|
const quoteMatches = error.sqlMessage.match(betweenQuotes);
|
|
86
88
|
if (!tickMatches || !quoteMatches)
|
|
87
89
|
return error;
|
|
88
|
-
const collection = tickMatches[0].slice(1, -1);
|
|
89
|
-
const field = quoteMatches[0].slice(1, -1);
|
|
90
|
+
const collection = (_a = tickMatches[0]) === null || _a === void 0 ? void 0 : _a.slice(1, -1);
|
|
91
|
+
const field = (_b = quoteMatches[0]) === null || _b === void 0 ? void 0 : _b.slice(1, -1);
|
|
90
92
|
return new value_out_of_range_1.ValueOutOfRangeException(field, {
|
|
91
93
|
collection,
|
|
92
94
|
field,
|
|
93
95
|
});
|
|
94
96
|
}
|
|
95
97
|
function valueLimitViolation(error) {
|
|
98
|
+
var _a, _b;
|
|
96
99
|
const betweenTicks = /`([^`]+)`/g;
|
|
97
100
|
const betweenQuotes = /'([^']+)'/g;
|
|
98
101
|
const tickMatches = error.sql.match(betweenTicks);
|
|
99
102
|
const quoteMatches = error.sqlMessage.match(betweenQuotes);
|
|
100
103
|
if (!tickMatches || !quoteMatches)
|
|
101
104
|
return error;
|
|
102
|
-
const collection = tickMatches[0].slice(1, -1);
|
|
103
|
-
const field = quoteMatches[0].slice(1, -1);
|
|
105
|
+
const collection = (_a = tickMatches[0]) === null || _a === void 0 ? void 0 : _a.slice(1, -1);
|
|
106
|
+
const field = (_b = quoteMatches[0]) === null || _b === void 0 ? void 0 : _b.slice(1, -1);
|
|
104
107
|
return new value_too_long_1.ValueTooLongException(field, {
|
|
105
108
|
collection,
|
|
106
109
|
field,
|
|
107
110
|
});
|
|
108
111
|
}
|
|
109
112
|
function notNullViolation(error) {
|
|
113
|
+
var _a, _b;
|
|
110
114
|
const betweenTicks = /`([^`]+)`/g;
|
|
111
115
|
const betweenQuotes = /'([^']+)'/g;
|
|
112
116
|
const tickMatches = error.sql.match(betweenTicks);
|
|
113
117
|
const quoteMatches = error.sqlMessage.match(betweenQuotes);
|
|
114
118
|
if (!tickMatches || !quoteMatches)
|
|
115
119
|
return error;
|
|
116
|
-
const collection = tickMatches[0].slice(1, -1);
|
|
117
|
-
const field = quoteMatches[0].slice(1, -1);
|
|
120
|
+
const collection = (_a = tickMatches[0]) === null || _a === void 0 ? void 0 : _a.slice(1, -1);
|
|
121
|
+
const field = (_b = quoteMatches[0]) === null || _b === void 0 ? void 0 : _b.slice(1, -1);
|
|
118
122
|
return new not_null_violation_1.NotNullViolationException(field, {
|
|
119
123
|
collection,
|
|
120
124
|
field,
|
|
121
125
|
});
|
|
122
126
|
}
|
|
123
127
|
function foreignKeyViolation(error) {
|
|
128
|
+
var _a, _b, _c;
|
|
124
129
|
const betweenTicks = /`([^`]+)`/g;
|
|
125
130
|
const betweenParens = /\(([^)]+)\)/g;
|
|
126
131
|
const tickMatches = error.sqlMessage.match(betweenTicks);
|
|
127
132
|
const parenMatches = error.sql.match(betweenParens);
|
|
128
133
|
if (!tickMatches || !parenMatches)
|
|
129
134
|
return error;
|
|
130
|
-
const collection = tickMatches[1].slice(1, -1);
|
|
131
|
-
const field = tickMatches[3].slice(1, -1);
|
|
132
|
-
const invalid = parenMatches[1].slice(1, -1);
|
|
135
|
+
const collection = (_a = tickMatches[1]) === null || _a === void 0 ? void 0 : _a.slice(1, -1);
|
|
136
|
+
const field = (_b = tickMatches[3]) === null || _b === void 0 ? void 0 : _b.slice(1, -1);
|
|
137
|
+
const invalid = (_c = parenMatches[1]) === null || _c === void 0 ? void 0 : _c.slice(1, -1);
|
|
133
138
|
return new invalid_foreign_key_1.InvalidForeignKeyException(field, {
|
|
134
139
|
collection,
|
|
135
140
|
field,
|
|
@@ -137,12 +142,13 @@ function foreignKeyViolation(error) {
|
|
|
137
142
|
});
|
|
138
143
|
}
|
|
139
144
|
function containsNullValues(error) {
|
|
145
|
+
var _a;
|
|
140
146
|
const betweenTicks = /`([^`]+)`/g;
|
|
141
147
|
// Normally, we shouldn't read from the executed SQL. In this case, we're altering a single
|
|
142
148
|
// column, so we shouldn't have the problem where multiple columns are altered at the same time
|
|
143
149
|
const tickMatches = error.sql.match(betweenTicks);
|
|
144
150
|
if (!tickMatches)
|
|
145
151
|
return error;
|
|
146
|
-
const field = tickMatches[1].slice(1, -1);
|
|
152
|
+
const field = (_a = tickMatches[1]) === null || _a === void 0 ? void 0 : _a.slice(1, -1);
|
|
147
153
|
return new contains_null_values_1.ContainsNullValuesException(field);
|
|
148
154
|
}
|
package/dist/extensions.js
CHANGED
|
@@ -38,8 +38,6 @@ const get_schema_1 = require("./utils/get-schema");
|
|
|
38
38
|
const services = __importStar(require("./services"));
|
|
39
39
|
const node_cron_1 = require("node-cron");
|
|
40
40
|
const rollup_1 = require("rollup");
|
|
41
|
-
// @TODO Remove this once a new version of @rollup/plugin-virtual has been released
|
|
42
|
-
// @ts-expect-error
|
|
43
41
|
const plugin_virtual_1 = __importDefault(require("@rollup/plugin-virtual"));
|
|
44
42
|
const plugin_alias_1 = __importDefault(require("@rollup/plugin-alias"));
|
|
45
43
|
const url_1 = require("./utils/url");
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/// <reference types="qs" />
|
|
2
|
+
import { NextFunction, Request, Response } from 'express';
|
|
2
3
|
/**
|
|
3
4
|
* Verify the passed JWT and assign the user ID and role to `req`
|
|
4
5
|
*/
|
|
5
|
-
declare const
|
|
6
|
-
|
|
6
|
+
export declare const handler: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
7
|
+
declare const _default: import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
|
|
8
|
+
export default _default;
|
|
@@ -1,39 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
-
}) : (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
o[k2] = m[k];
|
|
8
|
-
}));
|
|
9
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
-
}) : function(o, v) {
|
|
12
|
-
o["default"] = v;
|
|
13
|
-
});
|
|
14
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
-
if (mod && mod.__esModule) return mod;
|
|
16
|
-
var result = {};
|
|
17
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
-
__setModuleDefault(result, mod);
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
21
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
4
|
};
|
|
24
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
|
|
6
|
+
exports.handler = void 0;
|
|
7
|
+
const lodash_1 = require("lodash");
|
|
26
8
|
const database_1 = __importDefault(require("../database"));
|
|
9
|
+
const emitter_1 = __importDefault(require("../emitter"));
|
|
27
10
|
const env_1 = __importDefault(require("../env"));
|
|
28
11
|
const exceptions_1 = require("../exceptions");
|
|
29
12
|
const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
30
13
|
const get_ip_from_req_1 = require("../utils/get-ip-from-req");
|
|
31
14
|
const is_directus_jwt_1 = __importDefault(require("../utils/is-directus-jwt"));
|
|
15
|
+
const jwt_1 = require("../utils/jwt");
|
|
32
16
|
/**
|
|
33
17
|
* Verify the passed JWT and assign the user ID and role to `req`
|
|
34
18
|
*/
|
|
35
|
-
const
|
|
36
|
-
|
|
19
|
+
const handler = async (req, res, next) => {
|
|
20
|
+
const defaultAccountability = {
|
|
37
21
|
user: null,
|
|
38
22
|
role: null,
|
|
39
23
|
admin: false,
|
|
@@ -42,23 +26,21 @@ const authenticate = (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
42
26
|
userAgent: req.get('user-agent'),
|
|
43
27
|
};
|
|
44
28
|
const database = (0, database_1.default)();
|
|
29
|
+
const customAccountability = await emitter_1.default.emitFilter('authenticate', defaultAccountability, {
|
|
30
|
+
req,
|
|
31
|
+
}, {
|
|
32
|
+
database,
|
|
33
|
+
schema: null,
|
|
34
|
+
accountability: null,
|
|
35
|
+
});
|
|
36
|
+
if (customAccountability && (0, lodash_1.isEqual)(customAccountability, defaultAccountability) === false) {
|
|
37
|
+
req.accountability = customAccountability;
|
|
38
|
+
return next();
|
|
39
|
+
}
|
|
40
|
+
req.accountability = defaultAccountability;
|
|
45
41
|
if (req.token) {
|
|
46
42
|
if ((0, is_directus_jwt_1.default)(req.token)) {
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
payload = jsonwebtoken_1.default.verify(req.token, env_1.default.SECRET, { issuer: 'directus' });
|
|
50
|
-
}
|
|
51
|
-
catch (err) {
|
|
52
|
-
if (err instanceof jsonwebtoken_1.TokenExpiredError) {
|
|
53
|
-
throw new exceptions_1.InvalidCredentialsException('Token expired.');
|
|
54
|
-
}
|
|
55
|
-
else if (err instanceof jsonwebtoken_1.JsonWebTokenError) {
|
|
56
|
-
throw new exceptions_1.InvalidCredentialsException('Token invalid.');
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
throw err;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
43
|
+
const payload = (0, jwt_1.verifyAccessJWT)(req.token, env_1.default.SECRET);
|
|
62
44
|
req.accountability.share = payload.share;
|
|
63
45
|
req.accountability.share_scope = payload.share_scope;
|
|
64
46
|
req.accountability.user = payload.id;
|
|
@@ -87,5 +69,6 @@ const authenticate = (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
87
69
|
}
|
|
88
70
|
}
|
|
89
71
|
return next();
|
|
90
|
-
}
|
|
91
|
-
exports.
|
|
72
|
+
};
|
|
73
|
+
exports.handler = handler;
|
|
74
|
+
exports.default = (0, async_handler_1.default)(exports.handler);
|
|
@@ -4,16 +4,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.respond = void 0;
|
|
7
|
-
const json2csv_1 = require("json2csv");
|
|
8
7
|
const ms_1 = __importDefault(require("ms"));
|
|
9
|
-
const stream_1 = require("stream");
|
|
10
8
|
const cache_1 = require("../cache");
|
|
11
9
|
const env_1 = __importDefault(require("../env"));
|
|
12
10
|
const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
13
11
|
const get_cache_key_1 = require("../utils/get-cache-key");
|
|
14
|
-
const js2xmlparser_1 = require("js2xmlparser");
|
|
15
12
|
const get_cache_headers_1 = require("../utils/get-cache-headers");
|
|
16
13
|
const logger_1 = __importDefault(require("../logger"));
|
|
14
|
+
const services_1 = require("../services");
|
|
15
|
+
const get_date_formatted_1 = require("../utils/get-date-formatted");
|
|
17
16
|
exports.respond = (0, async_handler_1.default)(async (req, res) => {
|
|
18
17
|
var _a, _b, _c;
|
|
19
18
|
const { cache } = (0, cache_1.getCache)();
|
|
@@ -39,6 +38,7 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
|
|
|
39
38
|
res.setHeader('Vary', 'Origin, Cache-Control');
|
|
40
39
|
}
|
|
41
40
|
if (req.sanitizedQuery.export) {
|
|
41
|
+
const exportService = new services_1.ExportService({ accountability: req.accountability, schema: req.schema });
|
|
42
42
|
let filename = '';
|
|
43
43
|
if (req.collection) {
|
|
44
44
|
filename += req.collection;
|
|
@@ -46,32 +46,21 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
|
|
|
46
46
|
else {
|
|
47
47
|
filename += 'Export';
|
|
48
48
|
}
|
|
49
|
-
filename += ' ' + getDateFormatted();
|
|
49
|
+
filename += ' ' + (0, get_date_formatted_1.getDateFormatted)();
|
|
50
50
|
if (req.sanitizedQuery.export === 'json') {
|
|
51
51
|
res.attachment(`${filename}.json`);
|
|
52
52
|
res.set('Content-Type', 'application/json');
|
|
53
|
-
return res.status(200).send(
|
|
53
|
+
return res.status(200).send(exportService.transform((_a = res.locals.payload) === null || _a === void 0 ? void 0 : _a.data, 'json'));
|
|
54
54
|
}
|
|
55
55
|
if (req.sanitizedQuery.export === 'xml') {
|
|
56
56
|
res.attachment(`${filename}.xml`);
|
|
57
57
|
res.set('Content-Type', 'text/xml');
|
|
58
|
-
return res.status(200).send(
|
|
58
|
+
return res.status(200).send(exportService.transform((_b = res.locals.payload) === null || _b === void 0 ? void 0 : _b.data, 'xml'));
|
|
59
59
|
}
|
|
60
60
|
if (req.sanitizedQuery.export === 'csv') {
|
|
61
61
|
res.attachment(`${filename}.csv`);
|
|
62
62
|
res.set('Content-Type', 'text/csv');
|
|
63
|
-
|
|
64
|
-
if (!((_c = res.locals.payload) === null || _c === void 0 ? void 0 : _c.data) || res.locals.payload.data.length === 0) {
|
|
65
|
-
stream.end(Buffer.from(''));
|
|
66
|
-
return stream.pipe(res);
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
stream.end(Buffer.from(JSON.stringify(res.locals.payload.data), 'utf-8'));
|
|
70
|
-
const json2csv = new json2csv_1.Transform({
|
|
71
|
-
transforms: [json2csv_1.transforms.flatten({ separator: '.' })],
|
|
72
|
-
});
|
|
73
|
-
return stream.pipe(json2csv).pipe(res);
|
|
74
|
-
}
|
|
63
|
+
return res.status(200).send(exportService.transform((_c = res.locals.payload) === null || _c === void 0 ? void 0 : _c.data, 'csv'));
|
|
75
64
|
}
|
|
76
65
|
}
|
|
77
66
|
if (Buffer.isBuffer(res.locals.payload)) {
|
|
@@ -84,13 +73,3 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
|
|
|
84
73
|
return res.status(204).end();
|
|
85
74
|
}
|
|
86
75
|
});
|
|
87
|
-
function getDateFormatted() {
|
|
88
|
-
const date = new Date();
|
|
89
|
-
let month = String(date.getMonth() + 1);
|
|
90
|
-
if (month.length === 1)
|
|
91
|
-
month = '0' + month;
|
|
92
|
-
let day = String(date.getDate());
|
|
93
|
-
if (day.length === 1)
|
|
94
|
-
day = '0' + day;
|
|
95
|
-
return `${date.getFullYear()}-${month}-${day} at ${date.getHours()}.${date.getMinutes()}.${date.getSeconds()}`;
|
|
96
|
-
}
|
package/dist/server.js
CHANGED
|
@@ -36,8 +36,10 @@ const logger_1 = __importDefault(require("./logger"));
|
|
|
36
36
|
const emitter_1 = __importDefault(require("./emitter"));
|
|
37
37
|
const update_check_1 = __importDefault(require("update-check"));
|
|
38
38
|
const package_json_1 = __importDefault(require("../package.json"));
|
|
39
|
+
const get_config_from_env_1 = require("./utils/get-config-from-env");
|
|
39
40
|
async function createServer() {
|
|
40
41
|
const server = http.createServer(await (0, app_1.default)());
|
|
42
|
+
Object.assign(server, (0, get_config_from_env_1.getConfigFromEnv)('SERVER_'));
|
|
41
43
|
server.on('request', function (req, res) {
|
|
42
44
|
const startTime = process.hrtime();
|
|
43
45
|
const complete = (0, lodash_1.once)(function (finished) {
|
|
@@ -124,9 +126,10 @@ async function createServer() {
|
|
|
124
126
|
exports.createServer = createServer;
|
|
125
127
|
async function startServer() {
|
|
126
128
|
const server = await createServer();
|
|
129
|
+
const host = env_1.default.HOST;
|
|
127
130
|
const port = env_1.default.PORT;
|
|
128
131
|
server
|
|
129
|
-
.listen(port, () => {
|
|
132
|
+
.listen(port, host, () => {
|
|
130
133
|
(0, update_check_1.default)(package_json_1.default)
|
|
131
134
|
.then((update) => {
|
|
132
135
|
if (update) {
|
|
@@ -136,7 +139,7 @@ async function startServer() {
|
|
|
136
139
|
.catch(() => {
|
|
137
140
|
// No need to log/warn here. The update message is only an informative nice-to-have
|
|
138
141
|
});
|
|
139
|
-
logger_1.default.info(`Server started at http
|
|
142
|
+
logger_1.default.info(`Server started at http://${host}:${port}`);
|
|
140
143
|
emitter_1.default.emitAction('server.start', { server }, {
|
|
141
144
|
database: (0, database_1.default)(),
|
|
142
145
|
schema: null,
|
|
@@ -11,6 +11,7 @@ const exceptions_2 = require("@directus/shared/exceptions");
|
|
|
11
11
|
const utils_1 = require("@directus/shared/utils");
|
|
12
12
|
const items_1 = require("./items");
|
|
13
13
|
const payload_1 = require("./payload");
|
|
14
|
+
const strip_function_1 = require("../utils/strip-function");
|
|
14
15
|
class AuthorizationService {
|
|
15
16
|
constructor(options) {
|
|
16
17
|
this.knex = options.knex || (0, database_1.default)();
|
|
@@ -34,6 +35,7 @@ class AuthorizationService {
|
|
|
34
35
|
throw new exceptions_1.ForbiddenException();
|
|
35
36
|
}
|
|
36
37
|
validateFields(ast);
|
|
38
|
+
validateFilterPermissions(ast, this.schema, this.accountability);
|
|
37
39
|
applyFilters(ast, this.accountability);
|
|
38
40
|
return ast;
|
|
39
41
|
/**
|
|
@@ -97,13 +99,70 @@ class AuthorizationService {
|
|
|
97
99
|
}
|
|
98
100
|
if (allowedFields.includes('*'))
|
|
99
101
|
continue;
|
|
100
|
-
const fieldKey = childNode.name;
|
|
102
|
+
const fieldKey = (0, strip_function_1.stripFunction)(childNode.name);
|
|
101
103
|
if (allowedFields.includes(fieldKey) === false) {
|
|
102
104
|
throw new exceptions_1.ForbiddenException();
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
}
|
|
106
108
|
}
|
|
109
|
+
function validateFilterPermissions(ast, schema, accountability) {
|
|
110
|
+
var _a, _b, _c, _d, _e;
|
|
111
|
+
if (ast.type !== 'field') {
|
|
112
|
+
if (ast.type === 'a2o') {
|
|
113
|
+
for (const collection of Object.keys(ast.children)) {
|
|
114
|
+
checkFilter(collection, (_c = (_b = (_a = ast.query) === null || _a === void 0 ? void 0 : _a[collection]) === null || _b === void 0 ? void 0 : _b.filter) !== null && _c !== void 0 ? _c : {});
|
|
115
|
+
for (const child of ast.children[collection]) {
|
|
116
|
+
validateFilterPermissions(child, schema, accountability);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
checkFilter(ast.name, (_e = (_d = ast.query) === null || _d === void 0 ? void 0 : _d.filter) !== null && _e !== void 0 ? _e : {});
|
|
122
|
+
for (const child of ast.children) {
|
|
123
|
+
validateFilterPermissions(child, schema, accountability);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function checkFilter(collection, filter) {
|
|
128
|
+
var _a;
|
|
129
|
+
const permissions = (_a = accountability === null || accountability === void 0 ? void 0 : accountability.permissions) === null || _a === void 0 ? void 0 : _a.find((permission) => permission.collection === collection);
|
|
130
|
+
if (!permissions)
|
|
131
|
+
throw new exceptions_1.ForbiddenException();
|
|
132
|
+
const allowedFields = permissions.fields || [];
|
|
133
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
134
|
+
if (key.startsWith('_')) {
|
|
135
|
+
// Continue checking for _and and _or
|
|
136
|
+
if ((0, lodash_1.isArray)(value)) {
|
|
137
|
+
for (const val of value) {
|
|
138
|
+
checkFilter(collection, val);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
if (allowedFields.length !== 0 &&
|
|
144
|
+
allowedFields.includes('*') === false &&
|
|
145
|
+
allowedFields.includes(key) === false) {
|
|
146
|
+
throw new exceptions_1.ForbiddenException();
|
|
147
|
+
}
|
|
148
|
+
const relation = schema.relations.find((relation) => {
|
|
149
|
+
var _a;
|
|
150
|
+
return ((relation.collection === collection && relation.field === key) ||
|
|
151
|
+
(relation.related_collection === collection && ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) === key));
|
|
152
|
+
});
|
|
153
|
+
// Field is a relation
|
|
154
|
+
if (relation) {
|
|
155
|
+
if (relation.related_collection === collection) {
|
|
156
|
+
checkFilter(relation.collection, value);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
checkFilter(relation.related_collection, value);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
107
166
|
function applyFilters(ast, accountability) {
|
|
108
167
|
if (ast.type !== 'field') {
|
|
109
168
|
if (ast.type === 'a2o') {
|
|
@@ -129,7 +129,7 @@ class CollectionsService {
|
|
|
129
129
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
|
130
130
|
await this.cache.clear();
|
|
131
131
|
}
|
|
132
|
-
await
|
|
132
|
+
await (0, cache_1.clearSystemCache)();
|
|
133
133
|
return payload.collection;
|
|
134
134
|
}
|
|
135
135
|
/**
|
|
@@ -152,7 +152,7 @@ class CollectionsService {
|
|
|
152
152
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
|
153
153
|
await this.cache.clear();
|
|
154
154
|
}
|
|
155
|
-
await
|
|
155
|
+
await (0, cache_1.clearSystemCache)();
|
|
156
156
|
return collections;
|
|
157
157
|
}
|
|
158
158
|
/**
|
|
@@ -275,7 +275,7 @@ class CollectionsService {
|
|
|
275
275
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
|
276
276
|
await this.cache.clear();
|
|
277
277
|
}
|
|
278
|
-
await
|
|
278
|
+
await (0, cache_1.clearSystemCache)();
|
|
279
279
|
return collectionKey;
|
|
280
280
|
}
|
|
281
281
|
/**
|
|
@@ -298,7 +298,7 @@ class CollectionsService {
|
|
|
298
298
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
|
299
299
|
await this.cache.clear();
|
|
300
300
|
}
|
|
301
|
-
await
|
|
301
|
+
await (0, cache_1.clearSystemCache)();
|
|
302
302
|
return collectionKeys;
|
|
303
303
|
}
|
|
304
304
|
/**
|
|
@@ -379,7 +379,7 @@ class CollectionsService {
|
|
|
379
379
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
|
380
380
|
await this.cache.clear();
|
|
381
381
|
}
|
|
382
|
-
await
|
|
382
|
+
await (0, cache_1.clearSystemCache)();
|
|
383
383
|
return collectionKey;
|
|
384
384
|
}
|
|
385
385
|
/**
|
|
@@ -402,7 +402,7 @@ class CollectionsService {
|
|
|
402
402
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
|
403
403
|
await this.cache.clear();
|
|
404
404
|
}
|
|
405
|
-
await
|
|
405
|
+
await (0, cache_1.clearSystemCache)();
|
|
406
406
|
return collectionKeys;
|
|
407
407
|
}
|
|
408
408
|
}
|
package/dist/services/fields.js
CHANGED
|
@@ -249,7 +249,7 @@ class FieldsService {
|
|
|
249
249
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
250
250
|
await this.cache.clear();
|
|
251
251
|
}
|
|
252
|
-
await
|
|
252
|
+
await (0, cache_1.clearSystemCache)();
|
|
253
253
|
}
|
|
254
254
|
async updateField(collection, field) {
|
|
255
255
|
if (this.accountability && this.accountability.admin !== true) {
|
|
@@ -300,7 +300,7 @@ class FieldsService {
|
|
|
300
300
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
301
301
|
await this.cache.clear();
|
|
302
302
|
}
|
|
303
|
-
await
|
|
303
|
+
await (0, cache_1.clearSystemCache)();
|
|
304
304
|
emitter_1.default.emitAction(`fields.update`, {
|
|
305
305
|
payload: hookAdjustedField,
|
|
306
306
|
keys: [hookAdjustedField.field],
|
|
@@ -395,7 +395,7 @@ class FieldsService {
|
|
|
395
395
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
396
396
|
await this.cache.clear();
|
|
397
397
|
}
|
|
398
|
-
await
|
|
398
|
+
await (0, cache_1.clearSystemCache)();
|
|
399
399
|
emitter_1.default.emitAction('fields.delete', {
|
|
400
400
|
payload: [field],
|
|
401
401
|
collection: collection,
|
package/dist/services/files.js
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
2
21
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
22
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
23
|
};
|
|
@@ -11,7 +30,9 @@ const lodash_1 = require("lodash");
|
|
|
11
30
|
const mime_types_1 = require("mime-types");
|
|
12
31
|
const path_1 = __importDefault(require("path"));
|
|
13
32
|
const sharp_1 = __importDefault(require("sharp"));
|
|
14
|
-
const url_1 =
|
|
33
|
+
const url_1 = __importStar(require("url"));
|
|
34
|
+
const util_1 = require("util");
|
|
35
|
+
const dns_1 = require("dns");
|
|
15
36
|
const emitter_1 = __importDefault(require("../emitter"));
|
|
16
37
|
const env_1 = __importDefault(require("../env"));
|
|
17
38
|
const exceptions_1 = require("../exceptions");
|
|
@@ -19,6 +40,9 @@ const logger_1 = __importDefault(require("../logger"));
|
|
|
19
40
|
const storage_1 = __importDefault(require("../storage"));
|
|
20
41
|
const utils_1 = require("@directus/shared/utils");
|
|
21
42
|
const items_1 = require("./items");
|
|
43
|
+
const net_1 = __importDefault(require("net"));
|
|
44
|
+
const os_1 = __importDefault(require("os"));
|
|
45
|
+
const lookupDNS = (0, util_1.promisify)(dns_1.lookup);
|
|
22
46
|
class FilesService extends items_1.ItemsService {
|
|
23
47
|
constructor(options) {
|
|
24
48
|
super('directus_files', options);
|
|
@@ -137,6 +161,49 @@ class FilesService extends items_1.ItemsService {
|
|
|
137
161
|
if (this.accountability && ((_c = this.accountability) === null || _c === void 0 ? void 0 : _c.admin) !== true && !fileCreatePermissions) {
|
|
138
162
|
throw new exceptions_1.ForbiddenException();
|
|
139
163
|
}
|
|
164
|
+
let resolvedUrl;
|
|
165
|
+
try {
|
|
166
|
+
resolvedUrl = new url_1.URL(importURL);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
logger_1.default.warn(err, `Requested URL ${importURL} isn't a valid URL`);
|
|
170
|
+
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
171
|
+
service: 'external-file',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
let ip = resolvedUrl.hostname;
|
|
175
|
+
if (net_1.default.isIP(ip) === 0) {
|
|
176
|
+
try {
|
|
177
|
+
ip = (await lookupDNS(ip)).address;
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
logger_1.default.warn(err, `Couldn't lookup the DNS for url ${importURL}`);
|
|
181
|
+
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
182
|
+
service: 'external-file',
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (env_1.default.IMPORT_IP_DENY_LIST.includes('0.0.0.0')) {
|
|
187
|
+
const networkInterfaces = os_1.default.networkInterfaces();
|
|
188
|
+
for (const networkInfo of Object.values(networkInterfaces)) {
|
|
189
|
+
if (!networkInfo)
|
|
190
|
+
continue;
|
|
191
|
+
for (const info of networkInfo) {
|
|
192
|
+
if (info.address === ip) {
|
|
193
|
+
logger_1.default.warn(`Requested URL ${importURL} resolves to localhost.`);
|
|
194
|
+
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
195
|
+
service: 'external-file',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (env_1.default.IMPORT_IP_DENY_LIST.includes(ip)) {
|
|
202
|
+
logger_1.default.warn(`Requested URL ${importURL} resolves to a denied IP address.`);
|
|
203
|
+
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
204
|
+
service: 'external-file',
|
|
205
|
+
});
|
|
206
|
+
}
|
|
140
207
|
let fileResponse;
|
|
141
208
|
try {
|
|
142
209
|
fileResponse = await axios_1.default.get(importURL, {
|
|
@@ -144,8 +211,7 @@ class FilesService extends items_1.ItemsService {
|
|
|
144
211
|
});
|
|
145
212
|
}
|
|
146
213
|
catch (err) {
|
|
147
|
-
logger_1.default.warn(`Couldn't fetch file from url "${importURL}"`);
|
|
148
|
-
logger_1.default.warn(err);
|
|
214
|
+
logger_1.default.warn(err, `Couldn't fetch file from url "${importURL}"`);
|
|
149
215
|
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
150
216
|
service: 'external-file',
|
|
151
217
|
});
|
package/dist/services/graphql.js
CHANGED
|
@@ -1732,9 +1732,9 @@ class GraphQLService {
|
|
|
1732
1732
|
if (((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.admin) !== true) {
|
|
1733
1733
|
throw new exceptions_1.ForbiddenException();
|
|
1734
1734
|
}
|
|
1735
|
-
const { cache
|
|
1735
|
+
const { cache } = (0, cache_1.getCache)();
|
|
1736
1736
|
await (cache === null || cache === void 0 ? void 0 : cache.clear());
|
|
1737
|
-
await
|
|
1737
|
+
await (0, cache_1.clearSystemCache)();
|
|
1738
1738
|
return;
|
|
1739
1739
|
},
|
|
1740
1740
|
},
|