directus 9.3.0 → 9.4.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.
Files changed (70) hide show
  1. package/dist/app.js +5 -3
  2. package/dist/auth/auth.d.ts +4 -6
  3. package/dist/auth/auth.js +5 -9
  4. package/dist/auth/drivers/ldap.d.ts +3 -3
  5. package/dist/auth/drivers/ldap.js +0 -2
  6. package/dist/auth/drivers/local.d.ts +2 -2
  7. package/dist/auth/drivers/local.js +5 -12
  8. package/dist/auth/drivers/oauth2.d.ts +3 -3
  9. package/dist/auth/drivers/oauth2.js +2 -3
  10. package/dist/auth/drivers/openid.d.ts +3 -3
  11. package/dist/auth/drivers/openid.js +2 -3
  12. package/dist/cli/commands/schema/apply.js +1 -1
  13. package/dist/constants.d.ts +8 -0
  14. package/dist/constants.js +16 -2
  15. package/dist/controllers/shares.d.ts +2 -0
  16. package/dist/controllers/shares.js +212 -0
  17. package/dist/controllers/users.js +21 -9
  18. package/dist/database/migrations/20211211A-add-shares.d.ts +3 -0
  19. package/dist/database/migrations/20211211A-add-shares.js +37 -0
  20. package/dist/database/run-ast.js +5 -5
  21. package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +0 -15
  22. package/dist/database/system-data/app-access-permissions/index.d.ts +1 -0
  23. package/dist/database/system-data/app-access-permissions/index.js +4 -2
  24. package/dist/database/system-data/app-access-permissions/schema-access-permissions.yaml +17 -0
  25. package/dist/database/system-data/collections/collections.yaml +3 -0
  26. package/dist/database/system-data/fields/sessions.yaml +1 -1
  27. package/dist/database/system-data/fields/shares.yaml +73 -0
  28. package/dist/database/system-data/fields/users.yaml +1 -1
  29. package/dist/database/system-data/relations/relations.yaml +15 -0
  30. package/dist/extensions.js +5 -3
  31. package/dist/middleware/authenticate.js +5 -15
  32. package/dist/middleware/check-ip.js +9 -6
  33. package/dist/middleware/respond.js +4 -1
  34. package/dist/services/activity.d.ts +2 -1
  35. package/dist/services/activity.js +2 -2
  36. package/dist/services/authentication.d.ts +2 -7
  37. package/dist/services/authentication.js +81 -41
  38. package/dist/services/authorization.js +3 -3
  39. package/dist/services/collections.d.ts +1 -2
  40. package/dist/services/collections.js +2 -2
  41. package/dist/services/files.d.ts +2 -2
  42. package/dist/services/graphql.js +16 -5
  43. package/dist/services/index.d.ts +1 -0
  44. package/dist/services/index.js +1 -0
  45. package/dist/services/items.d.ts +1 -15
  46. package/dist/services/notifications.d.ts +2 -2
  47. package/dist/services/permissions.d.ts +2 -2
  48. package/dist/services/roles.d.ts +2 -2
  49. package/dist/services/shares.d.ts +17 -0
  50. package/dist/services/shares.js +135 -0
  51. package/dist/services/specifications.js +1 -1
  52. package/dist/services/users.d.ts +2 -2
  53. package/dist/services/webhooks.d.ts +2 -2
  54. package/dist/types/ast.d.ts +3 -3
  55. package/dist/types/auth.d.ts +31 -0
  56. package/dist/types/items.d.ts +14 -0
  57. package/dist/utils/apply-query.d.ts +0 -38
  58. package/dist/utils/apply-query.js +66 -67
  59. package/dist/utils/get-ast-from-query.js +3 -3
  60. package/dist/utils/get-permissions.js +15 -7
  61. package/dist/utils/get-relation-type.d.ts +1 -1
  62. package/dist/utils/get-relation-type.js +1 -1
  63. package/dist/utils/merge-permissions-for-share.d.ts +5 -0
  64. package/dist/utils/merge-permissions-for-share.js +116 -0
  65. package/dist/utils/merge-permissions.d.ts +13 -1
  66. package/dist/utils/merge-permissions.js +27 -19
  67. package/dist/utils/reduce-schema.d.ts +2 -2
  68. package/dist/utils/reduce-schema.js +7 -7
  69. package/dist/utils/user-name.js +3 -0
  70. package/package.json +12 -12
@@ -0,0 +1,37 @@
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.createTable('directus_shares', (table) => {
6
+ table.uuid('id').primary();
7
+ table.string('name');
8
+ table.string('collection', 64).references('collection').inTable('directus_collections').onDelete('CASCADE');
9
+ table.string('item');
10
+ table.uuid('role').references('id').inTable('directus_roles').onDelete('CASCADE');
11
+ table.string('password');
12
+ table.uuid('user_created').references('id').inTable('directus_users').onDelete('SET NULL');
13
+ table.timestamp('date_created').defaultTo(knex.fn.now());
14
+ table.timestamp('date_start');
15
+ table.timestamp('date_end');
16
+ table.integer('times_used').defaultTo(0);
17
+ table.integer('max_uses');
18
+ });
19
+ await knex.schema.alterTable('directus_sessions', (table) => {
20
+ table.dropColumn('data');
21
+ });
22
+ await knex.schema.alterTable('directus_sessions', (table) => {
23
+ table.uuid('user').nullable().alter();
24
+ table.uuid('share').references('id').inTable('directus_shares').onDelete('CASCADE');
25
+ });
26
+ }
27
+ exports.up = up;
28
+ async function down(knex) {
29
+ await knex.schema.alterTable('directus_sessions', (table) => {
30
+ table.uuid('user').notNullable().alter();
31
+ table.json('data');
32
+ table.dropForeign('share');
33
+ table.dropColumn('share');
34
+ });
35
+ await knex.schema.dropTable('directus_shares');
36
+ }
37
+ exports.down = down;
@@ -18,7 +18,7 @@ const helpers_1 = require("../database/helpers");
18
18
  async function runAST(originalAST, schema, options) {
19
19
  const ast = (0, lodash_1.cloneDeep)(originalAST);
20
20
  const knex = (options === null || options === void 0 ? void 0 : options.knex) || (0, _1.default)();
21
- if (ast.type === 'm2a') {
21
+ if (ast.type === 'a2o') {
22
22
  const results = {};
23
23
  for (const collection of ast.names) {
24
24
  results[collection] = await run(collection, ast.children[collection], ast.query[collection]);
@@ -85,7 +85,7 @@ async function parseCurrentLevel(schema, collection, children, query) {
85
85
  if (child.type === 'm2o') {
86
86
  columnsToSelectInternal.push(child.fieldKey);
87
87
  }
88
- if (child.type === 'm2a') {
88
+ if (child.type === 'a2o') {
89
89
  columnsToSelectInternal.push(child.relation.field);
90
90
  columnsToSelectInternal.push(child.relation.meta.one_collection_field);
91
91
  }
@@ -182,7 +182,7 @@ function applyParentFilters(schema, nestedCollectionNodes, parentItem) {
182
182
  nestedNode.query.union = [foreignField, foreignIds];
183
183
  }
184
184
  }
185
- else if (nestedNode.type === 'm2a') {
185
+ else if (nestedNode.type === 'a2o') {
186
186
  const keysPerCollection = {};
187
187
  for (const parentItem of parentItems) {
188
188
  const collection = parentItem[nestedNode.relation.meta.one_collection_field];
@@ -256,7 +256,7 @@ function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode) {
256
256
  parentItem[nestedNode.fieldKey] = itemChildren.length > 0 ? itemChildren : [];
257
257
  }
258
258
  }
259
- else if (nestedNode.type === 'm2a') {
259
+ else if (nestedNode.type === 'a2o') {
260
260
  for (const parentItem of parentItems) {
261
261
  if (!((_a = nestedNode.relation.meta) === null || _a === void 0 ? void 0 : _a.one_collection_field)) {
262
262
  parentItem[nestedNode.fieldKey] = null;
@@ -279,7 +279,7 @@ function removeTemporaryFields(schema, rawItem, ast, primaryKeyField, parentItem
279
279
  var _a;
280
280
  const rawItems = (0, lodash_1.cloneDeep)((0, utils_1.toArray)(rawItem));
281
281
  const items = [];
282
- if (ast.type === 'm2a') {
282
+ if (ast.type === 'a2o') {
283
283
  const fields = {};
284
284
  const nestedCollectionNodes = {};
285
285
  for (const relatedCollection of ast.names) {
@@ -13,18 +13,6 @@
13
13
  comment:
14
14
  _nnull: true
15
15
 
16
- - collection: directus_collections
17
- action: read
18
-
19
- - collection: directus_fields
20
- action: read
21
-
22
- - collection: directus_permissions
23
- action: read
24
- permissions:
25
- role:
26
- _eq: $CURRENT_ROLE
27
-
28
16
  - collection: directus_presets
29
17
  action: read
30
18
  permissions:
@@ -60,9 +48,6 @@
60
48
  user:
61
49
  _eq: $CURRENT_USER
62
50
 
63
- - collection: directus_relations
64
- action: read
65
-
66
51
  - collection: directus_roles
67
52
  action: read
68
53
  permissions:
@@ -1,2 +1,3 @@
1
1
  import { Permission } from '@directus/shared/types';
2
+ export declare const schemaPermissions: Permission[];
2
3
  export declare const appAccessMinimalPermissions: Permission[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.appAccessMinimalPermissions = void 0;
3
+ exports.appAccessMinimalPermissions = exports.schemaPermissions = void 0;
4
4
  const lodash_1 = require("lodash");
5
5
  const require_yaml_1 = require("../../../utils/require-yaml");
6
6
  const defaults = {
@@ -11,5 +11,7 @@ const defaults = {
11
11
  fields: ['*'],
12
12
  system: true,
13
13
  };
14
+ const schemaPermissionsRaw = (0, require_yaml_1.requireYAML)(require.resolve('./schema-access-permissions.yaml'));
14
15
  const permissions = (0, require_yaml_1.requireYAML)(require.resolve('./app-access-permissions.yaml'));
15
- exports.appAccessMinimalPermissions = permissions.map((row) => (0, lodash_1.merge)({}, defaults, row));
16
+ exports.schemaPermissions = schemaPermissionsRaw.map((row) => (0, lodash_1.merge)({}, defaults, row));
17
+ exports.appAccessMinimalPermissions = [...exports.schemaPermissions, ...permissions].map((row) => (0, lodash_1.merge)({}, defaults, row));
@@ -0,0 +1,17 @@
1
+ # NOTE: Activity/collections/fields/presets/relations/revisions will have an extra hardcoded filter
2
+ # to filter out collections you don't have read access
3
+
4
+ - collection: directus_collections
5
+ action: read
6
+
7
+ - collection: directus_fields
8
+ action: read
9
+
10
+ - collection: directus_permissions
11
+ action: read
12
+ permissions:
13
+ role:
14
+ _eq: $CURRENT_ROLE
15
+
16
+ - collection: directus_relations
17
+ action: read
@@ -65,3 +65,6 @@ data:
65
65
  note: $t:directus_collection.directus_panels
66
66
  - collection: directus_notifications
67
67
  note: $t:directus_collection.directus_notifications
68
+ - collection: directus_shares
69
+ icon: share
70
+ note: $t:directus_collection.directus_shares
@@ -11,4 +11,4 @@ fields:
11
11
  width: half
12
12
  - field: user_agent
13
13
  width: half
14
- - field: data
14
+ - field: share
@@ -0,0 +1,73 @@
1
+ table: directus_shares
2
+
3
+ fields:
4
+ - field: id
5
+ special: uuid
6
+ readonly: true
7
+ hidden: true
8
+
9
+ - field: name
10
+
11
+ - field: collection
12
+ width: half
13
+ hidden: true
14
+
15
+ - field: item
16
+ width: half
17
+ hidden: true
18
+
19
+ - field: role
20
+ interface: select-dropdown-m2o
21
+ width: half
22
+ options:
23
+ template: '{{name}}'
24
+ filter:
25
+ admin_access:
26
+ _eq: false
27
+
28
+ - field: password
29
+ special: hash,conceal
30
+ interface: input-hash
31
+ options:
32
+ iconRight: lock
33
+ masked: true
34
+ width: half
35
+
36
+ - field: date_start
37
+ width: half
38
+
39
+ - field: date_end
40
+ width: half
41
+
42
+ - field: max_uses
43
+ width: half
44
+
45
+ - field: times_used
46
+ width: half
47
+ readonly: true
48
+
49
+ - field: date_created
50
+ special: date-created
51
+ width: half
52
+ readonly: true
53
+ conditions:
54
+ - name: notCreatedYet
55
+ rule:
56
+ id:
57
+ _null: true
58
+ hidden: true
59
+
60
+ - field: user_created
61
+ special: user-created
62
+ interface: select-dropdown-m2o
63
+ width: half
64
+ display: user
65
+ options:
66
+ template: '{{avatar.$thumbnail}} {{first_name}} {{last_name}}'
67
+ readonly: true
68
+ conditions:
69
+ - name: notCreatedYet
70
+ rule:
71
+ id:
72
+ _null: true
73
+ hidden: true
@@ -100,7 +100,7 @@ fields:
100
100
  options:
101
101
  icon: verified_user
102
102
  title: $t:fields.directus_users.admin_options
103
- color: '#E35169'
103
+ color: 'var(--danger)'
104
104
  special:
105
105
  - alias
106
106
  - no-data
@@ -12,6 +12,9 @@ defaults:
12
12
  sort_field: null
13
13
 
14
14
  data:
15
+ - many_collection: directus_collections
16
+ many_field: group
17
+ one_collection: directus_collections
15
18
  - many_collection: directus_users
16
19
  many_field: role
17
20
  one_collection: directus_roles
@@ -73,6 +76,9 @@ data:
73
76
  - many_collection: directus_sessions
74
77
  many_field: user
75
78
  one_collection: directus_users
79
+ - many_collection: directus_sessions
80
+ many_field: share
81
+ one_collection: directus_shares
76
82
  - many_collection: directus_settings
77
83
  many_field: storage_default_folder
78
84
  one_collection: directus_folders
@@ -88,3 +94,12 @@ data:
88
94
  - many_collection: directus_notifications
89
95
  many_field: sender
90
96
  one_collection: directus_users
97
+ - many_collection: directus_shares
98
+ many_field: role
99
+ one_collection: directus_roles
100
+ - many_collection: directus_shares
101
+ many_field: collection
102
+ one_collection: directus_collections
103
+ - many_collection: directus_shares
104
+ many_field: user_created
105
+ one_collection: directus_users
@@ -44,6 +44,7 @@ const plugin_virtual_1 = __importDefault(require("@rollup/plugin-virtual"));
44
44
  const plugin_alias_1 = __importDefault(require("@rollup/plugin-alias"));
45
45
  const url_1 = require("./utils/url");
46
46
  const get_module_default_1 = __importDefault(require("./utils/get-module-default"));
47
+ const lodash_1 = require("lodash");
47
48
  let extensionManager;
48
49
  function getExtensionManager() {
49
50
  if (extensionManager) {
@@ -141,12 +142,13 @@ class ExtensionManager {
141
142
  return bundles;
142
143
  }
143
144
  async getSharedDepsMapping(deps) {
144
- const appDir = await fs_extra_1.default.readdir(path_1.default.join((0, node_1.resolvePackage)('@directus/app'), 'dist'));
145
+ const appDir = await fs_extra_1.default.readdir(path_1.default.join((0, node_1.resolvePackage)('@directus/app'), 'dist', 'assets'));
145
146
  const depsMapping = {};
146
147
  for (const dep of deps) {
147
- const depName = appDir.find((file) => dep.replace(/\//g, '_') === file.substring(0, file.indexOf('.')));
148
+ const depRegex = new RegExp(`${(0, lodash_1.escapeRegExp)(dep.replace(/\//g, '_'))}\\.[0-9a-f]{8}\\.entry\\.js`);
149
+ const depName = appDir.find((file) => depRegex.test(file));
148
150
  if (depName) {
149
- const depUrl = new url_1.Url(env_1.default.PUBLIC_URL).addPath('admin', depName);
151
+ const depUrl = new url_1.Url(env_1.default.PUBLIC_URL).addPath('admin', 'assets', depName);
150
152
  depsMapping[dep] = depUrl.toString({ rootRelative: true });
151
153
  }
152
154
  else {
@@ -58,22 +58,12 @@ const authenticate = (0, async_handler_1.default)(async (req, res, next) => {
58
58
  throw err;
59
59
  }
60
60
  }
61
- const user = await database
62
- .select('directus_users.role', 'directus_roles.admin_access', 'directus_roles.app_access')
63
- .from('directus_users')
64
- .leftJoin('directus_roles', 'directus_users.role', 'directus_roles.id')
65
- .where({
66
- 'directus_users.id': payload.id,
67
- status: 'active',
68
- })
69
- .first();
70
- if (!user) {
71
- throw new exceptions_1.InvalidCredentialsException();
72
- }
61
+ req.accountability.share = payload.share;
62
+ req.accountability.share_scope = payload.share_scope;
73
63
  req.accountability.user = payload.id;
74
- req.accountability.role = user.role;
75
- req.accountability.admin = user.admin_access === true || user.admin_access == 1;
76
- req.accountability.app = user.app_access === true || user.app_access == 1;
64
+ req.accountability.role = payload.role;
65
+ req.accountability.admin = payload.admin_access === true || payload.admin_access == 1;
66
+ req.accountability.app = payload.app_access === true || payload.app_access == 1;
77
67
  }
78
68
  else {
79
69
  // Try finding the user with the provided token
@@ -7,13 +7,16 @@ exports.checkIP = void 0;
7
7
  const database_1 = __importDefault(require("../database"));
8
8
  const exceptions_1 = require("../exceptions");
9
9
  const async_handler_1 = __importDefault(require("../utils/async-handler"));
10
- exports.checkIP = (0, async_handler_1.default)(async (req, res, next) => {
10
+ exports.checkIP = (0, async_handler_1.default)(async (req, _res, next) => {
11
11
  const database = (0, database_1.default)();
12
- const role = await database
13
- .select('ip_access')
14
- .from('directus_roles')
15
- .where({ id: req.accountability.role })
16
- .first();
12
+ const query = database.select('ip_access').from('directus_roles');
13
+ if (req.accountability.role) {
14
+ query.where({ id: req.accountability.role });
15
+ }
16
+ else {
17
+ query.whereNull('id');
18
+ }
19
+ const role = await query.first();
17
20
  const ipAllowlist = ((role === null || role === void 0 ? void 0 : role.ip_access) || '').split(',').filter((ip) => ip);
18
21
  if (ipAllowlist.length > 0 && ipAllowlist.includes(req.accountability.ip) === false)
19
22
  throw new exceptions_1.InvalidIPException();
@@ -77,9 +77,12 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
77
77
  if (Buffer.isBuffer(res.locals.payload)) {
78
78
  return res.end(res.locals.payload);
79
79
  }
80
- else {
80
+ else if (res.locals.payload) {
81
81
  return res.json(res.locals.payload);
82
82
  }
83
+ else {
84
+ return res.status(204).end();
85
+ }
83
86
  });
84
87
  function getDateFormatted() {
85
88
  const date = new Date();
@@ -1,5 +1,6 @@
1
1
  import { AbstractServiceOptions, PrimaryKey, Item } from '../types';
2
- import { ItemsService, MutationOptions } from './index';
2
+ import { ItemsService } from './items';
3
+ import { MutationOptions } from '../types';
3
4
  import { NotificationsService } from './notifications';
4
5
  import { UsersService } from './users';
5
6
  export declare class ActivityService extends ItemsService {
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ActivityService = void 0;
7
7
  const types_1 = require("../types");
8
- const index_1 = require("./index");
8
+ const items_1 = require("./items");
9
9
  const notifications_1 = require("./notifications");
10
10
  const users_1 = require("./users");
11
11
  const authorization_1 = require("./authorization");
@@ -16,7 +16,7 @@ const user_name_1 = require("../utils/user-name");
16
16
  const lodash_1 = require("lodash");
17
17
  const env_1 = __importDefault(require("../env"));
18
18
  const uuid_validate_1 = __importDefault(require("uuid-validate"));
19
- class ActivityService extends index_1.ItemsService {
19
+ class ActivityService extends items_1.ItemsService {
20
20
  constructor(options) {
21
21
  super('directus_activity', options);
22
22
  this.notificationsService = new notifications_1.NotificationsService({ schema: this.schema });
@@ -1,6 +1,6 @@
1
1
  import { Knex } from 'knex';
2
2
  import { ActivityService } from './activity';
3
- import { AbstractServiceOptions, SchemaOverview } from '../types';
3
+ import { AbstractServiceOptions, SchemaOverview, LoginResult } from '../types';
4
4
  import { Accountability } from '@directus/shared/types';
5
5
  export declare class AuthenticationService {
6
6
  knex: Knex;
@@ -14,12 +14,7 @@ export declare class AuthenticationService {
14
14
  * Password is optional to allow usage of this function within the SSO flow and extensions. Make sure
15
15
  * to handle password existence checks elsewhere
16
16
  */
17
- login(providerName: string | undefined, payload: Record<string, any>, otp?: string): Promise<{
18
- accessToken: any;
19
- refreshToken: any;
20
- expires: any;
21
- id?: any;
22
- }>;
17
+ login(providerName: string | undefined, payload: Record<string, any>, otp?: string): Promise<LoginResult>;
23
18
  refresh(refreshToken: string): Promise<Record<string, any>>;
24
19
  logout(refreshToken: string): Promise<void>;
25
20
  verifyPassword(userID: string, password: string): Promise<void>;
@@ -21,7 +21,6 @@ const settings_1 = require("./settings");
21
21
  const lodash_1 = require("lodash");
22
22
  const perf_hooks_1 = require("perf_hooks");
23
23
  const stall_1 = require("../utils/stall");
24
- const logger_1 = __importDefault(require("../logger"));
25
24
  const loginAttemptsLimiter = (0, rate_limiter_1.createRateLimiter)({ duration: 0 });
26
25
  class AuthenticationService {
27
26
  constructor(options) {
@@ -42,10 +41,11 @@ class AuthenticationService {
42
41
  const timeStart = perf_hooks_1.performance.now();
43
42
  const provider = (0, auth_1.getAuthProvider)(providerName);
44
43
  const user = await this.knex
45
- .select('id', 'first_name', 'last_name', 'email', 'password', 'status', 'role', 'tfa_secret', 'provider', 'external_identifier', 'auth_data')
46
- .from('directus_users')
47
- .where('id', await provider.getUserID((0, lodash_1.cloneDeep)(payload)))
48
- .andWhere('provider', providerName)
44
+ .select('u.id', 'u.first_name', 'u.last_name', 'u.email', 'u.password', 'u.status', 'u.role', 'r.admin_access', 'r.app_access', 'u.tfa_secret', 'u.provider', 'u.external_identifier', 'u.auth_data')
45
+ .from('directus_users as u')
46
+ .innerJoin('directus_roles as r', 'u.role', 'r.id')
47
+ .where('u.id', await provider.getUserID((0, lodash_1.cloneDeep)(payload)))
48
+ .andWhere('u.provider', providerName)
49
49
  .first();
50
50
  const updatedPayload = await emitter_1.default.emitFilter('auth.login', payload, {
51
51
  status: 'pending',
@@ -98,9 +98,8 @@ class AuthenticationService {
98
98
  await loginAttemptsLimiter.set(user.id, 0, 0);
99
99
  }
100
100
  }
101
- let sessionData = null;
102
101
  try {
103
- sessionData = await provider.login((0, lodash_1.clone)(user), (0, lodash_1.cloneDeep)(updatedPayload));
102
+ await provider.login((0, lodash_1.clone)(user), (0, lodash_1.cloneDeep)(updatedPayload));
104
103
  }
105
104
  catch (e) {
106
105
  emitStatus('fail');
@@ -123,6 +122,9 @@ class AuthenticationService {
123
122
  }
124
123
  const tokenPayload = {
125
124
  id: user.id,
125
+ role: user.role,
126
+ app_access: user.app_access,
127
+ admin_access: user.admin_access,
126
128
  };
127
129
  const customClaims = await emitter_1.default.emitFilter('auth.jwt', tokenPayload, {
128
130
  status: 'pending',
@@ -146,7 +148,6 @@ class AuthenticationService {
146
148
  expires: refreshTokenExpiration,
147
149
  ip: (_a = this.accountability) === null || _a === void 0 ? void 0 : _a.ip,
148
150
  user_agent: (_b = this.accountability) === null || _b === void 0 ? void 0 : _b.userAgent,
149
- data: sessionData && JSON.stringify(sessionData),
150
151
  });
151
152
  await this.knex('directus_sessions').delete().where('expires', '<', new Date());
152
153
  if (this.accountability) {
@@ -177,33 +178,80 @@ class AuthenticationService {
177
178
  throw new exceptions_1.InvalidCredentialsException();
178
179
  }
179
180
  const record = await this.knex
180
- .select('s.expires', 's.data', 'u.id', 'u.first_name', 'u.last_name', 'u.email', 'u.password', 'u.status', 'u.role', 'u.provider', 'u.external_identifier', 'u.auth_data')
181
- .from('directus_sessions as s')
182
- .innerJoin('directus_users as u', 's.user', 'u.id')
181
+ .select({
182
+ session_expires: 's.expires',
183
+ user_id: 'u.id',
184
+ user_first_name: 'u.first_name',
185
+ user_last_name: 'u.last_name',
186
+ user_email: 'u.email',
187
+ user_password: 'u.password',
188
+ user_status: 'u.status',
189
+ user_provider: 'u.provider',
190
+ user_external_identifier: 'u.external_identifier',
191
+ user_auth_data: 'u.auth_data',
192
+ role_id: 'r.id',
193
+ role_admin_access: 'r.admin_access',
194
+ role_app_access: 'r.app_access',
195
+ share_id: 'd.id',
196
+ share_item: 'd.item',
197
+ share_role: 'd.role',
198
+ share_collection: 'd.collection',
199
+ share_start: 'd.date_start',
200
+ share_end: 'd.date_end',
201
+ share_times_used: 'd.times_used',
202
+ share_max_uses: 'd.max_uses',
203
+ })
204
+ .from('directus_sessions AS s')
205
+ .leftJoin('directus_users AS u', 's.user', 'u.id')
206
+ .leftJoin('directus_shares AS d', 's.share', 'd.id')
207
+ .joinRaw('LEFT JOIN directus_roles AS r ON r.id IN (u.role, d.role)')
183
208
  .where('s.token', refreshToken)
209
+ .andWhere('s.expires', '>=', this.knex.fn.now())
210
+ .andWhere((subQuery) => {
211
+ subQuery.whereNull('d.date_end').orWhere('d.date_end', '>=', this.knex.fn.now());
212
+ })
213
+ .andWhere((subQuery) => {
214
+ subQuery.whereNull('d.date_start').orWhere('d.date_start', '<=', this.knex.fn.now());
215
+ })
184
216
  .first();
185
- if (!record || record.expires < new Date()) {
217
+ if (!record || (!record.share_id && !record.user_id)) {
186
218
  throw new exceptions_1.InvalidCredentialsException();
187
219
  }
188
- let { data: sessionData } = record;
189
- const user = (0, lodash_1.omit)(record, 'data');
190
- if (typeof sessionData === 'string') {
191
- try {
192
- sessionData = JSON.parse(sessionData);
193
- }
194
- catch {
195
- logger_1.default.warn(`Session data isn't valid JSON: ${sessionData}`);
196
- }
220
+ if (record.user_id) {
221
+ const provider = (0, auth_1.getAuthProvider)(record.user_provider);
222
+ await provider.refresh({
223
+ id: record.user_id,
224
+ first_name: record.user_first_name,
225
+ last_name: record.user_last_name,
226
+ email: record.user_email,
227
+ password: record.user_password,
228
+ status: record.user_status,
229
+ provider: record.user_provider,
230
+ external_identifier: record.user_external_identifier,
231
+ auth_data: record.user_auth_data,
232
+ role: record.role_id,
233
+ app_access: record.role_app_access,
234
+ admin_access: record.role_admin_access,
235
+ });
197
236
  }
198
- const provider = (0, auth_1.getAuthProvider)(user.provider);
199
- const newSessionData = await provider.refresh((0, lodash_1.clone)(user), sessionData);
200
237
  const tokenPayload = {
201
- id: user.id,
238
+ id: record.user_id,
239
+ role: record.role_id,
240
+ app_access: record.role_app_access,
241
+ admin_access: record.role_admin_access,
202
242
  };
243
+ if (record.share_id) {
244
+ tokenPayload.share = record.share_id;
245
+ tokenPayload.role = record.share_role;
246
+ tokenPayload.share_scope = {
247
+ collection: record.share_collection,
248
+ item: record.share_item,
249
+ };
250
+ }
203
251
  const customClaims = await emitter_1.default.emitFilter('auth.jwt', tokenPayload, {
204
252
  status: 'pending',
205
- user: user === null || user === void 0 ? void 0 : user.id,
206
- provider: user.provider,
253
+ user: record.user_id,
254
+ provider: record.user_provider,
207
255
  type: 'refresh',
208
256
  }, {
209
257
  database: this.knex,
@@ -220,37 +268,29 @@ class AuthenticationService {
220
268
  .update({
221
269
  token: newRefreshToken,
222
270
  expires: refreshTokenExpiration,
223
- data: newSessionData && JSON.stringify(newSessionData),
224
271
  })
225
272
  .where({ token: refreshToken });
226
- await this.knex('directus_users').update({ last_access: new Date() }).where({ id: user.id });
273
+ if (record.user_id) {
274
+ await this.knex('directus_users').update({ last_access: new Date() }).where({ id: record.user_id });
275
+ }
227
276
  return {
228
277
  accessToken,
229
278
  refreshToken: newRefreshToken,
230
279
  expires: (0, ms_1.default)(env_1.default.ACCESS_TOKEN_TTL),
231
- id: user.id,
280
+ id: record.user_id,
232
281
  };
233
282
  }
234
283
  async logout(refreshToken) {
235
284
  const record = await this.knex
236
- .select('u.id', 'u.first_name', 'u.last_name', 'u.email', 'u.password', 'u.status', 'u.role', 'u.provider', 'u.external_identifier', 'u.auth_data', 's.data')
285
+ .select('u.id', 'u.first_name', 'u.last_name', 'u.email', 'u.password', 'u.status', 'u.role', 'u.provider', 'u.external_identifier', 'u.auth_data')
237
286
  .from('directus_sessions as s')
238
287
  .innerJoin('directus_users as u', 's.user', 'u.id')
239
288
  .where('s.token', refreshToken)
240
289
  .first();
241
290
  if (record) {
242
- let { data: sessionData } = record;
243
- const user = (0, lodash_1.omit)(record, 'data');
244
- if (typeof sessionData === 'string') {
245
- try {
246
- sessionData = JSON.parse(sessionData);
247
- }
248
- catch {
249
- logger_1.default.warn(`Session data isn't valid JSON: ${sessionData}`);
250
- }
251
- }
291
+ const user = record;
252
292
  const provider = (0, auth_1.getAuthProvider)(user.provider);
253
- await provider.logout((0, lodash_1.clone)(user), sessionData);
293
+ await provider.logout((0, lodash_1.clone)(user));
254
294
  await this.knex.delete().from('directus_sessions').where('token', refreshToken);
255
295
  }
256
296
  }
@@ -41,7 +41,7 @@ class AuthorizationService {
41
41
  */
42
42
  function getCollectionsFromAST(ast) {
43
43
  const collections = [];
44
- if (ast.type === 'm2a') {
44
+ if (ast.type === 'a2o') {
45
45
  collections.push(...ast.names.map((name) => ({ collection: name, field: ast.fieldKey })));
46
46
  for (const children of Object.values(ast.children)) {
47
47
  for (const nestedNode of children) {
@@ -67,7 +67,7 @@ class AuthorizationService {
67
67
  function validateFields(ast) {
68
68
  var _a, _b, _c;
69
69
  if (ast.type !== 'field') {
70
- if (ast.type === 'm2a') {
70
+ if (ast.type === 'a2o') {
71
71
  for (const [collection, children] of Object.entries(ast.children)) {
72
72
  checkFields(collection, children, (_b = (_a = ast.query) === null || _a === void 0 ? void 0 : _a[collection]) === null || _b === void 0 ? void 0 : _b.aggregate);
73
73
  }
@@ -106,7 +106,7 @@ class AuthorizationService {
106
106
  }
107
107
  function applyFilters(ast, accountability) {
108
108
  if (ast.type !== 'field') {
109
- if (ast.type === 'm2a') {
109
+ if (ast.type === 'a2o') {
110
110
  const collections = Object.keys(ast.children);
111
111
  for (const collection of collections) {
112
112
  updateFilterQuery(collection, ast.query[collection]);