directus 9.1.1 → 9.2.2
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.js +11 -2
- package/dist/auth/drivers/oauth2.d.ts +1 -1
- package/dist/auth/drivers/oauth2.js +46 -19
- package/dist/auth/drivers/openid.d.ts +1 -1
- package/dist/auth/drivers/openid.js +34 -17
- package/dist/auth.js +5 -3
- package/dist/cli/commands/database/install.js +2 -4
- package/dist/cli/commands/schema/apply.js +26 -10
- package/dist/controllers/assets.js +0 -27
- package/dist/controllers/auth.js +7 -2
- package/dist/controllers/extensions.js +1 -1
- package/dist/database/{functions/types.d.ts → helpers/date/dialects/mssql.d.ts} +2 -1
- package/dist/database/{functions → helpers/date}/dialects/mssql.js +4 -6
- package/dist/database/{functions → helpers/date}/dialects/mysql.d.ts +2 -4
- package/dist/database/{functions → helpers/date}/dialects/mysql.js +4 -6
- package/dist/database/{functions/dialects/mssql.d.ts → helpers/date/dialects/oracle.d.ts} +2 -4
- package/dist/database/{functions → helpers/date}/dialects/oracle.js +4 -6
- package/dist/database/helpers/date/dialects/postgres.d.ts +12 -0
- package/dist/database/{functions → helpers/date}/dialects/postgres.js +4 -6
- package/dist/database/{functions → helpers/date}/dialects/sqlite.d.ts +3 -4
- package/dist/database/helpers/date/dialects/sqlite.js +35 -0
- package/dist/database/helpers/date/index.d.ts +6 -0
- package/dist/database/helpers/date/index.js +15 -0
- package/dist/database/helpers/date/types.d.ts +13 -0
- package/dist/database/helpers/date/types.js +10 -0
- package/dist/database/helpers/geometry/dialects/mssql.d.ts +14 -0
- package/dist/database/helpers/geometry/dialects/mssql.js +36 -0
- package/dist/database/helpers/geometry/dialects/mysql.d.ts +7 -0
- package/dist/database/helpers/geometry/dialects/mysql.js +16 -0
- package/dist/database/helpers/geometry/dialects/oracle.d.ts +15 -0
- package/dist/database/helpers/geometry/dialects/oracle.js +39 -0
- package/dist/database/helpers/geometry/dialects/postgres.d.ts +10 -0
- package/dist/database/helpers/geometry/dialects/postgres.js +23 -0
- package/dist/database/helpers/geometry/dialects/redshift.d.ts +7 -0
- package/dist/database/helpers/geometry/dialects/redshift.js +16 -0
- package/dist/database/helpers/geometry/dialects/sqlite.d.ts +6 -0
- package/dist/database/helpers/geometry/dialects/sqlite.js +14 -0
- package/dist/database/helpers/geometry/index.d.ts +6 -0
- package/dist/database/helpers/geometry/index.js +15 -0
- package/dist/database/helpers/{geometry.d.ts → geometry/types.d.ts} +3 -7
- package/dist/database/helpers/geometry/types.js +54 -0
- package/dist/database/helpers/index.d.ts +8 -0
- package/dist/database/helpers/index.js +33 -0
- package/dist/database/helpers/types.d.ts +5 -0
- package/dist/database/helpers/types.js +9 -0
- package/dist/database/index.js +6 -6
- package/dist/database/run-ast.js +5 -5
- package/dist/database/seeds/run.js +3 -3
- package/dist/database/system-data/fields/notifications.yaml +1 -0
- package/dist/env.js +1 -0
- package/dist/exceptions/index.d.ts +2 -0
- package/dist/exceptions/index.js +2 -0
- package/dist/exceptions/invalid-token.d.ts +4 -0
- package/dist/exceptions/invalid-token.js +10 -0
- package/dist/exceptions/unexpected-response.d.ts +4 -0
- package/dist/exceptions/unexpected-response.js +10 -0
- package/dist/extensions.js +17 -2
- package/dist/middleware/sanitize-query.js +1 -1
- package/dist/services/activity.js +7 -2
- package/dist/services/assets.js +14 -0
- package/dist/services/fields.d.ts +2 -0
- package/dist/services/fields.js +57 -26
- package/dist/services/files.d.ts +1 -1
- package/dist/services/files.js +13 -11
- package/dist/services/graphql.js +3 -0
- package/dist/services/items.js +18 -29
- package/dist/services/payload.d.ts +2 -0
- package/dist/services/payload.js +3 -3
- package/dist/services/users.js +8 -6
- package/dist/tests/database/migrations/run.test.d.ts +1 -0
- package/dist/tests/database/migrations/run.test.js +29 -0
- package/dist/utils/apply-query.js +9 -12
- package/dist/utils/apply-snapshot.js +27 -28
- package/dist/utils/get-column.js +2 -2
- package/dist/utils/get-default-index-name.js +2 -2
- package/dist/utils/get-local-type.js +1 -12
- package/dist/utils/get-permissions.d.ts +2 -2
- package/dist/utils/get-permissions.js +103 -66
- package/dist/utils/sanitize-query.js +1 -12
- package/dist/utils/validate-query.js +1 -1
- package/dist/webhooks.js +16 -24
- package/package.json +15 -14
- package/dist/database/functions/dialects/oracle.d.ts +0 -14
- package/dist/database/functions/dialects/postgres.d.ts +0 -14
- package/dist/database/functions/dialects/sqlite.js +0 -33
- package/dist/database/functions/index.d.ts +0 -3
- package/dist/database/functions/index.js +0 -26
- package/dist/database/functions/types.js +0 -2
- package/dist/database/helpers/date.d.ts +0 -8
- package/dist/database/helpers/date.js +0 -44
- package/dist/database/helpers/geometry.js +0 -189
- package/dist/utils/get-simple-hash.d.ts +0 -5
- package/dist/utils/get-simple-hash.js +0 -15
|
@@ -8,11 +8,9 @@ const lodash_1 = require("lodash");
|
|
|
8
8
|
const nanoid_1 = require("nanoid");
|
|
9
9
|
const uuid_validate_1 = __importDefault(require("uuid-validate"));
|
|
10
10
|
const exceptions_1 = require("../exceptions");
|
|
11
|
-
const apply_function_to_column_name_1 = require("./apply-function-to-column-name");
|
|
12
11
|
const get_column_1 = require("./get-column");
|
|
13
12
|
const get_relation_type_1 = require("./get-relation-type");
|
|
14
|
-
const
|
|
15
|
-
const date_1 = require("../database/helpers/date");
|
|
13
|
+
const helpers_1 = require("../database/helpers");
|
|
16
14
|
const generateAlias = (0, nanoid_1.customAlphabet)('abcdefghijklmnopqrstuvwxyz', 5);
|
|
17
15
|
/**
|
|
18
16
|
* Apply the Query to a given Knex query builder instance
|
|
@@ -45,7 +43,7 @@ function applyQuery(knex, collection, dbQuery, query, schema, subQuery = false)
|
|
|
45
43
|
applySearch(schema, dbQuery, query.search, collection);
|
|
46
44
|
}
|
|
47
45
|
if (query.group) {
|
|
48
|
-
dbQuery.groupBy(query.group.map(
|
|
46
|
+
dbQuery.groupBy(query.group.map((column) => (0, get_column_1.getColumn)(knex, collection, column, false)));
|
|
49
47
|
}
|
|
50
48
|
if (query.aggregate) {
|
|
51
49
|
applyAggregate(dbQuery, query.aggregate, collection);
|
|
@@ -117,6 +115,7 @@ exports.default = applyQuery;
|
|
|
117
115
|
* ```
|
|
118
116
|
*/
|
|
119
117
|
function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery = false) {
|
|
118
|
+
const helpers = (0, helpers_1.getHelpers)(knex);
|
|
120
119
|
const relations = schema.relations;
|
|
121
120
|
const aliasMap = {};
|
|
122
121
|
addJoins(rootQuery, rootFilter, collection);
|
|
@@ -273,16 +272,15 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
|
|
|
273
272
|
query.where(key, '!=', '');
|
|
274
273
|
});
|
|
275
274
|
}
|
|
276
|
-
const dateHelper = (0, date_1.getDateHelper)();
|
|
277
275
|
const [collection, field] = key.split('.');
|
|
278
276
|
if (collection in schema.collections && field in schema.collections[collection].fields) {
|
|
279
277
|
const type = schema.collections[collection].fields[field].type;
|
|
280
278
|
if (['date', 'dateTime', 'time', 'timestamp'].includes(type)) {
|
|
281
279
|
if (Array.isArray(compareValue)) {
|
|
282
|
-
compareValue = compareValue.map((val) =>
|
|
280
|
+
compareValue = compareValue.map((val) => helpers.date.parse(val));
|
|
283
281
|
}
|
|
284
282
|
else {
|
|
285
|
-
compareValue =
|
|
283
|
+
compareValue = helpers.date.parse(compareValue);
|
|
286
284
|
}
|
|
287
285
|
}
|
|
288
286
|
}
|
|
@@ -361,18 +359,17 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
|
|
|
361
359
|
value = value.split(',');
|
|
362
360
|
dbQuery[logical].whereNotBetween(selectionRaw, value);
|
|
363
361
|
}
|
|
364
|
-
const geometryHelper = (0, geometry_1.getGeometryHelper)();
|
|
365
362
|
if (operator == '_intersects') {
|
|
366
|
-
dbQuery[logical].whereRaw(
|
|
363
|
+
dbQuery[logical].whereRaw(helpers.st.intersects(key, compareValue));
|
|
367
364
|
}
|
|
368
365
|
if (operator == '_nintersects') {
|
|
369
|
-
dbQuery[logical].whereRaw(
|
|
366
|
+
dbQuery[logical].whereRaw(helpers.st.nintersects(key, compareValue));
|
|
370
367
|
}
|
|
371
368
|
if (operator == '_intersects_bbox') {
|
|
372
|
-
dbQuery[logical].whereRaw(
|
|
369
|
+
dbQuery[logical].whereRaw(helpers.st.intersects_bbox(key, compareValue));
|
|
373
370
|
}
|
|
374
371
|
if (operator == '_nintersects_bbox') {
|
|
375
|
-
dbQuery[logical].whereRaw(
|
|
372
|
+
dbQuery[logical].whereRaw(helpers.st.nintersects_bbox(key, compareValue));
|
|
376
373
|
}
|
|
377
374
|
}
|
|
378
375
|
function getWhereColumn(path, collection) {
|
|
@@ -36,14 +36,13 @@ async function applySnapshot(snapshot, options) {
|
|
|
36
36
|
// edits
|
|
37
37
|
snapshotDiff.fields = snapshotDiff.fields.filter((fieldDiff) => fieldDiff.collection !== collection);
|
|
38
38
|
}
|
|
39
|
-
if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'E') {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
await collectionsService.updateOne(collection, updates);
|
|
39
|
+
if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'E' || (diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'A') {
|
|
40
|
+
const newValues = snapshot.collections.find((field) => {
|
|
41
|
+
return field.collection === collection;
|
|
42
|
+
});
|
|
43
|
+
if (newValues) {
|
|
44
|
+
await collectionsService.updateOne(collection, newValues);
|
|
45
|
+
}
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
const fieldsService = new services_1.FieldsService({ knex: trx, schema: await (0, get_schema_1.getSchema)({ database: trx }) });
|
|
@@ -51,18 +50,15 @@ async function applySnapshot(snapshot, options) {
|
|
|
51
50
|
if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'N') {
|
|
52
51
|
await fieldsService.createField(collection, diff[0].rhs);
|
|
53
52
|
}
|
|
54
|
-
if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'E') {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
return acc;
|
|
58
|
-
(0, lodash_1.set)(acc, edit.path, edit.rhs);
|
|
59
|
-
return acc;
|
|
60
|
-
}, {});
|
|
61
|
-
await fieldsService.updateField(collection, {
|
|
62
|
-
field,
|
|
63
|
-
type: 'unknown',
|
|
64
|
-
...updates,
|
|
53
|
+
if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'E' || (diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'A') {
|
|
54
|
+
const newValues = snapshot.fields.find((snapshotField) => {
|
|
55
|
+
return snapshotField.collection === collection && snapshotField.field === field;
|
|
65
56
|
});
|
|
57
|
+
if (newValues) {
|
|
58
|
+
await fieldsService.updateField(collection, {
|
|
59
|
+
...newValues,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
66
62
|
}
|
|
67
63
|
if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'D') {
|
|
68
64
|
await fieldsService.deleteField(collection, field);
|
|
@@ -72,18 +68,21 @@ async function applySnapshot(snapshot, options) {
|
|
|
72
68
|
}
|
|
73
69
|
}
|
|
74
70
|
const relationsService = new services_1.RelationsService({ knex: trx, schema: await (0, get_schema_1.getSchema)({ database: trx }) });
|
|
75
|
-
for (const { collection, field, diff
|
|
71
|
+
for (const { collection, field, diff } of snapshotDiff.relations) {
|
|
72
|
+
const structure = {};
|
|
73
|
+
for (const diffEdit of diff) {
|
|
74
|
+
(0, lodash_1.set)(structure, diffEdit.path, undefined);
|
|
75
|
+
}
|
|
76
76
|
if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'N') {
|
|
77
77
|
await relationsService.createOne(diff[0].rhs);
|
|
78
78
|
}
|
|
79
|
-
if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'E') {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
await relationsService.updateOne(collection, field, updates);
|
|
79
|
+
if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'E' || (diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'A') {
|
|
80
|
+
const newValues = snapshot.relations.find((relation) => {
|
|
81
|
+
return relation.collection === collection && relation.field === field;
|
|
82
|
+
});
|
|
83
|
+
if (newValues) {
|
|
84
|
+
await relationsService.updateOne(collection, field, newValues);
|
|
85
|
+
}
|
|
87
86
|
}
|
|
88
87
|
if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'D') {
|
|
89
88
|
await relationsService.deleteOne(collection, field);
|
package/dist/utils/get-column.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getColumn = void 0;
|
|
4
|
-
const
|
|
4
|
+
const helpers_1 = require("../database/helpers");
|
|
5
5
|
const constants_1 = require("@directus/shared/constants");
|
|
6
6
|
const apply_function_to_column_name_1 = require("./apply-function-to-column-name");
|
|
7
7
|
/**
|
|
@@ -15,7 +15,7 @@ const apply_function_to_column_name_1 = require("./apply-function-to-column-name
|
|
|
15
15
|
* @returns Knex raw instance
|
|
16
16
|
*/
|
|
17
17
|
function getColumn(knex, table, column, alias = (0, apply_function_to_column_name_1.applyFunctionToColumnName)(column)) {
|
|
18
|
-
const fn = (0,
|
|
18
|
+
const { date: fn } = (0, helpers_1.getHelpers)(knex);
|
|
19
19
|
if (column.includes('(') && column.includes(')')) {
|
|
20
20
|
const functionName = column.split('(')[0];
|
|
21
21
|
const columnName = column.match(constants_1.REGEX_BETWEEN_PARENS)[1];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getDefaultIndexName = void 0;
|
|
4
|
-
const
|
|
4
|
+
const utils_1 = require("@directus/shared/utils");
|
|
5
5
|
/**
|
|
6
6
|
* Generate an index name for a given collection + fields combination.
|
|
7
7
|
*
|
|
@@ -18,7 +18,7 @@ function getDefaultIndexName(type, collection, fields) {
|
|
|
18
18
|
const indexName = (table + '_' + fields.join('_') + '_' + type).toLowerCase();
|
|
19
19
|
if (indexName.length <= 60)
|
|
20
20
|
return indexName;
|
|
21
|
-
const suffix = `__${(0,
|
|
21
|
+
const suffix = `__${(0, utils_1.getSimpleHash)(indexName)}_${type}`;
|
|
22
22
|
const prefix = indexName.substring(0, 60 - suffix.length);
|
|
23
23
|
return `${prefix}${suffix}`;
|
|
24
24
|
}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const database_1 = require("../database");
|
|
7
|
-
const database_2 = __importDefault(require("../database"));
|
|
8
3
|
const localTypeMap = {
|
|
9
4
|
// Shared
|
|
10
5
|
boolean: 'boolean',
|
|
@@ -100,8 +95,6 @@ const localTypeMap = {
|
|
|
100
95
|
integerfirst: 'integer',
|
|
101
96
|
};
|
|
102
97
|
function getLocalType(column, field) {
|
|
103
|
-
const database = (0, database_2.default)();
|
|
104
|
-
const databaseClient = (0, database_1.getDatabaseClient)(database);
|
|
105
98
|
if (!column)
|
|
106
99
|
return 'alias';
|
|
107
100
|
const dataType = column.data_type.toLowerCase();
|
|
@@ -125,13 +118,9 @@ function getLocalType(column, field) {
|
|
|
125
118
|
return 'decimal';
|
|
126
119
|
}
|
|
127
120
|
/** Handle MS SQL varchar(MAX) (eg TEXT) types */
|
|
128
|
-
if (
|
|
121
|
+
if (column.data_type === 'nvarchar' && column.max_length === -1) {
|
|
129
122
|
return 'text';
|
|
130
123
|
}
|
|
131
|
-
/** Handle Boolean as TINYINT and edgecase MySQL where it still is just tinyint */
|
|
132
|
-
if (databaseClient === 'mysql' && dataType === 'tinyint(1)') {
|
|
133
|
-
return 'boolean';
|
|
134
|
-
}
|
|
135
124
|
return type !== null && type !== void 0 ? type : 'unknown';
|
|
136
125
|
}
|
|
137
126
|
exports.default = getLocalType;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { Accountability } from '@directus/shared/types';
|
|
1
|
+
import { Permission, Accountability } from '@directus/shared/types';
|
|
2
2
|
import { SchemaOverview } from '../types';
|
|
3
|
-
export declare function getPermissions(accountability: Accountability, schema: SchemaOverview): Promise<
|
|
3
|
+
export declare function getPermissions(accountability: Accountability, schema: SchemaOverview): Promise<Permission[]>;
|
|
@@ -16,14 +16,31 @@ const object_hash_1 = __importDefault(require("object-hash"));
|
|
|
16
16
|
const env_1 = __importDefault(require("../env"));
|
|
17
17
|
async function getPermissions(accountability, schema) {
|
|
18
18
|
const database = (0, database_1.default)();
|
|
19
|
-
const { systemCache } = (0, cache_1.getCache)();
|
|
19
|
+
const { systemCache, cache } = (0, cache_1.getCache)();
|
|
20
20
|
let permissions = [];
|
|
21
21
|
const { user, role, app, admin } = accountability;
|
|
22
22
|
const cacheKey = `permissions-${(0, object_hash_1.default)({ user, role, app, admin })}`;
|
|
23
23
|
if (env_1.default.CACHE_PERMISSIONS !== false) {
|
|
24
24
|
const cachedPermissions = await systemCache.get(cacheKey);
|
|
25
25
|
if (cachedPermissions) {
|
|
26
|
-
|
|
26
|
+
if (!cachedPermissions.containDynamicData) {
|
|
27
|
+
return processPermissions(accountability, cachedPermissions.permissions, {});
|
|
28
|
+
}
|
|
29
|
+
const cachedFilterContext = await (cache === null || cache === void 0 ? void 0 : cache.get(`filterContext-${(0, object_hash_1.default)({ user, role, permissions: cachedPermissions.permissions })}`));
|
|
30
|
+
if (cachedFilterContext) {
|
|
31
|
+
return processPermissions(accountability, cachedPermissions.permissions, cachedFilterContext);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
const { permissions: parsedPermissions, requiredPermissionData, containDynamicData, } = parsePermissions(cachedPermissions.permissions);
|
|
35
|
+
permissions = parsedPermissions;
|
|
36
|
+
const filterContext = containDynamicData
|
|
37
|
+
? await getFilterContext(schema, accountability, requiredPermissionData)
|
|
38
|
+
: {};
|
|
39
|
+
if (containDynamicData && env_1.default.CACHE_ENABLED !== false) {
|
|
40
|
+
await (cache === null || cache === void 0 ? void 0 : cache.set(`filterContext-${(0, object_hash_1.default)({ user, role, permissions })}`, filterContext));
|
|
41
|
+
}
|
|
42
|
+
return processPermissions(accountability, permissions, filterContext);
|
|
43
|
+
}
|
|
27
44
|
}
|
|
28
45
|
}
|
|
29
46
|
if (accountability.admin !== true) {
|
|
@@ -31,76 +48,96 @@ async function getPermissions(accountability, schema) {
|
|
|
31
48
|
.select('*')
|
|
32
49
|
.from('directus_permissions')
|
|
33
50
|
.where({ role: accountability.role });
|
|
34
|
-
const requiredPermissionData =
|
|
35
|
-
|
|
36
|
-
$CURRENT_ROLE: [],
|
|
37
|
-
};
|
|
38
|
-
permissions = permissionsForRole.map((permissionRaw) => {
|
|
39
|
-
const permission = (0, lodash_1.cloneDeep)(permissionRaw);
|
|
40
|
-
if (permission.permissions && typeof permission.permissions === 'string') {
|
|
41
|
-
permission.permissions = JSON.parse(permission.permissions);
|
|
42
|
-
}
|
|
43
|
-
else if (permission.permissions === null) {
|
|
44
|
-
permission.permissions = {};
|
|
45
|
-
}
|
|
46
|
-
if (permission.validation && typeof permission.validation === 'string') {
|
|
47
|
-
permission.validation = JSON.parse(permission.validation);
|
|
48
|
-
}
|
|
49
|
-
else if (permission.validation === null) {
|
|
50
|
-
permission.validation = {};
|
|
51
|
-
}
|
|
52
|
-
if (permission.presets && typeof permission.presets === 'string') {
|
|
53
|
-
permission.presets = JSON.parse(permission.presets);
|
|
54
|
-
}
|
|
55
|
-
else if (permission.presets === null) {
|
|
56
|
-
permission.presets = {};
|
|
57
|
-
}
|
|
58
|
-
if (permission.fields && typeof permission.fields === 'string') {
|
|
59
|
-
permission.fields = permission.fields.split(',');
|
|
60
|
-
}
|
|
61
|
-
else if (permission.fields === null) {
|
|
62
|
-
permission.fields = [];
|
|
63
|
-
}
|
|
64
|
-
const extractPermissionData = (val) => {
|
|
65
|
-
if (typeof val === 'string' && val.startsWith('$CURRENT_USER.')) {
|
|
66
|
-
requiredPermissionData.$CURRENT_USER.push(val.replace('$CURRENT_USER.', ''));
|
|
67
|
-
}
|
|
68
|
-
if (typeof val === 'string' && val.startsWith('$CURRENT_ROLE.')) {
|
|
69
|
-
requiredPermissionData.$CURRENT_ROLE.push(val.replace('$CURRENT_ROLE.', ''));
|
|
70
|
-
}
|
|
71
|
-
return val;
|
|
72
|
-
};
|
|
73
|
-
(0, utils_1.deepMap)(permission.permissions, extractPermissionData);
|
|
74
|
-
(0, utils_1.deepMap)(permission.validation, extractPermissionData);
|
|
75
|
-
(0, utils_1.deepMap)(permission.presets, extractPermissionData);
|
|
76
|
-
return permission;
|
|
77
|
-
});
|
|
51
|
+
const { permissions: parsedPermissions, requiredPermissionData, containDynamicData, } = parsePermissions(permissionsForRole);
|
|
52
|
+
permissions = parsedPermissions;
|
|
78
53
|
if (accountability.app === true) {
|
|
79
54
|
permissions = (0, merge_permissions_1.mergePermissions)(permissions, app_access_permissions_1.appAccessMinimalPermissions.map((perm) => ({ ...perm, role: accountability.role })));
|
|
80
55
|
}
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (accountability.user && requiredPermissionData.$CURRENT_USER.length > 0) {
|
|
85
|
-
filterContext.$CURRENT_USER = await usersService.readOne(accountability.user, {
|
|
86
|
-
fields: requiredPermissionData.$CURRENT_USER,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
if (accountability.role && requiredPermissionData.$CURRENT_ROLE.length > 0) {
|
|
90
|
-
filterContext.$CURRENT_ROLE = await rolesService.readOne(accountability.role, {
|
|
91
|
-
fields: requiredPermissionData.$CURRENT_ROLE,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
permissions = permissions.map((permission) => {
|
|
95
|
-
permission.permissions = (0, utils_1.parseFilter)(permission.permissions, accountability, filterContext);
|
|
96
|
-
permission.validation = (0, utils_1.parseFilter)(permission.validation, accountability, filterContext);
|
|
97
|
-
permission.presets = (0, utils_1.parseFilter)(permission.presets, accountability, filterContext);
|
|
98
|
-
return permission;
|
|
99
|
-
});
|
|
56
|
+
const filterContext = containDynamicData
|
|
57
|
+
? await getFilterContext(schema, accountability, requiredPermissionData)
|
|
58
|
+
: {};
|
|
100
59
|
if (env_1.default.CACHE_PERMISSIONS !== false) {
|
|
101
|
-
await systemCache.set(cacheKey, permissions);
|
|
60
|
+
await systemCache.set(cacheKey, { permissions, containDynamicData });
|
|
61
|
+
if (containDynamicData && env_1.default.CACHE_ENABLED !== false) {
|
|
62
|
+
await (cache === null || cache === void 0 ? void 0 : cache.set(`filterContext-${(0, object_hash_1.default)({ user, role, permissions })}`, filterContext));
|
|
63
|
+
}
|
|
102
64
|
}
|
|
65
|
+
return processPermissions(accountability, permissions, filterContext);
|
|
103
66
|
}
|
|
104
67
|
return permissions;
|
|
105
68
|
}
|
|
106
69
|
exports.getPermissions = getPermissions;
|
|
70
|
+
function parsePermissions(permissions) {
|
|
71
|
+
const requiredPermissionData = {
|
|
72
|
+
$CURRENT_USER: [],
|
|
73
|
+
$CURRENT_ROLE: [],
|
|
74
|
+
};
|
|
75
|
+
let containDynamicData = false;
|
|
76
|
+
permissions = permissions.map((permissionRaw) => {
|
|
77
|
+
const permission = (0, lodash_1.cloneDeep)(permissionRaw);
|
|
78
|
+
if (permission.permissions && typeof permission.permissions === 'string') {
|
|
79
|
+
permission.permissions = JSON.parse(permission.permissions);
|
|
80
|
+
}
|
|
81
|
+
else if (permission.permissions === null) {
|
|
82
|
+
permission.permissions = {};
|
|
83
|
+
}
|
|
84
|
+
if (permission.validation && typeof permission.validation === 'string') {
|
|
85
|
+
permission.validation = JSON.parse(permission.validation);
|
|
86
|
+
}
|
|
87
|
+
else if (permission.validation === null) {
|
|
88
|
+
permission.validation = {};
|
|
89
|
+
}
|
|
90
|
+
if (permission.presets && typeof permission.presets === 'string') {
|
|
91
|
+
permission.presets = JSON.parse(permission.presets);
|
|
92
|
+
}
|
|
93
|
+
else if (permission.presets === null) {
|
|
94
|
+
permission.presets = {};
|
|
95
|
+
}
|
|
96
|
+
if (permission.fields && typeof permission.fields === 'string') {
|
|
97
|
+
permission.fields = permission.fields.split(',');
|
|
98
|
+
}
|
|
99
|
+
else if (permission.fields === null) {
|
|
100
|
+
permission.fields = [];
|
|
101
|
+
}
|
|
102
|
+
const extractPermissionData = (val) => {
|
|
103
|
+
if (typeof val === 'string' && val.startsWith('$CURRENT_USER.')) {
|
|
104
|
+
requiredPermissionData.$CURRENT_USER.push(val.replace('$CURRENT_USER.', ''));
|
|
105
|
+
containDynamicData = true;
|
|
106
|
+
}
|
|
107
|
+
if (typeof val === 'string' && val.startsWith('$CURRENT_ROLE.')) {
|
|
108
|
+
requiredPermissionData.$CURRENT_ROLE.push(val.replace('$CURRENT_ROLE.', ''));
|
|
109
|
+
containDynamicData = true;
|
|
110
|
+
}
|
|
111
|
+
return val;
|
|
112
|
+
};
|
|
113
|
+
(0, utils_1.deepMap)(permission.permissions, extractPermissionData);
|
|
114
|
+
(0, utils_1.deepMap)(permission.validation, extractPermissionData);
|
|
115
|
+
(0, utils_1.deepMap)(permission.presets, extractPermissionData);
|
|
116
|
+
return permission;
|
|
117
|
+
});
|
|
118
|
+
return { permissions, requiredPermissionData, containDynamicData };
|
|
119
|
+
}
|
|
120
|
+
async function getFilterContext(schema, accountability, requiredPermissionData) {
|
|
121
|
+
const usersService = new users_1.UsersService({ schema });
|
|
122
|
+
const rolesService = new roles_1.RolesService({ schema });
|
|
123
|
+
const filterContext = {};
|
|
124
|
+
if (accountability.user && requiredPermissionData.$CURRENT_USER.length > 0) {
|
|
125
|
+
filterContext.$CURRENT_USER = await usersService.readOne(accountability.user, {
|
|
126
|
+
fields: requiredPermissionData.$CURRENT_USER,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (accountability.role && requiredPermissionData.$CURRENT_ROLE.length > 0) {
|
|
130
|
+
filterContext.$CURRENT_ROLE = await rolesService.readOne(accountability.role, {
|
|
131
|
+
fields: requiredPermissionData.$CURRENT_ROLE,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return filterContext;
|
|
135
|
+
}
|
|
136
|
+
function processPermissions(accountability, permissions, filterContext) {
|
|
137
|
+
return permissions.map((permission) => {
|
|
138
|
+
permission.permissions = (0, utils_1.parseFilter)(permission.permissions, accountability, filterContext);
|
|
139
|
+
permission.validation = (0, utils_1.parseFilter)(permission.validation, accountability, filterContext);
|
|
140
|
+
permission.presets = (0, utils_1.parseFilter)(permission.presets, accountability, filterContext);
|
|
141
|
+
return permission;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
@@ -106,17 +106,6 @@ function sanitizeFilter(rawFilter, accountability) {
|
|
|
106
106
|
logger_1.default.warn('Invalid value passed for filter query parameter.');
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
-
filters = (0, utils_1.deepMap)(filters, (val) => {
|
|
110
|
-
try {
|
|
111
|
-
const parsed = JSON.parse(val);
|
|
112
|
-
if (typeof parsed == 'number' && !Number.isSafeInteger(parsed))
|
|
113
|
-
return val;
|
|
114
|
-
return parsed;
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
return val;
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
109
|
return (0, utils_1.parseFilter)(filters, accountability);
|
|
121
110
|
}
|
|
122
111
|
function sanitizeLimit(rawLimit) {
|
|
@@ -167,7 +156,7 @@ function sanitizeDeep(deep, accountability) {
|
|
|
167
156
|
const [parsedKey, parsedValue] = Object.entries(parsedSubQuery)[0];
|
|
168
157
|
parsedLevel[`_${parsedKey}`] = parsedValue;
|
|
169
158
|
}
|
|
170
|
-
else {
|
|
159
|
+
else if ((0, lodash_1.isPlainObject)(value)) {
|
|
171
160
|
parse(value, [...path, key]);
|
|
172
161
|
}
|
|
173
162
|
}
|
|
@@ -100,7 +100,7 @@ function validateFilterPrimitive(value, key) {
|
|
|
100
100
|
false) {
|
|
101
101
|
throw new exceptions_1.InvalidQueryException(`The filter value for "${key}" has to be a string, number, or boolean`);
|
|
102
102
|
}
|
|
103
|
-
if (typeof value === 'number' && Number.isNaN(value)) {
|
|
103
|
+
if (typeof value === 'number' && (Number.isNaN(value) || !Number.isSafeInteger(value))) {
|
|
104
104
|
throw new exceptions_1.InvalidQueryException(`The filter value for "${key}" is not a valid number`);
|
|
105
105
|
}
|
|
106
106
|
if (typeof value === 'string' && value.length === 0) {
|
package/dist/webhooks.js
CHANGED
|
@@ -8,7 +8,6 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
8
8
|
const database_1 = __importDefault(require("./database"));
|
|
9
9
|
const emitter_1 = __importDefault(require("./emitter"));
|
|
10
10
|
const logger_1 = __importDefault(require("./logger"));
|
|
11
|
-
const lodash_1 = require("lodash");
|
|
12
11
|
const services_1 = require("./services");
|
|
13
12
|
const get_schema_1 = require("./utils/get-schema");
|
|
14
13
|
let registered = [];
|
|
@@ -17,20 +16,12 @@ async function register() {
|
|
|
17
16
|
const webhookService = new services_1.WebhooksService({ knex: (0, database_1.default)(), schema: await (0, get_schema_1.getSchema)() });
|
|
18
17
|
const webhooks = await webhookService.readByQuery({ filter: { status: { _eq: 'active' } } });
|
|
19
18
|
for (const webhook of webhooks) {
|
|
20
|
-
|
|
21
|
-
const event =
|
|
22
|
-
const handler = createHandler(webhook);
|
|
19
|
+
for (const action of webhook.actions) {
|
|
20
|
+
const event = `items.${action}`;
|
|
21
|
+
const handler = createHandler(webhook, event);
|
|
23
22
|
emitter_1.default.onAction(event, handler);
|
|
24
23
|
registered.push({ event, handler });
|
|
25
24
|
}
|
|
26
|
-
else {
|
|
27
|
-
for (const action of webhook.actions) {
|
|
28
|
-
const event = `items.${action}`;
|
|
29
|
-
const handler = createHandler(webhook);
|
|
30
|
-
emitter_1.default.onAction(event, handler);
|
|
31
|
-
registered.push({ event, handler });
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
25
|
}
|
|
35
26
|
}
|
|
36
27
|
exports.register = register;
|
|
@@ -41,19 +32,20 @@ function unregister() {
|
|
|
41
32
|
registered = [];
|
|
42
33
|
}
|
|
43
34
|
exports.unregister = unregister;
|
|
44
|
-
function createHandler(webhook) {
|
|
45
|
-
return async (
|
|
46
|
-
if (webhook.collections.includes(
|
|
35
|
+
function createHandler(webhook, event) {
|
|
36
|
+
return async (meta, context) => {
|
|
37
|
+
if (webhook.collections.includes(meta.collection) === false)
|
|
47
38
|
return;
|
|
48
|
-
const webhookPayload =
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
39
|
+
const webhookPayload = {
|
|
40
|
+
event,
|
|
41
|
+
accountability: context.accountability
|
|
42
|
+
? {
|
|
43
|
+
user: context.accountability.user,
|
|
44
|
+
role: context.accountability.role,
|
|
45
|
+
}
|
|
46
|
+
: null,
|
|
47
|
+
...meta,
|
|
48
|
+
};
|
|
57
49
|
try {
|
|
58
50
|
await (0, axios_1.default)({
|
|
59
51
|
url: webhook.url,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "directus",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.2.2",
|
|
4
4
|
"license": "GPL-3.0-only",
|
|
5
5
|
"homepage": "https://github.com/directus/directus#readme",
|
|
6
6
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content.",
|
|
@@ -76,16 +76,16 @@
|
|
|
76
76
|
],
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"@aws-sdk/client-ses": "^3.40.0",
|
|
79
|
-
"@directus/app": "9.
|
|
80
|
-
"@directus/drive": "9.
|
|
81
|
-
"@directus/drive-azure": "9.
|
|
82
|
-
"@directus/drive-gcs": "9.
|
|
83
|
-
"@directus/drive-s3": "9.
|
|
84
|
-
"@directus/extensions-sdk": "9.
|
|
85
|
-
"@directus/format-title": "9.
|
|
86
|
-
"@directus/schema": "9.
|
|
87
|
-
"@directus/shared": "9.
|
|
88
|
-
"@directus/specs": "9.
|
|
79
|
+
"@directus/app": "9.2.2",
|
|
80
|
+
"@directus/drive": "9.2.2",
|
|
81
|
+
"@directus/drive-azure": "9.2.2",
|
|
82
|
+
"@directus/drive-gcs": "9.2.2",
|
|
83
|
+
"@directus/drive-s3": "9.2.2",
|
|
84
|
+
"@directus/extensions-sdk": "9.2.2",
|
|
85
|
+
"@directus/format-title": "9.2.2",
|
|
86
|
+
"@directus/schema": "9.2.2",
|
|
87
|
+
"@directus/shared": "9.2.2",
|
|
88
|
+
"@directus/specs": "9.2.2",
|
|
89
89
|
"@godaddy/terminus": "^4.9.0",
|
|
90
90
|
"@rollup/plugin-alias": "^3.1.2",
|
|
91
91
|
"@rollup/plugin-virtual": "^2.0.3",
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
"jsonwebtoken": "^8.5.1",
|
|
123
123
|
"keyv": "^4.0.3",
|
|
124
124
|
"knex": "^0.95.11",
|
|
125
|
-
"knex-schema-inspector": "1.6.
|
|
125
|
+
"knex-schema-inspector": "1.6.6",
|
|
126
126
|
"ldapjs": "^2.3.1",
|
|
127
127
|
"liquidjs": "^9.25.0",
|
|
128
128
|
"lodash": "^4.17.21",
|
|
@@ -169,7 +169,7 @@
|
|
|
169
169
|
"sqlite3": "^5.0.2",
|
|
170
170
|
"tedious": "^13.0.0"
|
|
171
171
|
},
|
|
172
|
-
"gitHead": "
|
|
172
|
+
"gitHead": "546d525175c7ecc6ac9c235b4ebf77a1ae209e10",
|
|
173
173
|
"devDependencies": {
|
|
174
174
|
"@types/async": "3.2.10",
|
|
175
175
|
"@types/atob": "2.1.2",
|
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
"@types/nodemailer": "6.4.4",
|
|
200
200
|
"@types/object-hash": "2.2.1",
|
|
201
201
|
"@types/qs": "6.9.7",
|
|
202
|
-
"@types/sanitize-html": "
|
|
202
|
+
"@types/sanitize-html": "2.5.0",
|
|
203
203
|
"@types/sharp": "0.29.4",
|
|
204
204
|
"@types/stream-json": "1.7.1",
|
|
205
205
|
"@types/supertest": "2.0.11",
|
|
@@ -209,6 +209,7 @@
|
|
|
209
209
|
"copyfiles": "2.4.1",
|
|
210
210
|
"cross-env": "7.0.3",
|
|
211
211
|
"jest": "27.3.1",
|
|
212
|
+
"knex-mock-client": "^1.6.1",
|
|
212
213
|
"ts-jest": "27.0.7",
|
|
213
214
|
"ts-node-dev": "1.1.8",
|
|
214
215
|
"typescript": "4.5.2"
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { Knex } from 'knex';
|
|
2
|
-
import { HelperFn } from '../types';
|
|
3
|
-
export declare class HelperOracle implements HelperFn {
|
|
4
|
-
private knex;
|
|
5
|
-
constructor(knex: Knex);
|
|
6
|
-
year(table: string, column: string): Knex.Raw;
|
|
7
|
-
month(table: string, column: string): Knex.Raw;
|
|
8
|
-
week(table: string, column: string): Knex.Raw;
|
|
9
|
-
day(table: string, column: string): Knex.Raw;
|
|
10
|
-
weekday(table: string, column: string): Knex.Raw;
|
|
11
|
-
hour(table: string, column: string): Knex.Raw;
|
|
12
|
-
minute(table: string, column: string): Knex.Raw;
|
|
13
|
-
second(table: string, column: string): Knex.Raw;
|
|
14
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { Knex } from 'knex';
|
|
2
|
-
import { HelperFn } from '../types';
|
|
3
|
-
export declare class HelperPostgres implements HelperFn {
|
|
4
|
-
private knex;
|
|
5
|
-
constructor(knex: Knex);
|
|
6
|
-
year(table: string, column: string): Knex.Raw;
|
|
7
|
-
month(table: string, column: string): Knex.Raw;
|
|
8
|
-
week(table: string, column: string): Knex.Raw;
|
|
9
|
-
day(table: string, column: string): Knex.Raw;
|
|
10
|
-
weekday(table: string, column: string): Knex.Raw;
|
|
11
|
-
hour(table: string, column: string): Knex.Raw;
|
|
12
|
-
minute(table: string, column: string): Knex.Raw;
|
|
13
|
-
second(table: string, column: string): Knex.Raw;
|
|
14
|
-
}
|