directus 9.15.1 → 9.17.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/__utils__/items-utils.d.ts +2 -0
- package/dist/__utils__/items-utils.js +36 -0
- package/dist/__utils__/schemas.d.ts +13 -0
- package/dist/__utils__/schemas.js +304 -0
- package/dist/__utils__/snapshots.d.ts +5 -0
- package/dist/__utils__/snapshots.js +897 -0
- package/dist/app.js +8 -7
- package/dist/auth/drivers/ldap.js +1 -0
- package/dist/auth/drivers/local.js +6 -0
- package/dist/auth/drivers/oauth2.js +1 -0
- package/dist/auth/drivers/openid.js +1 -0
- package/dist/cli/index.test.d.ts +1 -0
- package/dist/cli/index.test.js +58 -0
- package/dist/cli/utils/create-env/env-stub.liquid +6 -2
- package/dist/controllers/activity.js +1 -0
- package/dist/controllers/assets.js +20 -16
- package/dist/controllers/auth.js +6 -9
- package/dist/controllers/files.test.d.ts +1 -0
- package/dist/controllers/files.test.js +49 -0
- package/dist/controllers/server.js +0 -1
- package/dist/database/migrations/20220826A-add-origin-to-accountability.d.ts +3 -0
- package/dist/database/migrations/20220826A-add-origin-to-accountability.js +21 -0
- package/dist/database/migrations/run.test.d.ts +1 -0
- package/dist/database/migrations/run.test.js +92 -0
- package/dist/database/system-data/fields/activity.yaml +6 -0
- package/dist/database/system-data/fields/sessions.yaml +2 -0
- package/dist/env.js +15 -0
- package/dist/env.test.d.ts +8 -0
- package/dist/env.test.js +39 -0
- package/dist/extensions.d.ts +1 -0
- package/dist/extensions.js +16 -3
- package/dist/flows.js +28 -17
- package/dist/mailer.js +1 -0
- package/dist/middleware/authenticate.d.ts +1 -1
- package/dist/middleware/authenticate.js +1 -0
- package/dist/middleware/authenticate.test.d.ts +1 -0
- package/dist/middleware/authenticate.test.js +214 -0
- package/dist/middleware/extract-token.test.d.ts +1 -0
- package/dist/middleware/extract-token.test.js +60 -0
- package/dist/middleware/validate-batch.d.ts +1 -2
- package/dist/middleware/validate-batch.js +10 -13
- package/dist/middleware/validate-batch.test.d.ts +1 -0
- package/dist/middleware/validate-batch.test.js +82 -0
- package/dist/operations/exec/index.d.ts +5 -0
- package/dist/operations/exec/index.js +26 -0
- package/dist/operations/exec/index.test.d.ts +1 -0
- package/dist/operations/exec/index.test.js +95 -0
- package/dist/operations/notification/index.js +9 -6
- package/dist/operations/request/index.js +22 -3
- package/dist/operations/transform/index.d.ts +1 -1
- package/dist/operations/transform/index.js +1 -1
- package/dist/services/authentication.js +13 -3
- package/dist/services/files.js +3 -2
- package/dist/services/files.test.d.ts +1 -0
- package/dist/services/files.test.js +53 -0
- package/dist/services/flows.js +4 -0
- package/dist/services/graphql/index.d.ts +2 -2
- package/dist/services/graphql/index.js +78 -75
- package/dist/services/items.js +98 -42
- package/dist/services/items.test.d.ts +1 -0
- package/dist/services/items.test.js +765 -0
- package/dist/services/payload.d.ts +7 -4
- package/dist/services/payload.js +63 -12
- package/dist/services/payload.test.d.ts +1 -0
- package/dist/services/payload.test.js +94 -0
- package/dist/services/server.js +10 -7
- package/dist/services/shares.js +2 -1
- package/dist/services/specifications.test.d.ts +1 -0
- package/dist/services/specifications.test.js +96 -0
- package/dist/types/items.d.ts +11 -0
- package/dist/utils/apply-query.js +7 -3
- package/dist/utils/apply-snapshot.js +15 -0
- package/dist/utils/apply-snapshot.test.d.ts +1 -0
- package/dist/utils/apply-snapshot.test.js +305 -0
- package/dist/utils/async-handler.d.ts +2 -6
- package/dist/utils/async-handler.js +1 -13
- package/dist/utils/async-handler.test.d.ts +1 -0
- package/dist/utils/async-handler.test.js +18 -0
- package/dist/utils/calculate-field-depth.test.d.ts +1 -0
- package/dist/utils/calculate-field-depth.test.js +76 -0
- package/dist/utils/filter-items.test.d.ts +1 -0
- package/dist/utils/filter-items.test.js +60 -0
- package/dist/utils/get-cache-key.test.d.ts +1 -0
- package/dist/utils/get-cache-key.test.js +53 -0
- package/dist/utils/get-column-path.test.d.ts +1 -0
- package/dist/utils/get-column-path.test.js +136 -0
- package/dist/utils/get-config-from-env.test.d.ts +1 -0
- package/dist/utils/get-config-from-env.test.js +19 -0
- package/dist/utils/get-graphql-type.d.ts +1 -1
- package/dist/utils/get-graphql-type.js +4 -1
- package/dist/utils/get-os-info.d.ts +9 -0
- package/dist/utils/get-os-info.js +47 -0
- package/dist/utils/get-relation-info.test.d.ts +1 -0
- package/dist/utils/get-relation-info.test.js +88 -0
- package/dist/utils/get-relation-type.test.d.ts +1 -0
- package/dist/utils/get-relation-type.test.js +69 -0
- package/dist/utils/get-string-byte-size.test.d.ts +1 -0
- package/dist/utils/get-string-byte-size.test.js +8 -0
- package/dist/utils/is-directus-jwt.test.d.ts +1 -0
- package/dist/utils/is-directus-jwt.test.js +26 -0
- package/dist/utils/jwt.test.d.ts +1 -0
- package/dist/utils/jwt.test.js +36 -0
- package/dist/utils/merge-permissions.test.d.ts +1 -0
- package/dist/utils/merge-permissions.test.js +80 -0
- package/dist/utils/validate-keys.test.d.ts +1 -0
- package/dist/utils/validate-keys.test.js +97 -0
- package/package.json +14 -12
package/dist/app.js
CHANGED
|
@@ -135,6 +135,13 @@ async function createApp() {
|
|
|
135
135
|
await emitter_1.default.emitInit('app.before', { app });
|
|
136
136
|
await emitter_1.default.emitInit('middlewares.before', { app });
|
|
137
137
|
app.use(logger_1.expressLogger);
|
|
138
|
+
app.use((_req, res, next) => {
|
|
139
|
+
res.setHeader('X-Powered-By', 'Directus');
|
|
140
|
+
next();
|
|
141
|
+
});
|
|
142
|
+
if (env_1.default.CORS_ENABLED === true) {
|
|
143
|
+
app.use(cors_1.default);
|
|
144
|
+
}
|
|
138
145
|
app.use((req, res, next) => {
|
|
139
146
|
express_1.default.json({
|
|
140
147
|
limit: env_1.default.MAX_PAYLOAD_SIZE,
|
|
@@ -147,13 +154,6 @@ async function createApp() {
|
|
|
147
154
|
});
|
|
148
155
|
app.use((0, cookie_parser_1.default)());
|
|
149
156
|
app.use(extract_token_1.default);
|
|
150
|
-
app.use((_req, res, next) => {
|
|
151
|
-
res.setHeader('X-Powered-By', 'Directus');
|
|
152
|
-
next();
|
|
153
|
-
});
|
|
154
|
-
if (env_1.default.CORS_ENABLED === true) {
|
|
155
|
-
app.use(cors_1.default);
|
|
156
|
-
}
|
|
157
157
|
app.get('/', (_req, res, next) => {
|
|
158
158
|
if (env_1.default.ROOT_REDIRECT) {
|
|
159
159
|
res.redirect(env_1.default.ROOT_REDIRECT);
|
|
@@ -190,6 +190,7 @@ async function createApp() {
|
|
|
190
190
|
if (env_1.default.RATE_LIMITER_ENABLED === true) {
|
|
191
191
|
app.use(rate_limiter_1.default);
|
|
192
192
|
}
|
|
193
|
+
app.get('/server/ping', (req, res) => res.send('pong'));
|
|
193
194
|
app.use(authenticate_1.default);
|
|
194
195
|
app.use(check_ip_1.checkIP);
|
|
195
196
|
app.use(sanitize_query_1.default);
|
|
@@ -302,6 +302,7 @@ function createLDAPAuthRouter(provider) {
|
|
|
302
302
|
const accountability = {
|
|
303
303
|
ip: (0, get_ip_from_req_1.getIPFromReq)(req),
|
|
304
304
|
userAgent: req.get('user-agent'),
|
|
305
|
+
origin: req.get('origin'),
|
|
305
306
|
role: null,
|
|
306
307
|
};
|
|
307
308
|
const authenticationService = new services_1.AuthenticationService({
|
|
@@ -15,6 +15,8 @@ const env_1 = __importDefault(require("../../env"));
|
|
|
15
15
|
const respond_1 = require("../../middleware/respond");
|
|
16
16
|
const constants_1 = require("../../constants");
|
|
17
17
|
const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
|
|
18
|
+
const perf_hooks_1 = require("perf_hooks");
|
|
19
|
+
const stall_1 = require("../../utils/stall");
|
|
18
20
|
class LocalAuthDriver extends auth_1.AuthDriver {
|
|
19
21
|
async getUserID(payload) {
|
|
20
22
|
if (!payload.email) {
|
|
@@ -50,9 +52,12 @@ function createLocalAuthRouter(provider) {
|
|
|
50
52
|
}).unknown();
|
|
51
53
|
router.post('/', (0, async_handler_1.default)(async (req, res, next) => {
|
|
52
54
|
var _a;
|
|
55
|
+
const STALL_TIME = env_1.default.LOGIN_STALL_TIME;
|
|
56
|
+
const timeStart = perf_hooks_1.performance.now();
|
|
53
57
|
const accountability = {
|
|
54
58
|
ip: (0, get_ip_from_req_1.getIPFromReq)(req),
|
|
55
59
|
userAgent: req.get('user-agent'),
|
|
60
|
+
origin: req.get('origin'),
|
|
56
61
|
role: null,
|
|
57
62
|
};
|
|
58
63
|
const authenticationService = new services_1.AuthenticationService({
|
|
@@ -61,6 +66,7 @@ function createLocalAuthRouter(provider) {
|
|
|
61
66
|
});
|
|
62
67
|
const { error } = userLoginSchema.validate(req.body);
|
|
63
68
|
if (error) {
|
|
69
|
+
await (0, stall_1.stall)(STALL_TIME, timeStart);
|
|
64
70
|
throw new exceptions_1.InvalidPayloadException(error.message);
|
|
65
71
|
}
|
|
66
72
|
const mode = req.body.mode || 'json';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("./index");
|
|
4
|
+
jest.mock('../../src/env', () => ({
|
|
5
|
+
...jest.requireActual('../../src/env').default,
|
|
6
|
+
EXTENSIONS_PATH: '',
|
|
7
|
+
SERVE_APP: false,
|
|
8
|
+
DB_CLIENT: 'pg',
|
|
9
|
+
DB_HOST: 'localhost',
|
|
10
|
+
DB_PORT: 5432,
|
|
11
|
+
DB_DATABASE: 'directus',
|
|
12
|
+
DB_USER: 'postgres',
|
|
13
|
+
DB_PASSWORD: 'psql1234',
|
|
14
|
+
}));
|
|
15
|
+
jest.mock('@directus/shared/utils/node/get-extensions', () => ({
|
|
16
|
+
getPackageExtensions: jest.fn(() => Promise.resolve([])),
|
|
17
|
+
getLocalExtensions: jest.fn(() => Promise.resolve([customCliExtension])),
|
|
18
|
+
}));
|
|
19
|
+
jest.mock(`/hooks/custom-cli/index.js`, () => customCliHook, { virtual: true });
|
|
20
|
+
const customCliExtension = {
|
|
21
|
+
path: `/hooks/custom-cli`,
|
|
22
|
+
name: 'custom-cli',
|
|
23
|
+
type: 'hook',
|
|
24
|
+
entrypoint: 'index.js',
|
|
25
|
+
local: true,
|
|
26
|
+
};
|
|
27
|
+
const beforeHook = jest.fn();
|
|
28
|
+
const afterAction = jest.fn();
|
|
29
|
+
const afterHook = jest.fn(({ program }) => {
|
|
30
|
+
program.command('custom').action(afterAction);
|
|
31
|
+
});
|
|
32
|
+
const customCliHook = ({ init }) => {
|
|
33
|
+
init('cli.before', beforeHook);
|
|
34
|
+
init('cli.after', afterHook);
|
|
35
|
+
};
|
|
36
|
+
const writeOut = jest.fn();
|
|
37
|
+
const writeErr = jest.fn();
|
|
38
|
+
const setup = async () => {
|
|
39
|
+
const program = await (0, index_1.createCli)();
|
|
40
|
+
program.exitOverride();
|
|
41
|
+
program.configureOutput({ writeOut, writeErr });
|
|
42
|
+
return program;
|
|
43
|
+
};
|
|
44
|
+
beforeEach(jest.clearAllMocks);
|
|
45
|
+
describe('cli hooks', () => {
|
|
46
|
+
test('should call hooks before and after creating the cli', async () => {
|
|
47
|
+
const program = await setup();
|
|
48
|
+
expect(beforeHook).toHaveBeenCalledTimes(1);
|
|
49
|
+
expect(beforeHook).toHaveBeenCalledWith({ event: 'cli.before', program });
|
|
50
|
+
expect(afterHook).toHaveBeenCalledTimes(1);
|
|
51
|
+
expect(afterHook).toHaveBeenCalledWith({ event: 'cli.after', program });
|
|
52
|
+
});
|
|
53
|
+
test('should be able to add a custom cli command', async () => {
|
|
54
|
+
const program = await setup();
|
|
55
|
+
program.parseAsync(['custom'], { from: 'user' });
|
|
56
|
+
expect(afterAction).toHaveBeenCalledTimes(1);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -211,6 +211,10 @@ REFRESH_TOKEN_COOKIE_NAME="directus_refresh_token"
|
|
|
211
211
|
# Which domain to use for the refresh cookie. Useful for development mode.
|
|
212
212
|
# REFRESH_TOKEN_COOKIE_DOMAIN
|
|
213
213
|
|
|
214
|
+
# The duration in milliseconds that a login request will be stalled for,
|
|
215
|
+
# and it should be greater than the time taken for a login request with an invalid password [500]
|
|
216
|
+
# LOGIN_STALL_TIME=500
|
|
217
|
+
|
|
214
218
|
# Whether or not to enable the CORS headers [false]
|
|
215
219
|
CORS_ENABLED=true
|
|
216
220
|
|
|
@@ -296,10 +300,10 @@ EMAIL_SENDMAIL_PATH="/usr/sbin/sendmail"
|
|
|
296
300
|
## Email (Sendmail Transport)
|
|
297
301
|
|
|
298
302
|
# What new line style to use in sendmail ["unix"]
|
|
299
|
-
EMAIL_SENDMAIL_NEW_LINE="unix"
|
|
303
|
+
# EMAIL_SENDMAIL_NEW_LINE="unix"
|
|
300
304
|
|
|
301
305
|
# Path to your sendmail executable ["/usr/sbin/sendmail"]
|
|
302
|
-
EMAIL_SENDMAIL_PATH="/usr/sbin/sendmail"
|
|
306
|
+
# EMAIL_SENDMAIL_PATH="/usr/sbin/sendmail"
|
|
303
307
|
|
|
304
308
|
## Email (SMTP Transport)
|
|
305
309
|
# EMAIL_SMTP_HOST="localhost"
|
|
@@ -75,6 +75,7 @@ router.post('/comment', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
75
75
|
user: (_a = req.accountability) === null || _a === void 0 ? void 0 : _a.user,
|
|
76
76
|
ip: (0, get_ip_from_req_1.getIPFromReq)(req),
|
|
77
77
|
user_agent: req.get('user-agent'),
|
|
78
|
+
origin: req.get('origin'),
|
|
78
79
|
});
|
|
79
80
|
try {
|
|
80
81
|
const record = await service.readOne(primaryKey, req.sanitizedQuery);
|
|
@@ -148,27 +148,31 @@ router.get('/:pk/:filename?',
|
|
|
148
148
|
res.setHeader('Content-Length', stat.size);
|
|
149
149
|
return res.end();
|
|
150
150
|
}
|
|
151
|
-
|
|
151
|
+
let isDataSent = false;
|
|
152
|
+
stream.on('data', (chunk) => {
|
|
153
|
+
isDataSent = true;
|
|
154
|
+
res.write(chunk);
|
|
155
|
+
});
|
|
152
156
|
stream.on('end', () => {
|
|
153
|
-
res.
|
|
157
|
+
res.end();
|
|
154
158
|
});
|
|
155
159
|
stream.on('error', (e) => {
|
|
156
160
|
logger_1.default.error(e, `Couldn't stream file ${file.id} to the client`);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
161
|
+
if (!isDataSent) {
|
|
162
|
+
res.removeHeader('Content-Type');
|
|
163
|
+
res.removeHeader('Content-Disposition');
|
|
164
|
+
res.removeHeader('Cache-Control');
|
|
165
|
+
res.status(500).json({
|
|
166
|
+
errors: [
|
|
167
|
+
{
|
|
168
|
+
message: 'An unexpected error occurred.',
|
|
169
|
+
extensions: {
|
|
170
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
171
|
+
},
|
|
168
172
|
},
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
173
|
+
],
|
|
174
|
+
});
|
|
175
|
+
}
|
|
172
176
|
});
|
|
173
177
|
}));
|
|
174
178
|
exports.default = router;
|
package/dist/controllers/auth.js
CHANGED
|
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const express_1 = require("express");
|
|
7
|
-
const ms_1 = __importDefault(require("ms"));
|
|
8
7
|
const env_1 = __importDefault(require("../env"));
|
|
9
8
|
const exceptions_1 = require("../exceptions");
|
|
10
9
|
const respond_1 = require("../middleware/respond");
|
|
@@ -15,6 +14,7 @@ const logger_1 = __importDefault(require("../logger"));
|
|
|
15
14
|
const drivers_1 = require("../auth/drivers");
|
|
16
15
|
const constants_1 = require("../constants");
|
|
17
16
|
const get_ip_from_req_1 = require("../utils/get-ip-from-req");
|
|
17
|
+
const constants_2 = require("../constants");
|
|
18
18
|
const router = (0, express_1.Router)();
|
|
19
19
|
const authProviders = (0, get_auth_providers_1.getAuthProviders)();
|
|
20
20
|
for (const authProvider of authProviders) {
|
|
@@ -43,10 +43,10 @@ if (!env_1.default.AUTH_DISABLE_DEFAULT) {
|
|
|
43
43
|
router.use('/login', (0, drivers_1.createLocalAuthRouter)(constants_1.DEFAULT_AUTH_PROVIDER));
|
|
44
44
|
}
|
|
45
45
|
router.post('/refresh', (0, async_handler_1.default)(async (req, res, next) => {
|
|
46
|
-
var _a;
|
|
47
46
|
const accountability = {
|
|
48
47
|
ip: (0, get_ip_from_req_1.getIPFromReq)(req),
|
|
49
48
|
userAgent: req.get('user-agent'),
|
|
49
|
+
origin: req.get('origin'),
|
|
50
50
|
role: null,
|
|
51
51
|
};
|
|
52
52
|
const authenticationService = new services_1.AuthenticationService({
|
|
@@ -66,13 +66,7 @@ router.post('/refresh', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
66
66
|
payload.data.refresh_token = refreshToken;
|
|
67
67
|
}
|
|
68
68
|
if (mode === 'cookie') {
|
|
69
|
-
res.cookie(env_1.default.REFRESH_TOKEN_COOKIE_NAME, refreshToken,
|
|
70
|
-
httpOnly: true,
|
|
71
|
-
domain: env_1.default.REFRESH_TOKEN_COOKIE_DOMAIN,
|
|
72
|
-
maxAge: (0, ms_1.default)(env_1.default.REFRESH_TOKEN_TTL),
|
|
73
|
-
secure: (_a = env_1.default.REFRESH_TOKEN_COOKIE_SECURE) !== null && _a !== void 0 ? _a : false,
|
|
74
|
-
sameSite: env_1.default.REFRESH_TOKEN_COOKIE_SAME_SITE || 'strict',
|
|
75
|
-
});
|
|
69
|
+
res.cookie(env_1.default.REFRESH_TOKEN_COOKIE_NAME, refreshToken, constants_2.COOKIE_OPTIONS);
|
|
76
70
|
}
|
|
77
71
|
res.locals.payload = payload;
|
|
78
72
|
return next();
|
|
@@ -82,6 +76,7 @@ router.post('/logout', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
82
76
|
const accountability = {
|
|
83
77
|
ip: (0, get_ip_from_req_1.getIPFromReq)(req),
|
|
84
78
|
userAgent: req.get('user-agent'),
|
|
79
|
+
origin: req.get('origin'),
|
|
85
80
|
role: null,
|
|
86
81
|
};
|
|
87
82
|
const authenticationService = new services_1.AuthenticationService({
|
|
@@ -110,6 +105,7 @@ router.post('/password/request', (0, async_handler_1.default)(async (req, res, n
|
|
|
110
105
|
const accountability = {
|
|
111
106
|
ip: (0, get_ip_from_req_1.getIPFromReq)(req),
|
|
112
107
|
userAgent: req.get('user-agent'),
|
|
108
|
+
origin: req.get('origin'),
|
|
113
109
|
role: null,
|
|
114
110
|
};
|
|
115
111
|
const service = new services_1.UsersService({ accountability, schema: req.schema });
|
|
@@ -137,6 +133,7 @@ router.post('/password/reset', (0, async_handler_1.default)(async (req, res, nex
|
|
|
137
133
|
const accountability = {
|
|
138
134
|
ip: (0, get_ip_from_req_1.getIPFromReq)(req),
|
|
139
135
|
userAgent: req.get('user-agent'),
|
|
136
|
+
origin: req.get('origin'),
|
|
140
137
|
role: null,
|
|
141
138
|
};
|
|
142
139
|
const service = new services_1.UsersService({ accountability, schema: req.schema });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
jest.mock('../../src/cache');
|
|
8
|
+
jest.mock('../../src/database');
|
|
9
|
+
jest.mock('../../src/utils/validate-env');
|
|
10
|
+
const files_1 = require("./files");
|
|
11
|
+
const invalid_payload_1 = require("../exceptions/invalid-payload");
|
|
12
|
+
const stream_1 = require("stream");
|
|
13
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
14
|
+
describe('multipartHandler', () => {
|
|
15
|
+
it(`Errors out if request doesn't contain any files to upload`, () => {
|
|
16
|
+
const fakeForm = new form_data_1.default();
|
|
17
|
+
fakeForm.append('field', 'test');
|
|
18
|
+
const req = {
|
|
19
|
+
headers: fakeForm.getHeaders(),
|
|
20
|
+
is: jest.fn().mockReturnValue(true),
|
|
21
|
+
body: fakeForm.getBuffer(),
|
|
22
|
+
params: {},
|
|
23
|
+
pipe: (input) => stream.pipe(input),
|
|
24
|
+
};
|
|
25
|
+
const stream = new stream_1.PassThrough();
|
|
26
|
+
stream.push(fakeForm.getBuffer());
|
|
27
|
+
(0, files_1.multipartHandler)(req, {}, (err) => {
|
|
28
|
+
expect(err.message).toBe('No files where included in the body');
|
|
29
|
+
expect(err).toBeInstanceOf(invalid_payload_1.InvalidPayloadException);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
it(`Errors out if uploaded file doesn't include the filename`, () => {
|
|
33
|
+
const fakeForm = new form_data_1.default();
|
|
34
|
+
fakeForm.append('file', Buffer.from('<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 243 266" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="Calligraphy"><path d="M67.097,135.868c0,3.151 0.598,14.121 -11.586,14.121c-17.076,0 -15.95,-12.947 -15.95,-12.947c0,-2.521 4.597,-5.638 4.597,-7.318c0,-0.63 0.041,-3.519 -2.27,-3.519c-5.671,0 -5.671,10.083 -5.671,10.083c0,0 0.419,15.336 19.116,15.336c20.205,0 30.04,-23.712 30.04,-30.88c0,-18.197 -51.112,-27.701 -51.112,-57.949c0,1.575 -2.205,-13.864 14.18,-13.864c28.358,0 44.426,42.536 44.426,71.524c0,28.988 -16.699,55.455 -16.699,55.455c0,0 33.4,-25.837 33.4,-76.25c0,-70.264 -46.003,-69.634 -46.003,-69.634c-4.792,0 -7.602,-0.241 -28.398,20.555c-20.797,20.797 -17.646,29.83 -17.646,29.83c0,31.93 49.576,32.35 49.576,55.457Z" style="fill-rule:nonzero;"/><path d="M241.886,174.861c-1.602,-9.142 -15.448,-9.916 -22.675,-9.682c-0.7,-0.003 -1.172,0.02 -1.327,0.03c-8.804,0.01 -19.314,4.179 -33.072,13.115c-3.554,2.308 -7.19,4.847 -10.902,7.562c-6.979,-31.39 -13.852,-63.521 -28.033,-63.521c20.415,-20.119 22.19,-16.272 22.19,-39.054c0,-11.244 14.498,-21.35 14.498,-21.35l-0.296,-2.024c-19.193,5.304 -37.307,-8.577 -42.2,-12.755c5.375,-9.663 9.584,-12.565 9.584,-12.565c1.891,-20.377 15.965,-27.31 15.965,-27.31c1.681,-4.201 6.092,-7.142 6.092,-7.142c-70.162,22.267 -54.247,189.298 -54.247,189.298c-0.475,-55.91 5.238,-92.242 11.977,-115.55c9.094,8.248 24.425,11.765 24.425,11.765c-7.396,3.55 -5.324,12.13 -5.324,19.527c0,7.397 -3.848,10.651 -3.848,10.651l-21.893,22.782c17.043,0.294 23.638,31.657 30.689,63.056c-2.548,2.042 -5.125,4.12 -7.728,6.219c-16.396,13.223 -33.351,26.897 -50.266,37.354c-19.086,11.797 -35.151,17.533 -49.116,17.533c-25.25,0 -44.118,-24.368 -44.118,-46.154c0,-9.838 3.227,-17.831 5.935,-22.805c2.935,-5.39 5.911,-8.503 5.967,-8.561c0.001,0 0.001,0 0.001,-0.001l-0.013,-0.012c1.803,-1.885 4.841,-5.181 10.423,-5.181c20.715,0 27.475,40.776 55.603,40.776c24.857,0 31.834,-20.497 37.286,-31.399c0,0 -8.94,11.12 -21.587,11.12c-27.038,0 -35.323,-40.557 -55.166,-40.557c-13.41,0 -22.743,15.506 -31.029,27.281c0,0 0.018,-0.001 0.048,-0.003c-1.02,1.415 -2.214,3.233 -3.41,5.425c-2.847,5.21 -6.239,13.587 -6.239,23.917c0,22.816 19.801,48.334 46.299,48.334c14.381,0 30.822,-5.84 50.262,-17.858c17.033,-10.529 34.04,-24.246 50.489,-37.511c2.309,-1.862 4.607,-3.715 6.891,-5.549c6.952,30.814 14.606,60.912 33.278,60.912c14.794,0 26.923,-25.445 26.923,-25.445c-7.987,7.101 -13.313,5.621 -13.313,5.621c-13.139,0.379 -19.937,-27.594 -26.48,-56.931c16.455,-12.099 31.46,-20.829 43.488,-20.829l0.072,-0.003c0.082,-0.005 5.246,-0.305 9.957,1.471c-2.95,1.636 -4.947,4.782 -4.947,8.394c0,5.299 4.296,9.594 9.594,9.594c5.298,0 9.594,-4.295 9.594,-9.594c0,-0.826 -0.104,-1.627 -0.301,-2.391Z" style="fill-rule:nonzero;"/></g></svg>'));
|
|
35
|
+
const req = {
|
|
36
|
+
headers: fakeForm.getHeaders(),
|
|
37
|
+
is: jest.fn().mockReturnValue(true),
|
|
38
|
+
body: fakeForm.getBuffer(),
|
|
39
|
+
params: {},
|
|
40
|
+
pipe: (input) => stream.pipe(input),
|
|
41
|
+
};
|
|
42
|
+
const stream = new stream_1.PassThrough();
|
|
43
|
+
stream.push(fakeForm.getBuffer());
|
|
44
|
+
(0, files_1.multipartHandler)(req, {}, (err) => {
|
|
45
|
+
expect(err.message).toBe('File is missing filename');
|
|
46
|
+
expect(err).toBeInstanceOf(invalid_payload_1.InvalidPayloadException);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -36,7 +36,6 @@ router.get('/specs/graphql/:scope?', (0, async_handler_1.default)(async (req, re
|
|
|
36
36
|
res.attachment(filename);
|
|
37
37
|
res.send(result);
|
|
38
38
|
}));
|
|
39
|
-
router.get('/ping', (req, res) => res.send('pong'));
|
|
40
39
|
router.get('/info', (0, async_handler_1.default)(async (req, res, next) => {
|
|
41
40
|
const service = new services_1.ServerService({
|
|
42
41
|
accountability: req.accountability,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.down = exports.up = void 0;
|
|
4
|
+
async function up(knex) {
|
|
5
|
+
await knex.schema.alterTable('directus_activity', (table) => {
|
|
6
|
+
table.string('origin').nullable();
|
|
7
|
+
});
|
|
8
|
+
await knex.schema.alterTable('directus_sessions', (table) => {
|
|
9
|
+
table.string('origin').nullable();
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
exports.up = up;
|
|
13
|
+
async function down(knex) {
|
|
14
|
+
await knex.schema.alterTable('directus_activity', (table) => {
|
|
15
|
+
table.dropColumn('origin');
|
|
16
|
+
});
|
|
17
|
+
await knex.schema.alterTable('directus_sessions', (table) => {
|
|
18
|
+
table.dropColumn('origin');
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
exports.down = down;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const knex_1 = __importDefault(require("knex"));
|
|
7
|
+
const knex_mock_client_1 = require("knex-mock-client");
|
|
8
|
+
const run_1 = __importDefault(require("./run"));
|
|
9
|
+
describe('run', () => {
|
|
10
|
+
let db;
|
|
11
|
+
let tracker;
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
db = (0, knex_1.default)({ client: knex_mock_client_1.MockClient });
|
|
14
|
+
tracker = (0, knex_mock_client_1.getTracker)();
|
|
15
|
+
});
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
tracker.reset();
|
|
18
|
+
});
|
|
19
|
+
describe('when passed the argument up', () => {
|
|
20
|
+
it('returns "Nothing To Upgrade" if no directus_migrations', async () => {
|
|
21
|
+
tracker.on.select('directus_migrations').response(['Empty']);
|
|
22
|
+
await (0, run_1.default)(db, 'up').catch((e) => {
|
|
23
|
+
expect(e).toBeInstanceOf(Error);
|
|
24
|
+
expect(e.message).toBe('Nothing to upgrade');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
it('returns "Method implemented in the dialect driver" if no directus_migrations', async () => {
|
|
28
|
+
tracker.on.select('directus_migrations').response([]);
|
|
29
|
+
await (0, run_1.default)(db, 'up').catch((e) => {
|
|
30
|
+
expect(e).toBeInstanceOf(Error);
|
|
31
|
+
expect(e.message).toBe('Method implemented in the dialect driver');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
it('returns undefined if the migration is successful', async () => {
|
|
35
|
+
tracker.on.select('directus_migrations').response([
|
|
36
|
+
{
|
|
37
|
+
version: '20201028A',
|
|
38
|
+
name: 'Remove Collection Foreign Keys',
|
|
39
|
+
timestamp: '2021-11-27 11:36:56.471595-05',
|
|
40
|
+
},
|
|
41
|
+
]);
|
|
42
|
+
tracker.on.delete('directus_relations').response([]);
|
|
43
|
+
tracker.on.insert('directus_migrations').response(['Remove System Relations', '20201029A']);
|
|
44
|
+
expect(await (0, run_1.default)(db, 'up')).toBe(undefined);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('when passed the argument down', () => {
|
|
48
|
+
it('returns "Nothing To downgrade" if no valid directus_migrations', async () => {
|
|
49
|
+
tracker.on.select('directus_migrations').response(['Empty']);
|
|
50
|
+
await (0, run_1.default)(db, 'down').catch((e) => {
|
|
51
|
+
expect(e).toBeInstanceOf(Error);
|
|
52
|
+
expect(e.message).toBe(`Couldn't find migration`);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
it('returns "Method implemented in the dialect driver" if no directus_migrations', async () => {
|
|
56
|
+
tracker.on.select('directus_migrations').response([]);
|
|
57
|
+
await (0, run_1.default)(db, 'down').catch((e) => {
|
|
58
|
+
expect(e).toBeInstanceOf(Error);
|
|
59
|
+
expect(e.message).toBe('Nothing to downgrade');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
it(`returns "Couldn't find migration" if an invalid migration object is supplied`, async () => {
|
|
63
|
+
tracker.on.select('directus_migrations').response([
|
|
64
|
+
{
|
|
65
|
+
version: '202018129A',
|
|
66
|
+
name: 'Fake Migration',
|
|
67
|
+
timestamp: '2020-00-32 11:36:56.471595-05',
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
await (0, run_1.default)(db, 'down').catch((e) => {
|
|
71
|
+
expect(e).toBeInstanceOf(Error);
|
|
72
|
+
expect(e.message).toBe(`Couldn't find migration`);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('when passed the argument latest', () => {
|
|
77
|
+
it('returns "Nothing To downgrade" if no valid directus_migrations', async () => {
|
|
78
|
+
tracker.on.select('directus_migrations').response(['Empty']);
|
|
79
|
+
await (0, run_1.default)(db, 'latest').catch((e) => {
|
|
80
|
+
expect(e).toBeInstanceOf(Error);
|
|
81
|
+
expect(e.message).toBe(`Method implemented in the dialect driver`);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
it('returns "Method implemented in the dialect driver" if no directus_migrations', async () => {
|
|
85
|
+
tracker.on.select('directus_migrations').response([]);
|
|
86
|
+
await (0, run_1.default)(db, 'latest').catch((e) => {
|
|
87
|
+
expect(e).toBeInstanceOf(Error);
|
|
88
|
+
expect(e.message).toBe('Method implemented in the dialect driver');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
package/dist/env.js
CHANGED
|
@@ -42,6 +42,7 @@ const allowedEnvironmentVars = [
|
|
|
42
42
|
'REFRESH_TOKEN_COOKIE_SECURE',
|
|
43
43
|
'REFRESH_TOKEN_COOKIE_SAME_SITE',
|
|
44
44
|
'REFRESH_TOKEN_COOKIE_NAME',
|
|
45
|
+
'LOGIN_STALL_TIME',
|
|
45
46
|
'PASSWORD_RESET_URL_ALLOW_LIST',
|
|
46
47
|
'USER_INVITE_URL_ALLOW_LIST',
|
|
47
48
|
'IP_TRUST_PROXY',
|
|
@@ -79,6 +80,7 @@ const allowedEnvironmentVars = [
|
|
|
79
80
|
'CACHE_REDIS_PASSWORD',
|
|
80
81
|
'CACHE_MEMCACHE',
|
|
81
82
|
'CACHE_VALUE_MAX_SIZE',
|
|
83
|
+
'CACHE_HEALTHCHECK_THRESHOLD',
|
|
82
84
|
// storage
|
|
83
85
|
'STORAGE_LOCATIONS',
|
|
84
86
|
'STORAGE_.+_DRIVER',
|
|
@@ -95,6 +97,7 @@ const allowedEnvironmentVars = [
|
|
|
95
97
|
'STORAGE_.+_ENDPOINT',
|
|
96
98
|
'STORAGE_.+_KEY_FILENAME',
|
|
97
99
|
'STORAGE_.+_BUCKET',
|
|
100
|
+
'STORAGE_.+_HEALTHCHECK_THRESHOLD',
|
|
98
101
|
// metadata
|
|
99
102
|
'FILE_METADATA_ALLOW_LIST',
|
|
100
103
|
// assets
|
|
@@ -138,12 +141,20 @@ const allowedEnvironmentVars = [
|
|
|
138
141
|
// extensions
|
|
139
142
|
'EXTENSIONS_PATH',
|
|
140
143
|
'EXTENSIONS_AUTO_RELOAD',
|
|
144
|
+
// messenger
|
|
145
|
+
'MESSENGER_STORE',
|
|
146
|
+
'MESSENGER_NAMESPACE',
|
|
147
|
+
'MESSENGER_REDIS',
|
|
148
|
+
'MESSENGER_REDIS_HOST',
|
|
149
|
+
'MESSENGER_REDIS_PORT',
|
|
150
|
+
'MESSENGER_REDIS_PASSWORD',
|
|
141
151
|
// emails
|
|
142
152
|
'EMAIL_FROM',
|
|
143
153
|
'EMAIL_TRANSPORT',
|
|
144
154
|
'EMAIL_VERIFY_SETUP',
|
|
145
155
|
'EMAIL_SENDMAIL_NEW_LINE',
|
|
146
156
|
'EMAIL_SENDMAIL_PATH',
|
|
157
|
+
'EMAIL_SMTP_NAME',
|
|
147
158
|
'EMAIL_SMTP_HOST',
|
|
148
159
|
'EMAIL_SMTP_PORT',
|
|
149
160
|
'EMAIL_SMTP_USER',
|
|
@@ -165,6 +176,8 @@ const allowedEnvironmentVars = [
|
|
|
165
176
|
// limits & optimization
|
|
166
177
|
'RELATIONAL_BATCH_SIZE',
|
|
167
178
|
'EXPORT_BATCH_SIZE',
|
|
179
|
+
// flows
|
|
180
|
+
'FLOWS_EXEC_ALLOWED_MODULES',
|
|
168
181
|
].map((name) => new RegExp(`^${name}$`));
|
|
169
182
|
const acceptedEnvTypes = ['string', 'number', 'regex', 'array', 'json'];
|
|
170
183
|
const defaults = {
|
|
@@ -187,6 +200,7 @@ const defaults = {
|
|
|
187
200
|
REFRESH_TOKEN_COOKIE_SECURE: false,
|
|
188
201
|
REFRESH_TOKEN_COOKIE_SAME_SITE: 'lax',
|
|
189
202
|
REFRESH_TOKEN_COOKIE_NAME: 'directus_refresh_token',
|
|
203
|
+
LOGIN_STALL_TIME: 500,
|
|
190
204
|
ROOT_REDIRECT: './admin',
|
|
191
205
|
CORS_ENABLED: false,
|
|
192
206
|
CORS_ORIGIN: false,
|
|
@@ -226,6 +240,7 @@ const defaults = {
|
|
|
226
240
|
EXPORT_BATCH_SIZE: 5000,
|
|
227
241
|
FILE_METADATA_ALLOW_LIST: 'ifd0.Make,ifd0.Model,exif.FNumber,exif.ExposureTime,exif.FocalLength,exif.ISO',
|
|
228
242
|
GRAPHQL_INTROSPECTION: true,
|
|
243
|
+
FLOWS_EXEC_ALLOWED_MODULES: false,
|
|
229
244
|
};
|
|
230
245
|
// Allows us to force certain environment variable into a type, instead of relying
|
|
231
246
|
// on the auto-parsed type in processValues. ref #3705
|
package/dist/env.test.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const testEnv = {
|
|
3
|
+
NUMBER: '1234',
|
|
4
|
+
NUMBER_CAST_AS_STRING: 'string:1234',
|
|
5
|
+
REGEX: 'regex:\\.example\\.com$',
|
|
6
|
+
CSV: 'one,two,three,four',
|
|
7
|
+
CSV_CAST_AS_STRING: 'string:one,two,three,four',
|
|
8
|
+
MULTIPLE: 'array:string:https://example.com,regex:\\.example2\\.com$',
|
|
9
|
+
};
|
|
10
|
+
describe('env processed values', () => {
|
|
11
|
+
const originalEnv = process.env;
|
|
12
|
+
let env;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.resetModules();
|
|
15
|
+
process.env = { ...testEnv };
|
|
16
|
+
env = jest.requireActual('../src/env').default;
|
|
17
|
+
});
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
process.env = originalEnv;
|
|
20
|
+
});
|
|
21
|
+
test('Number value should be a number', () => {
|
|
22
|
+
expect(env.NUMBER).toStrictEqual(1234);
|
|
23
|
+
});
|
|
24
|
+
test('Number value casted as string should be a string', () => {
|
|
25
|
+
expect(env.NUMBER_CAST_AS_STRING).toStrictEqual('1234');
|
|
26
|
+
});
|
|
27
|
+
test('Value casted as regex', () => {
|
|
28
|
+
expect(env.REGEX).toBeInstanceOf(RegExp);
|
|
29
|
+
});
|
|
30
|
+
test('CSV value should be an array', () => {
|
|
31
|
+
expect(env.CSV).toStrictEqual(['one', 'two', 'three', 'four']);
|
|
32
|
+
});
|
|
33
|
+
test('CSV value casted as string should be a string', () => {
|
|
34
|
+
expect(env.CSV_CAST_AS_STRING).toStrictEqual('one,two,three,four');
|
|
35
|
+
});
|
|
36
|
+
test('Multiple type cast', () => {
|
|
37
|
+
expect(env.MULTIPLE).toStrictEqual(['https://example.com', /\.example2\.com$/]);
|
|
38
|
+
});
|
|
39
|
+
});
|