directus 9.7.0 → 9.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -235,9 +235,6 @@ exports.validateDatabaseExtensions = validateDatabaseExtensions;
235
235
  async function validateDatabaseCharset(database) {
236
236
  database = database !== null && database !== void 0 ? database : getDatabase();
237
237
  if (getDatabaseClient(database) === 'mysql') {
238
- if (env_1.default.DB_CHARSET) {
239
- logger_1.default.warn(`Using custom DB_CHARSET "${env_1.default.DB_CHARSET}". Using a charset different from the database's default can cause problems in relationships. Omitting DB_CHARSET is strongly recommended.`);
240
- }
241
238
  const { collation } = await database.select(database.raw(`@@collation_database as collation`)).first();
242
239
  const tables = await database('information_schema.tables')
243
240
  .select({ name: 'TABLE_NAME', collation: 'TABLE_COLLATION' })
@@ -0,0 +1,3 @@
1
+ import { Knex } from 'knex';
2
+ export declare function up(knex: Knex): Promise<void>;
3
+ export declare function down(knex: Knex): Promise<void>;
@@ -0,0 +1,15 @@
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_settings', (table) => {
6
+ table.json('translation_strings');
7
+ });
8
+ }
9
+ exports.up = up;
10
+ async function down(knex) {
11
+ await knex.schema.alterTable('directus_settings', (table) => {
12
+ table.dropColumn('translation_strings');
13
+ });
14
+ }
15
+ exports.down = down;
@@ -0,0 +1,3 @@
1
+ import { Knex } from 'knex';
2
+ export declare function up(knex: Knex): Promise<void>;
3
+ export declare function down(knex: Knex): Promise<void>;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.down = exports.up = void 0;
4
+ const utils_1 = require("@directus/shared/utils");
5
+ const lodash_1 = require("lodash");
6
+ async function up(knex) {
7
+ const fields = await knex
8
+ .select('id', 'special')
9
+ .from('directus_fields')
10
+ .whereNotNull('special')
11
+ .orWhere('special', '<>', '');
12
+ for (const { id, special } of fields) {
13
+ let parsedSpecial;
14
+ try {
15
+ parsedSpecial = (0, utils_1.toArray)(special);
16
+ }
17
+ catch {
18
+ continue;
19
+ }
20
+ if (parsedSpecial && (0, lodash_1.isArray)(parsedSpecial)) {
21
+ let updateRequired = false;
22
+ parsedSpecial = parsedSpecial.map((special) => {
23
+ switch (special) {
24
+ case 'boolean':
25
+ case 'csv':
26
+ case 'json':
27
+ updateRequired = true;
28
+ return 'cast-' + special;
29
+ default:
30
+ return special;
31
+ }
32
+ });
33
+ if (updateRequired) {
34
+ await knex('directus_fields').update({ special: parsedSpecial }).where({ id });
35
+ }
36
+ }
37
+ }
38
+ }
39
+ exports.up = up;
40
+ async function down(knex) {
41
+ const fields = await knex
42
+ .select('id', 'special')
43
+ .from('directus_fields')
44
+ .whereNotNull('special')
45
+ .orWhere('special', '<>', '');
46
+ for (const { id, special } of fields) {
47
+ let parsedSpecial;
48
+ try {
49
+ parsedSpecial = (0, utils_1.toArray)(special);
50
+ }
51
+ catch {
52
+ continue;
53
+ }
54
+ if (parsedSpecial && (0, lodash_1.isArray)(parsedSpecial)) {
55
+ let updateRequired = false;
56
+ parsedSpecial = parsedSpecial.map((special) => {
57
+ switch (special) {
58
+ case 'cast-boolean':
59
+ case 'cast-csv':
60
+ case 'cast-json':
61
+ updateRequired = true;
62
+ return special.replace('cast-', '');
63
+ default:
64
+ return special;
65
+ }
66
+ });
67
+ if (updateRequired) {
68
+ await knex('directus_fields').update({ special: parsedSpecial }).where({ id });
69
+ }
70
+ }
71
+ }
72
+ }
73
+ exports.down = down;
@@ -63,7 +63,7 @@ async function run(database, direction, log = true) {
63
63
  await database.insert({ version: nextVersion.version, name: nextVersion.name }).into('directus_migrations');
64
64
  }
65
65
  async function down() {
66
- const lastAppliedMigration = (0, lodash_1.orderBy)(completedMigrations, ['timestamp'], ['desc'])[0];
66
+ const lastAppliedMigration = (0, lodash_1.orderBy)(completedMigrations, ['timestamp', 'version'], ['desc', 'desc'])[0];
67
67
  if (!lastAppliedMigration) {
68
68
  throw Error('Nothing to downgrade');
69
69
  }
@@ -42,21 +42,21 @@ fields:
42
42
  width: full
43
43
 
44
44
  - field: hidden
45
- special: boolean
45
+ special: cast-boolean
46
46
  interface: boolean
47
47
  options:
48
48
  label: $t:field_options.directus_collections.hidden_label
49
49
  width: half
50
50
 
51
51
  - field: singleton
52
- special: boolean
52
+ special: cast-boolean
53
53
  interface: boolean
54
54
  options:
55
55
  label: $t:singleton_label
56
56
  width: half
57
57
 
58
58
  - field: translations
59
- special: json
59
+ special: cast-json
60
60
  interface: list
61
61
  options:
62
62
  template: '{{ translation }} ({{ language }})'
@@ -115,7 +115,7 @@ fields:
115
115
 
116
116
  - field: archive_app_filter
117
117
  interface: boolean
118
- special: boolean
118
+ special: cast-boolean
119
119
  options:
120
120
  label: $t:field_options.directus_collections.archive_app_filter
121
121
  width: half
@@ -17,7 +17,7 @@ fields:
17
17
  - collection: directus_fields
18
18
  field: special
19
19
  hidden: true
20
- special: csv
20
+ special: cast-csv
21
21
  width: half
22
22
 
23
23
  - collection: directus_fields
@@ -27,7 +27,7 @@ fields:
27
27
  - collection: directus_fields
28
28
  field: options
29
29
  hidden: true
30
- special: json
30
+ special: cast-json
31
31
  width: half
32
32
 
33
33
  - collection: directus_fields
@@ -37,25 +37,25 @@ fields:
37
37
  - collection: directus_fields
38
38
  field: display_options
39
39
  hidden: true
40
- special: json
40
+ special: cast-json
41
41
  width: half
42
42
 
43
43
  - collection: directus_fields
44
44
  field: readonly
45
45
  hidden: true
46
- special: boolean
46
+ special: cast-boolean
47
47
  width: half
48
48
 
49
49
  - collection: directus_fields
50
50
  field: hidden
51
51
  hidden: true
52
- special: boolean
52
+ special: cast-boolean
53
53
  width: half
54
54
 
55
55
  - collection: directus_fields
56
56
  field: required
57
57
  hidden: true
58
- special: boolean
58
+ special: cast-boolean
59
59
  width: half
60
60
 
61
61
  - collection: directus_fields
@@ -73,7 +73,7 @@ fields:
73
73
  - collection: directus_fields
74
74
  field: translations
75
75
  hidden: true
76
- special: json
76
+ special: cast-json
77
77
  width: half
78
78
 
79
79
  - collection: directus_fields
@@ -83,4 +83,4 @@ fields:
83
83
  - collection: directus_fields
84
84
  field: conditions
85
85
  hidden: true
86
- special: json
86
+ special: cast-json
@@ -23,7 +23,7 @@ fields:
23
23
  interface: tags
24
24
  options:
25
25
  iconRight: local_offer
26
- special: json
26
+ special: cast-json
27
27
  width: full
28
28
  display: labels
29
29
  display_options:
@@ -71,7 +71,7 @@ fields:
71
71
 
72
72
  - field: metadata
73
73
  hidden: true
74
- special: json
74
+ special: cast-json
75
75
 
76
76
  - field: type
77
77
  display: mime-type
@@ -9,13 +9,13 @@ fields:
9
9
  - field: note
10
10
  - field: type
11
11
  - field: show_header
12
- special: boolean
12
+ special: cast-boolean
13
13
  - field: position_x
14
14
  - field: position_y
15
15
  - field: width
16
16
  - field: height
17
17
  - field: options
18
- special: json
18
+ special: cast-json
19
19
  - field: date_created
20
20
  special: date-created
21
21
  - field: user_created
@@ -4,12 +4,12 @@ table: directus_permissions
4
4
  fields:
5
5
  - field: permissions
6
6
  hidden: true
7
- special: json
7
+ special: cast-json
8
8
  width: half
9
9
 
10
10
  - field: presets
11
11
  hidden: true
12
- special: json
12
+ special: cast-json
13
13
  width: half
14
14
 
15
15
  - field: role
@@ -23,11 +23,11 @@ fields:
23
23
 
24
24
  - field: fields
25
25
  width: half
26
- special: csv
26
+ special: cast-csv
27
27
 
28
28
  - field: action
29
29
  width: half
30
30
 
31
31
  - field: validation
32
32
  width: half
33
- special: json
33
+ special: cast-json
@@ -3,15 +3,15 @@ table: directus_presets
3
3
  fields:
4
4
  - field: filter
5
5
  hidden: true
6
- special: json
6
+ special: cast-json
7
7
 
8
8
  - field: layout_query
9
9
  hidden: true
10
- special: json
10
+ special: cast-json
11
11
 
12
12
  - field: layout_options
13
13
  hidden: true
14
- special: json
14
+ special: cast-json
15
15
 
16
16
  - field: role
17
17
  width: half
@@ -20,7 +20,7 @@ fields:
20
20
  width: half
21
21
 
22
22
  - field: one_allowed_collections
23
- special: csv
23
+ special: cast-csv
24
24
  width: half
25
25
 
26
26
  - field: junction_field
@@ -15,11 +15,11 @@ fields:
15
15
 
16
16
  - field: data
17
17
  hidden: true
18
- special: json
18
+ special: cast-json
19
19
 
20
20
  - field: delta
21
21
  hidden: true
22
- special: json
22
+ special: cast-json
23
23
 
24
24
  - field: parent
25
25
  width: half
@@ -25,24 +25,24 @@ fields:
25
25
 
26
26
  - field: app_access
27
27
  interface: boolean
28
- special: boolean
28
+ special: cast-boolean
29
29
  width: half
30
30
 
31
31
  - field: admin_access
32
32
  interface: boolean
33
- special: boolean
33
+ special: cast-boolean
34
34
  width: half
35
35
 
36
36
  - field: ip_access
37
37
  interface: tags
38
38
  options:
39
39
  placeholder: $t:field_options.directus_roles.ip_access
40
- special: csv
40
+ special: cast-csv
41
41
  width: full
42
42
 
43
43
  - field: enforce_tfa
44
44
  interface: boolean
45
- special: boolean
45
+ special: cast-boolean
46
46
  width: half
47
47
 
48
48
  - field: users
@@ -121,7 +121,7 @@ fields:
121
121
 
122
122
  - field: module_bar
123
123
  interface: system-modules
124
- special: json
124
+ special: cast-json
125
125
 
126
126
  - field: security_divider
127
127
  interface: presentation-divider
@@ -297,7 +297,7 @@ fields:
297
297
  ]
298
298
  width: full
299
299
  template: '{{key}}'
300
- special: json
300
+ special: cast-json
301
301
  width: full
302
302
 
303
303
  - field: map_divider
@@ -322,7 +322,7 @@ fields:
322
322
 
323
323
  - field: basemaps
324
324
  interface: list
325
- special: json
325
+ special: cast-json
326
326
  options:
327
327
  template: '{{name}}'
328
328
  fields:
@@ -378,3 +378,6 @@ fields:
378
378
  interface: input
379
379
  options:
380
380
  placeholder: $t:fields.directus_settings.attribution_placeholder
381
+
382
+ - field: translation_strings
383
+ hidden: true
@@ -50,7 +50,7 @@ fields:
50
50
 
51
51
  - field: tags
52
52
  interface: tags
53
- special: json
53
+ special: cast-json
54
54
  width: full
55
55
  options:
56
56
  iconRight: local_offer
@@ -93,7 +93,7 @@ fields:
93
93
  - field: email_notifications
94
94
  interface: boolean
95
95
  width: half
96
- special: boolean
96
+ special: cast-boolean
97
97
 
98
98
  - field: admin_divider
99
99
  interface: presentation-divider
@@ -60,12 +60,12 @@ fields:
60
60
  interface: boolean
61
61
  options:
62
62
  label: $t:fields.directus_webhooks.data_label
63
- special: boolean
63
+ special: cast-boolean
64
64
  width: half
65
65
  display: boolean
66
66
 
67
67
  - field: headers
68
- special: json
68
+ special: cast-json
69
69
  interface: list
70
70
  options:
71
71
  template: '{{ header }}: {{ value }}'
@@ -105,7 +105,7 @@ fields:
105
105
  value: update
106
106
  - text: $t:delete_label
107
107
  value: delete
108
- special: csv
108
+ special: cast-csv
109
109
  width: full
110
110
  display: labels
111
111
  display_options:
@@ -129,7 +129,7 @@ fields:
129
129
 
130
130
  - field: collections
131
131
  interface: system-collections
132
- special: csv
132
+ special: cast-csv
133
133
  width: full
134
134
  display: labels
135
135
  display_options:
package/dist/env.js CHANGED
@@ -235,6 +235,7 @@ function processValues(env) {
235
235
  }
236
236
  if (String(value).includes(',')) {
237
237
  env[key] = (0, utils_1.toArray)(value);
238
+ continue;
238
239
  }
239
240
  // Try converting the value to a JS object. This allows JSON objects to be passed for nested
240
241
  // config flags, or custom param names (that aren't camelCased)
@@ -35,6 +35,7 @@ class AuthorizationService {
35
35
  throw new exceptions_1.ForbiddenException();
36
36
  }
37
37
  validateFields(ast);
38
+ validateFilterPermissions(ast, this.schema, this.accountability);
38
39
  applyFilters(ast, this.accountability);
39
40
  return ast;
40
41
  /**
@@ -105,6 +106,63 @@ class AuthorizationService {
105
106
  }
106
107
  }
107
108
  }
109
+ function validateFilterPermissions(ast, schema, accountability) {
110
+ var _a, _b, _c, _d, _e;
111
+ if (ast.type !== 'field') {
112
+ if (ast.type === 'a2o') {
113
+ for (const collection of Object.keys(ast.children)) {
114
+ checkFilter(collection, (_c = (_b = (_a = ast.query) === null || _a === void 0 ? void 0 : _a[collection]) === null || _b === void 0 ? void 0 : _b.filter) !== null && _c !== void 0 ? _c : {});
115
+ for (const child of ast.children[collection]) {
116
+ validateFilterPermissions(child, schema, accountability);
117
+ }
118
+ }
119
+ }
120
+ else {
121
+ checkFilter(ast.name, (_e = (_d = ast.query) === null || _d === void 0 ? void 0 : _d.filter) !== null && _e !== void 0 ? _e : {});
122
+ for (const child of ast.children) {
123
+ validateFilterPermissions(child, schema, accountability);
124
+ }
125
+ }
126
+ }
127
+ function checkFilter(collection, filter) {
128
+ var _a;
129
+ const permissions = (_a = accountability === null || accountability === void 0 ? void 0 : accountability.permissions) === null || _a === void 0 ? void 0 : _a.find((permission) => permission.collection === collection);
130
+ if (!permissions)
131
+ throw new exceptions_1.ForbiddenException();
132
+ const allowedFields = permissions.fields || [];
133
+ for (const [key, value] of Object.entries(filter)) {
134
+ if (key.startsWith('_')) {
135
+ // Continue checking for _and and _or
136
+ if ((0, lodash_1.isArray)(value)) {
137
+ for (const val of value) {
138
+ checkFilter(collection, val);
139
+ }
140
+ }
141
+ }
142
+ else {
143
+ if (allowedFields.length !== 0 &&
144
+ allowedFields.includes('*') === false &&
145
+ allowedFields.includes(key) === false) {
146
+ throw new exceptions_1.ForbiddenException();
147
+ }
148
+ const relation = schema.relations.find((relation) => {
149
+ var _a;
150
+ return ((relation.collection === collection && relation.field === key) ||
151
+ (relation.related_collection === collection && ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) === key));
152
+ });
153
+ // Field is a relation
154
+ if (relation) {
155
+ if (relation.related_collection === collection) {
156
+ checkFilter(relation.collection, value);
157
+ }
158
+ else {
159
+ checkFilter(relation.related_collection, value);
160
+ }
161
+ }
162
+ }
163
+ }
164
+ }
165
+ }
108
166
  function applyFilters(ast, accountability) {
109
167
  if (ast.type !== 'field') {
110
168
  if (ast.type === 'a2o') {
@@ -496,6 +496,7 @@ class ItemsService {
496
496
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
497
497
  emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.delete', `${this.collection}.items.delete`] : `${this.eventScope}.delete`, {
498
498
  payload: keys,
499
+ keys: keys,
499
500
  collection: this.collection,
500
501
  }, {
501
502
  // This hook is called async. If we would pass the transaction here, the hook can be
@@ -37,7 +37,7 @@ class PayloadService {
37
37
  }
38
38
  return value;
39
39
  },
40
- async boolean({ action, value }) {
40
+ async 'cast-boolean'({ action, value }) {
41
41
  if (action === 'read') {
42
42
  if (value === true || value === 1 || value === '1') {
43
43
  return true;
@@ -51,7 +51,7 @@ class PayloadService {
51
51
  }
52
52
  return value;
53
53
  },
54
- async json({ action, value }) {
54
+ async 'cast-json'({ action, value }) {
55
55
  if (action === 'read') {
56
56
  if (typeof value === 'string') {
57
57
  try {
@@ -99,7 +99,7 @@ class PayloadService {
99
99
  return new Date();
100
100
  return value;
101
101
  },
102
- async csv({ action, value }) {
102
+ async 'cast-csv'({ action, value }) {
103
103
  if (Array.isArray(value) === false && typeof value !== 'string')
104
104
  return;
105
105
  if (action === 'read' && Array.isArray(value) === false) {
@@ -101,11 +101,11 @@ function getLocalType(column, field) {
101
101
  const type = localTypeMap[dataType.split('(')[0]];
102
102
  const special = field === null || field === void 0 ? void 0 : field.special;
103
103
  if (special) {
104
- if (special.includes('json'))
104
+ if (special.includes('cast-json'))
105
105
  return 'json';
106
106
  if (special.includes('hash'))
107
107
  return 'hash';
108
- if (special.includes('csv'))
108
+ if (special.includes('cast-csv'))
109
109
  return 'csv';
110
110
  if (special.includes('uuid') || special.includes('file'))
111
111
  return 'uuid';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "directus",
3
- "version": "9.7.0",
3
+ "version": "9.7.1",
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.",
@@ -78,16 +78,16 @@
78
78
  ],
79
79
  "dependencies": {
80
80
  "@aws-sdk/client-ses": "^3.40.0",
81
- "@directus/app": "9.7.0",
82
- "@directus/drive": "9.7.0",
83
- "@directus/drive-azure": "9.7.0",
84
- "@directus/drive-gcs": "9.7.0",
85
- "@directus/drive-s3": "9.7.0",
86
- "@directus/extensions-sdk": "9.7.0",
87
- "@directus/format-title": "9.7.0",
88
- "@directus/schema": "9.7.0",
89
- "@directus/shared": "9.7.0",
90
- "@directus/specs": "9.7.0",
81
+ "@directus/app": "9.7.1",
82
+ "@directus/drive": "9.7.1",
83
+ "@directus/drive-azure": "9.7.1",
84
+ "@directus/drive-gcs": "9.7.1",
85
+ "@directus/drive-s3": "9.7.1",
86
+ "@directus/extensions-sdk": "9.7.1",
87
+ "@directus/format-title": "9.7.1",
88
+ "@directus/schema": "9.7.1",
89
+ "@directus/shared": "9.7.1",
90
+ "@directus/specs": "9.7.1",
91
91
  "@godaddy/terminus": "^4.9.0",
92
92
  "@rollup/plugin-alias": "^3.1.9",
93
93
  "@rollup/plugin-virtual": "^2.0.3",
@@ -173,7 +173,7 @@
173
173
  "sqlite3": "^5.0.2",
174
174
  "tedious": "^13.0.0"
175
175
  },
176
- "gitHead": "c1da41d6719d4efdc5d0196019fb7b2c6672c575",
176
+ "gitHead": "7b1594727686ab8bfac416d851b1f0063f38f893",
177
177
  "devDependencies": {
178
178
  "@types/async": "3.2.10",
179
179
  "@types/body-parser": "1.19.2",