directus 9.5.0 → 9.6.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/auth/drivers/ldap.d.ts +0 -1
- package/dist/auth/drivers/ldap.js +54 -60
- package/dist/auth/drivers/openid.js +6 -6
- package/dist/cli/commands/init/index.js +8 -0
- package/dist/cli/utils/create-env/env-stub.liquid +1 -0
- package/dist/controllers/files.js +4 -1
- package/dist/database/index.js +33 -4
- 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/run-ast.d.ts +1 -1
- package/dist/database/run-ast.js +42 -35
- package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +8 -2
- package/dist/database/system-data/fields/collections.yaml +2 -0
- package/dist/database/system-data/fields/settings.yaml +19 -3
- package/dist/env.js +3 -0
- package/dist/extensions.js +0 -2
- package/dist/middleware/authenticate.d.ts +5 -3
- package/dist/middleware/authenticate.js +22 -39
- package/dist/middleware/extract-token.js +1 -1
- package/dist/server.js +5 -2
- package/dist/services/authorization.js +2 -1
- package/dist/services/collections.js +25 -27
- package/dist/services/fields.js +16 -16
- package/dist/services/mail/templates/base.liquid +2 -2
- package/dist/services/payload.js +7 -3
- package/dist/services/relations.js +4 -0
- package/dist/services/utils.js +10 -0
- package/dist/utils/apply-query.js +2 -24
- package/dist/utils/get-permissions.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/md.js +1 -1
- package/dist/utils/merge-permissions.js +18 -6
- package/example.env +1 -0
- package/package.json +24 -22
- package/dist/__mocks__/cache.d.ts +0 -6
- package/dist/__mocks__/cache.js +0 -8
|
@@ -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);
|
|
@@ -15,7 +15,7 @@ const extractToken = (req, res, next) => {
|
|
|
15
15
|
}
|
|
16
16
|
if (req.headers && req.headers.authorization) {
|
|
17
17
|
const parts = req.headers.authorization.split(' ');
|
|
18
|
-
if (parts.length === 2 && parts[0] === '
|
|
18
|
+
if (parts.length === 2 && parts[0].toLowerCase() === 'bearer') {
|
|
19
19
|
token = parts[1];
|
|
20
20
|
}
|
|
21
21
|
}
|
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)();
|
|
@@ -97,7 +98,7 @@ class AuthorizationService {
|
|
|
97
98
|
}
|
|
98
99
|
if (allowedFields.includes('*'))
|
|
99
100
|
continue;
|
|
100
|
-
const fieldKey = childNode.name;
|
|
101
|
+
const fieldKey = (0, strip_function_1.stripFunction)(childNode.name);
|
|
101
102
|
if (allowedFields.includes(fieldKey) === false) {
|
|
102
103
|
throw new exceptions_1.ForbiddenException();
|
|
103
104
|
}
|
|
@@ -66,24 +66,7 @@ class CollectionsService {
|
|
|
66
66
|
// permission problems. This might not work reliably in MySQL, as it doesn't support DDL in
|
|
67
67
|
// transactions.
|
|
68
68
|
await this.knex.transaction(async (trx) => {
|
|
69
|
-
if (payload.meta) {
|
|
70
|
-
const collectionItemsService = new items_1.ItemsService('directus_collections', {
|
|
71
|
-
knex: trx,
|
|
72
|
-
accountability: this.accountability,
|
|
73
|
-
schema: this.schema,
|
|
74
|
-
});
|
|
75
|
-
await collectionItemsService.createOne({
|
|
76
|
-
...payload.meta,
|
|
77
|
-
collection: payload.collection,
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
69
|
if (payload.schema) {
|
|
81
|
-
const fieldsService = new fields_1.FieldsService({ knex: trx, schema: this.schema });
|
|
82
|
-
const fieldItemsService = new items_1.ItemsService('directus_fields', {
|
|
83
|
-
knex: trx,
|
|
84
|
-
accountability: this.accountability,
|
|
85
|
-
schema: this.schema,
|
|
86
|
-
});
|
|
87
70
|
// Directus heavily relies on the primary key of a collection, so we have to make sure that
|
|
88
71
|
// every collection that is created has a primary key. If no primary key field is created
|
|
89
72
|
// while making the collection, we default to an auto incremented id named `id`
|
|
@@ -114,18 +97,33 @@ class CollectionsService {
|
|
|
114
97
|
}
|
|
115
98
|
return field;
|
|
116
99
|
});
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
100
|
+
const fieldsService = new fields_1.FieldsService({ knex: trx, schema: this.schema });
|
|
101
|
+
await trx.schema.createTable(payload.collection, (table) => {
|
|
102
|
+
for (const field of payload.fields) {
|
|
103
|
+
if (field.type && constants_1.ALIAS_TYPES.includes(field.type) === false) {
|
|
104
|
+
fieldsService.addColumnToTable(table, field);
|
|
123
105
|
}
|
|
124
|
-
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const fieldItemsService = new items_1.ItemsService('directus_fields', {
|
|
109
|
+
knex: trx,
|
|
110
|
+
accountability: this.accountability,
|
|
111
|
+
schema: this.schema,
|
|
125
112
|
});
|
|
126
113
|
const fieldPayloads = payload.fields.filter((field) => field.meta).map((field) => field.meta);
|
|
127
114
|
await fieldItemsService.createMany(fieldPayloads);
|
|
128
115
|
}
|
|
116
|
+
if (payload.meta) {
|
|
117
|
+
const collectionItemsService = new items_1.ItemsService('directus_collections', {
|
|
118
|
+
knex: trx,
|
|
119
|
+
accountability: this.accountability,
|
|
120
|
+
schema: this.schema,
|
|
121
|
+
});
|
|
122
|
+
await collectionItemsService.createOne({
|
|
123
|
+
...payload.meta,
|
|
124
|
+
collection: payload.collection,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
129
127
|
return payload.collection;
|
|
130
128
|
});
|
|
131
129
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
|
@@ -318,6 +316,9 @@ class CollectionsService {
|
|
|
318
316
|
}
|
|
319
317
|
await this.knex.transaction(async (trx) => {
|
|
320
318
|
var _a;
|
|
319
|
+
if (collectionToBeDeleted.schema) {
|
|
320
|
+
await trx.schema.dropTable(collectionKey);
|
|
321
|
+
}
|
|
321
322
|
// Make sure this collection isn't used as a group in any other collections
|
|
322
323
|
await trx('directus_collections').update({ group: null }).where({ group: collectionKey });
|
|
323
324
|
if (collectionToBeDeleted.meta) {
|
|
@@ -373,9 +374,6 @@ class CollectionsService {
|
|
|
373
374
|
.update({ one_allowed_collections: newAllowedCollections })
|
|
374
375
|
.where({ id: relation.meta.id });
|
|
375
376
|
}
|
|
376
|
-
await trx.transaction(async (schemaTrx) => {
|
|
377
|
-
await schemaTrx.schema.dropTable(collectionKey);
|
|
378
|
-
});
|
|
379
377
|
}
|
|
380
378
|
});
|
|
381
379
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
package/dist/services/fields.js
CHANGED
|
@@ -224,10 +224,8 @@ class FieldsService {
|
|
|
224
224
|
this.addColumnToTable(table, hookAdjustedField);
|
|
225
225
|
}
|
|
226
226
|
else {
|
|
227
|
-
await trx.
|
|
228
|
-
|
|
229
|
-
this.addColumnToTable(table, hookAdjustedField);
|
|
230
|
-
});
|
|
227
|
+
await trx.schema.alterTable(collection, (table) => {
|
|
228
|
+
this.addColumnToTable(table, hookAdjustedField);
|
|
231
229
|
});
|
|
232
230
|
}
|
|
233
231
|
}
|
|
@@ -327,6 +325,13 @@ class FieldsService {
|
|
|
327
325
|
});
|
|
328
326
|
await this.knex.transaction(async (trx) => {
|
|
329
327
|
var _a, _b;
|
|
328
|
+
if (this.schema.collections[collection] &&
|
|
329
|
+
field in this.schema.collections[collection].fields &&
|
|
330
|
+
this.schema.collections[collection].fields[field].alias === false) {
|
|
331
|
+
await trx.schema.table(collection, (table) => {
|
|
332
|
+
table.dropColumn(field);
|
|
333
|
+
});
|
|
334
|
+
}
|
|
330
335
|
const relations = this.schema.relations.filter((relation) => {
|
|
331
336
|
var _a;
|
|
332
337
|
return ((relation.collection === collection && relation.field === field) ||
|
|
@@ -386,15 +391,6 @@ class FieldsService {
|
|
|
386
391
|
.where({ group: metaRow.field, collection: metaRow.collection });
|
|
387
392
|
}
|
|
388
393
|
await trx('directus_fields').delete().where({ collection, field });
|
|
389
|
-
if (this.schema.collections[collection] &&
|
|
390
|
-
field in this.schema.collections[collection].fields &&
|
|
391
|
-
this.schema.collections[collection].fields[field].alias === false) {
|
|
392
|
-
await trx.transaction(async (schemaTrx) => {
|
|
393
|
-
await schemaTrx.schema.table(collection, (table) => {
|
|
394
|
-
table.dropColumn(field);
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
394
|
});
|
|
399
395
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
400
396
|
await this.cache.clear();
|
|
@@ -456,11 +452,15 @@ class FieldsService {
|
|
|
456
452
|
column.defaultTo(field.schema.default_value);
|
|
457
453
|
}
|
|
458
454
|
}
|
|
459
|
-
if (((_g = field.schema) === null || _g === void 0 ? void 0 : _g.is_nullable)
|
|
460
|
-
|
|
455
|
+
if (((_g = field.schema) === null || _g === void 0 ? void 0 : _g.is_nullable) === false) {
|
|
456
|
+
if (!alter || alter.is_nullable === true) {
|
|
457
|
+
column.notNullable();
|
|
458
|
+
}
|
|
461
459
|
}
|
|
462
460
|
else {
|
|
463
|
-
|
|
461
|
+
if (!alter || alter.is_nullable === false) {
|
|
462
|
+
column.nullable();
|
|
463
|
+
}
|
|
464
464
|
}
|
|
465
465
|
if ((_h = field.schema) === null || _h === void 0 ? void 0 : _h.is_primary_key) {
|
|
466
466
|
column.primary().notNullable();
|
|
@@ -35,7 +35,7 @@ a[x-apple-data-detectors] {
|
|
|
35
35
|
line-height: inherit !important;
|
|
36
36
|
}
|
|
37
37
|
body a {
|
|
38
|
-
color: #
|
|
38
|
+
color: #6644ff;
|
|
39
39
|
text-decoration: none;
|
|
40
40
|
}
|
|
41
41
|
hr {
|
|
@@ -74,7 +74,7 @@ hr {
|
|
|
74
74
|
color: #FFFFFF !important;
|
|
75
75
|
}
|
|
76
76
|
.link {
|
|
77
|
-
color: #
|
|
77
|
+
color: #6644ff !important;
|
|
78
78
|
}
|
|
79
79
|
.button {
|
|
80
80
|
background-color:#0BA582 !important;
|
package/dist/services/payload.js
CHANGED
|
@@ -100,12 +100,16 @@ class PayloadService {
|
|
|
100
100
|
return value;
|
|
101
101
|
},
|
|
102
102
|
async csv({ action, value }) {
|
|
103
|
-
if (
|
|
103
|
+
if (Array.isArray(value) === false && typeof value !== 'string')
|
|
104
104
|
return;
|
|
105
|
-
if (action === 'read' && Array.isArray(value) === false)
|
|
105
|
+
if (action === 'read' && Array.isArray(value) === false) {
|
|
106
|
+
if (value === '')
|
|
107
|
+
return [];
|
|
106
108
|
return value.split(',');
|
|
107
|
-
|
|
109
|
+
}
|
|
110
|
+
if (Array.isArray(value)) {
|
|
108
111
|
return value.join(',');
|
|
112
|
+
}
|
|
109
113
|
return value;
|
|
110
114
|
},
|
|
111
115
|
};
|
|
@@ -135,6 +135,10 @@ class RelationsService {
|
|
|
135
135
|
if (relation.field in this.schema.collections[relation.collection].fields === false) {
|
|
136
136
|
throw new exceptions_1.InvalidPayloadException(`Field "${relation.field}" doesn't exist in collection "${relation.collection}"`);
|
|
137
137
|
}
|
|
138
|
+
// A primary key should not be a foreign key
|
|
139
|
+
if (this.schema.collections[relation.collection].primary === relation.field) {
|
|
140
|
+
throw new exceptions_1.InvalidPayloadException(`Field "${relation.field}" in collection "${relation.collection}" is a primary key`);
|
|
141
|
+
}
|
|
138
142
|
if (relation.related_collection && relation.related_collection in this.schema.collections === false) {
|
|
139
143
|
throw new exceptions_1.InvalidPayloadException(`Collection "${relation.related_collection}" doesn't exist`);
|
|
140
144
|
}
|
package/dist/services/utils.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.UtilsService = void 0;
|
|
|
7
7
|
const database_1 = __importDefault(require("../database"));
|
|
8
8
|
const collections_1 = require("../database/system-data/collections");
|
|
9
9
|
const exceptions_1 = require("../exceptions");
|
|
10
|
+
const emitter_1 = __importDefault(require("../emitter"));
|
|
10
11
|
class UtilsService {
|
|
11
12
|
constructor(options) {
|
|
12
13
|
this.knex = options.knex || (0, database_1.default)();
|
|
@@ -98,6 +99,15 @@ class UtilsService {
|
|
|
98
99
|
.andWhere(sortField, '<=', sourceSortValue)
|
|
99
100
|
.andWhereNot({ [primaryKeyField]: item });
|
|
100
101
|
}
|
|
102
|
+
emitter_1.default.emitAction(['items.sort', `${collection}.items.sort`], {
|
|
103
|
+
collection,
|
|
104
|
+
item,
|
|
105
|
+
to,
|
|
106
|
+
}, {
|
|
107
|
+
database: this.knex,
|
|
108
|
+
schema: this.schema,
|
|
109
|
+
accountability: this.accountability,
|
|
110
|
+
});
|
|
101
111
|
}
|
|
102
112
|
}
|
|
103
113
|
exports.UtilsService = UtilsService;
|
|
@@ -48,29 +48,7 @@ function applyQuery(knex, collection, dbQuery, query, schema, subQuery = false)
|
|
|
48
48
|
if (query.aggregate) {
|
|
49
49
|
applyAggregate(dbQuery, query.aggregate, collection);
|
|
50
50
|
}
|
|
51
|
-
if (query.
|
|
52
|
-
const [field, keys] = query.union;
|
|
53
|
-
const queries = keys.map((key) => {
|
|
54
|
-
const unionFilter = { [field]: { _eq: key } };
|
|
55
|
-
let filter = (0, lodash_1.cloneDeep)(query.filter);
|
|
56
|
-
if (filter) {
|
|
57
|
-
if ('_and' in filter) {
|
|
58
|
-
filter._and.push(unionFilter);
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
filter = {
|
|
62
|
-
_and: [filter, unionFilter],
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
filter = unionFilter;
|
|
68
|
-
}
|
|
69
|
-
return knex.select('*').from(applyFilter(knex, schema, dbQuery.clone(), filter, collection, subQuery).as('foo'));
|
|
70
|
-
});
|
|
71
|
-
dbQuery = knex.unionAll(queries);
|
|
72
|
-
}
|
|
73
|
-
else if (query.filter) {
|
|
51
|
+
if (query.filter) {
|
|
74
52
|
applyFilter(knex, schema, dbQuery, query.filter, collection, subQuery);
|
|
75
53
|
}
|
|
76
54
|
return dbQuery;
|
|
@@ -234,7 +212,7 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
|
|
|
234
212
|
applyFilterToQuery(`${collection}.${filterPath[0]}`, filterOperator, filterValue, logical);
|
|
235
213
|
}
|
|
236
214
|
}
|
|
237
|
-
else if (subQuery === false) {
|
|
215
|
+
else if (subQuery === false || filterPath.length > 1) {
|
|
238
216
|
if (!relation)
|
|
239
217
|
continue;
|
|
240
218
|
let pkField = `${collection}.${schema.collections[relation.related_collection].primary}`;
|
|
@@ -145,7 +145,7 @@ function processPermissions(accountability, permissions, filterContext) {
|
|
|
145
145
|
return permissions.map((permission) => {
|
|
146
146
|
permission.permissions = (0, utils_1.parseFilter)(permission.permissions, accountability, filterContext);
|
|
147
147
|
permission.validation = (0, utils_1.parseFilter)(permission.validation, accountability, filterContext);
|
|
148
|
-
permission.presets = (0, utils_1.
|
|
148
|
+
permission.presets = (0, utils_1.parsePreset)(permission.presets, accountability, filterContext);
|
|
149
149
|
return permission;
|
|
150
150
|
});
|
|
151
151
|
}
|
|
@@ -3,37 +3,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
7
|
-
const logger_1 = __importDefault(require("../logger"));
|
|
6
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
7
|
/**
|
|
9
8
|
* Check if a given string conforms to the structure of a JWT
|
|
10
9
|
* and whether it is issued by Directus.
|
|
11
10
|
*/
|
|
12
11
|
function isDirectusJWT(string) {
|
|
13
|
-
const parts = string.split('.');
|
|
14
|
-
// JWTs have the structure header.payload.signature
|
|
15
|
-
if (parts.length !== 3)
|
|
16
|
-
return false;
|
|
17
|
-
// Check if all segments are base64 encoded
|
|
18
|
-
try {
|
|
19
|
-
(0, atob_1.default)(parts[0]);
|
|
20
|
-
(0, atob_1.default)(parts[1]);
|
|
21
|
-
(0, atob_1.default)(parts[2]);
|
|
22
|
-
}
|
|
23
|
-
catch (err) {
|
|
24
|
-
logger_1.default.error(err);
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
// Check if the header and payload are valid JSON
|
|
28
12
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (payload.iss !== 'directus')
|
|
13
|
+
const payload = jsonwebtoken_1.default.decode(string, { json: true });
|
|
14
|
+
if ((payload === null || payload === void 0 ? void 0 : payload.iss) !== 'directus')
|
|
32
15
|
return false;
|
|
16
|
+
return true;
|
|
33
17
|
}
|
|
34
18
|
catch {
|
|
35
19
|
return false;
|
|
36
20
|
}
|
|
37
|
-
return true;
|
|
38
21
|
}
|
|
39
22
|
exports.default = isDirectusJWT;
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.verifyAccessJWT = void 0;
|
|
23
|
+
const jsonwebtoken_1 = __importStar(require("jsonwebtoken"));
|
|
24
|
+
const exceptions_1 = require("../exceptions");
|
|
25
|
+
function verifyAccessJWT(token, secret) {
|
|
26
|
+
let payload;
|
|
27
|
+
try {
|
|
28
|
+
payload = jsonwebtoken_1.default.verify(token, secret, {
|
|
29
|
+
issuer: 'directus',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
if (err instanceof jsonwebtoken_1.TokenExpiredError) {
|
|
34
|
+
throw new exceptions_1.InvalidTokenException('Token expired.');
|
|
35
|
+
}
|
|
36
|
+
else if (err instanceof jsonwebtoken_1.JsonWebTokenError) {
|
|
37
|
+
throw new exceptions_1.InvalidTokenException('Token invalid.');
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw new exceptions_1.ServiceUnavailableException(`Couldn't verify token.`, { service: 'jwt' });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const { id, role, app_access, admin_access, share, share_scope } = payload;
|
|
44
|
+
if (role === undefined || app_access === undefined || admin_access === undefined) {
|
|
45
|
+
throw new exceptions_1.InvalidTokenException('Invalid token payload.');
|
|
46
|
+
}
|
|
47
|
+
return { id, role, app_access, admin_access, share, share_scope };
|
|
48
|
+
}
|
|
49
|
+
exports.verifyAccessJWT = verifyAccessJWT;
|
package/dist/utils/md.js
CHANGED
|
@@ -10,6 +10,6 @@ const sanitize_html_1 = __importDefault(require("sanitize-html"));
|
|
|
10
10
|
* Render and sanitize a markdown string
|
|
11
11
|
*/
|
|
12
12
|
function md(str) {
|
|
13
|
-
return (0, sanitize_html_1.default)((0, marked_1.
|
|
13
|
+
return (0, sanitize_html_1.default)((0, marked_1.marked)(str));
|
|
14
14
|
}
|
|
15
15
|
exports.md = md;
|
|
@@ -31,9 +31,15 @@ function mergePermission(strategy, currentPerm, newPerm) {
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
else if (currentPerm.permissions) {
|
|
34
|
-
permissions
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
// Empty {} supersedes other permissions in _OR merge
|
|
35
|
+
if (strategy === 'or' && ((0, lodash_1.isEqual)(currentPerm.permissions, {}) || (0, lodash_1.isEqual)(newPerm.permissions, {}))) {
|
|
36
|
+
permissions = {};
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
permissions = {
|
|
40
|
+
[logicalKey]: [currentPerm.permissions, newPerm.permissions],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
44
|
else {
|
|
39
45
|
permissions = {
|
|
@@ -51,9 +57,15 @@ function mergePermission(strategy, currentPerm, newPerm) {
|
|
|
51
57
|
};
|
|
52
58
|
}
|
|
53
59
|
else if (currentPerm.validation) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
// Empty {} supersedes other validations in _OR merge
|
|
61
|
+
if (strategy === 'or' && ((0, lodash_1.isEqual)(currentPerm.validation, {}) || (0, lodash_1.isEqual)(newPerm.validation, {}))) {
|
|
62
|
+
validation = {};
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
validation = {
|
|
66
|
+
[logicalKey]: [currentPerm.validation, newPerm.validation],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
57
69
|
}
|
|
58
70
|
else {
|
|
59
71
|
validation = {
|