directus 9.2.2 → 9.4.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.
Files changed (96) hide show
  1. package/dist/app.js +16 -4
  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 +2 -3
  6. package/dist/auth/drivers/local.d.ts +2 -2
  7. package/dist/auth/drivers/local.js +7 -13
  8. package/dist/auth/drivers/oauth2.d.ts +3 -3
  9. package/dist/auth/drivers/oauth2.js +4 -4
  10. package/dist/auth/drivers/openid.d.ts +3 -3
  11. package/dist/auth/drivers/openid.js +4 -4
  12. package/dist/cache.js +1 -3
  13. package/dist/cli/commands/bootstrap/index.js +3 -2
  14. package/dist/cli/commands/init/index.js +3 -7
  15. package/dist/cli/commands/schema/apply.js +1 -1
  16. package/dist/cli/utils/defaults.d.ts +11 -0
  17. package/dist/cli/utils/defaults.js +14 -0
  18. package/dist/constants.d.ts +8 -0
  19. package/dist/constants.js +16 -2
  20. package/dist/controllers/activity.js +2 -1
  21. package/dist/controllers/auth.js +5 -4
  22. package/dist/controllers/shares.d.ts +2 -0
  23. package/dist/controllers/shares.js +212 -0
  24. package/dist/controllers/users.js +21 -9
  25. package/dist/database/index.js +3 -0
  26. package/dist/database/migrations/20211211A-add-shares.d.ts +3 -0
  27. package/dist/database/migrations/20211211A-add-shares.js +38 -0
  28. package/dist/database/migrations/20211230A-add-project-descriptor.d.ts +3 -0
  29. package/dist/database/migrations/20211230A-add-project-descriptor.js +15 -0
  30. package/dist/database/run-ast.js +5 -5
  31. package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +0 -15
  32. package/dist/database/system-data/app-access-permissions/index.d.ts +1 -0
  33. package/dist/database/system-data/app-access-permissions/index.js +4 -2
  34. package/dist/database/system-data/app-access-permissions/schema-access-permissions.yaml +17 -0
  35. package/dist/database/system-data/collections/collections.yaml +3 -0
  36. package/dist/database/system-data/fields/_defaults.yaml +2 -0
  37. package/dist/database/system-data/fields/sessions.yaml +1 -1
  38. package/dist/database/system-data/fields/settings.yaml +20 -1
  39. package/dist/database/system-data/fields/shares.yaml +77 -0
  40. package/dist/database/system-data/fields/users.yaml +1 -1
  41. package/dist/database/system-data/relations/relations.yaml +15 -0
  42. package/dist/emitter.d.ts +3 -2
  43. package/dist/emitter.js +13 -6
  44. package/dist/env.js +3 -1
  45. package/dist/extensions.d.ts +1 -0
  46. package/dist/extensions.js +10 -4
  47. package/dist/middleware/authenticate.js +7 -16
  48. package/dist/middleware/check-ip.js +9 -6
  49. package/dist/middleware/rate-limiter.js +2 -1
  50. package/dist/middleware/respond.js +4 -1
  51. package/dist/services/activity.d.ts +2 -1
  52. package/dist/services/activity.js +2 -2
  53. package/dist/services/authentication.d.ts +2 -7
  54. package/dist/services/authentication.js +81 -41
  55. package/dist/services/authorization.js +3 -3
  56. package/dist/services/collections.d.ts +1 -2
  57. package/dist/services/collections.js +2 -2
  58. package/dist/services/files.d.ts +2 -2
  59. package/dist/services/files.js +14 -8
  60. package/dist/services/graphql.d.ts +1 -1
  61. package/dist/services/graphql.js +47 -6
  62. package/dist/services/index.d.ts +1 -0
  63. package/dist/services/index.js +1 -0
  64. package/dist/services/items.d.ts +1 -15
  65. package/dist/services/notifications.d.ts +2 -2
  66. package/dist/services/permissions.d.ts +2 -2
  67. package/dist/services/roles.d.ts +2 -2
  68. package/dist/services/server.js +1 -0
  69. package/dist/services/shares.d.ts +17 -0
  70. package/dist/services/shares.js +135 -0
  71. package/dist/services/specifications.js +1 -1
  72. package/dist/services/users.d.ts +2 -2
  73. package/dist/services/webhooks.d.ts +2 -2
  74. package/dist/types/ast.d.ts +3 -3
  75. package/dist/types/auth.d.ts +31 -0
  76. package/dist/types/extensions.d.ts +2 -0
  77. package/dist/types/items.d.ts +14 -0
  78. package/dist/utils/apply-query.d.ts +0 -38
  79. package/dist/utils/apply-query.js +66 -67
  80. package/dist/utils/apply-snapshot.js +69 -14
  81. package/dist/utils/get-ast-from-query.js +3 -3
  82. package/dist/utils/get-default-value.js +3 -1
  83. package/dist/utils/get-ip-from-req.d.ts +2 -0
  84. package/dist/utils/get-ip-from-req.js +24 -0
  85. package/dist/utils/get-permissions.js +15 -7
  86. package/dist/utils/get-relation-type.d.ts +1 -1
  87. package/dist/utils/get-relation-type.js +1 -1
  88. package/dist/utils/merge-permissions-for-share.d.ts +5 -0
  89. package/dist/utils/merge-permissions-for-share.js +116 -0
  90. package/dist/utils/merge-permissions.d.ts +13 -1
  91. package/dist/utils/merge-permissions.js +27 -19
  92. package/dist/utils/reduce-schema.d.ts +2 -2
  93. package/dist/utils/reduce-schema.js +7 -7
  94. package/dist/utils/user-name.js +3 -0
  95. package/example.env +1 -1
  96. package/package.json +15 -14
package/dist/emitter.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import { ActionHandler, FilterHandler, HookContext, InitHandler } from './types';
2
- declare class Emitter {
2
+ export declare class Emitter {
3
3
  private filterEmitter;
4
4
  private actionEmitter;
5
5
  private initEmitter;
6
6
  constructor();
7
- eventsToEmit(event: string, meta: Record<string, any>): string[];
8
7
  emitFilter<T>(event: string, payload: T, meta: Record<string, any>, context: HookContext): Promise<T>;
9
8
  emitAction(event: string, meta: Record<string, any>, context: HookContext): void;
10
9
  emitInit(event: string, meta: Record<string, any>): Promise<void>;
@@ -14,6 +13,8 @@ declare class Emitter {
14
13
  offFilter(event: string, handler: FilterHandler): void;
15
14
  offAction(event: string, handler: ActionHandler): void;
16
15
  offInit(event: string, handler: InitHandler): void;
16
+ offAll(): void;
17
+ private eventsToEmit;
17
18
  }
18
19
  declare const emitter: Emitter;
19
20
  export default emitter;
package/dist/emitter.js CHANGED
@@ -3,6 +3,7 @@ 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
+ exports.Emitter = void 0;
6
7
  const eventemitter2_1 = require("eventemitter2");
7
8
  const logger_1 = __importDefault(require("./logger"));
8
9
  class Emitter {
@@ -18,12 +19,6 @@ class Emitter {
18
19
  this.actionEmitter = new eventemitter2_1.EventEmitter2(emitterOptions);
19
20
  this.initEmitter = new eventemitter2_1.EventEmitter2(emitterOptions);
20
21
  }
21
- eventsToEmit(event, meta) {
22
- if (event.startsWith('items')) {
23
- return [event, `${meta.collection}.${event}`];
24
- }
25
- return [event];
26
- }
27
22
  async emitFilter(event, payload, meta, context) {
28
23
  const events = this.eventsToEmit(event, meta);
29
24
  const listeners = events.flatMap((event) => this.filterEmitter.listeners(event));
@@ -72,6 +67,18 @@ class Emitter {
72
67
  offInit(event, handler) {
73
68
  this.initEmitter.off(event, handler);
74
69
  }
70
+ offAll() {
71
+ this.filterEmitter.removeAllListeners();
72
+ this.actionEmitter.removeAllListeners();
73
+ this.initEmitter.removeAllListeners();
74
+ }
75
+ eventsToEmit(event, meta) {
76
+ if (event.startsWith('items')) {
77
+ return [event, `${meta.collection}.${event}`];
78
+ }
79
+ return [event];
80
+ }
75
81
  }
82
+ exports.Emitter = Emitter;
76
83
  const emitter = new Emitter();
77
84
  exports.default = emitter;
package/dist/env.js CHANGED
@@ -20,7 +20,7 @@ const defaults = {
20
20
  PORT: 8055,
21
21
  PUBLIC_URL: '/',
22
22
  MAX_PAYLOAD_SIZE: '100kb',
23
- DB_EXCLUDE_TABLES: 'spatial_ref_sys',
23
+ DB_EXCLUDE_TABLES: 'spatial_ref_sys,sysdiagrams',
24
24
  STORAGE_LOCATIONS: 'local',
25
25
  STORAGE_LOCAL_DRIVER: 'local',
26
26
  STORAGE_LOCAL_ROOT: './uploads',
@@ -61,6 +61,8 @@ const defaults = {
61
61
  ASSETS_TRANSFORM_MAX_CONCURRENT: 1,
62
62
  ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION: 6000,
63
63
  ASSETS_TRANSFORM_MAX_OPERATIONS: 5,
64
+ IP_TRUST_PROXY: true,
65
+ IP_CUSTOM_HEADER: false,
64
66
  SERVE_APP: true,
65
67
  };
66
68
  // Allows us to force certain environment variable into a type, instead of relying
@@ -7,6 +7,7 @@ declare class ExtensionManager {
7
7
  private appExtensions;
8
8
  private apiHooks;
9
9
  private apiEndpoints;
10
+ private apiEmitter;
10
11
  private endpointRouter;
11
12
  private isScheduleHookEnabled;
12
13
  constructor();
@@ -28,7 +28,7 @@ const path_1 = __importDefault(require("path"));
28
28
  const node_1 = require("@directus/shared/utils/node");
29
29
  const constants_1 = require("@directus/shared/constants");
30
30
  const database_1 = __importDefault(require("./database"));
31
- const emitter_1 = __importDefault(require("./emitter"));
31
+ const emitter_1 = __importStar(require("./emitter"));
32
32
  const env_1 = __importDefault(require("./env"));
33
33
  const exceptions = __importStar(require("./exceptions"));
34
34
  const sharedExceptions = __importStar(require("@directus/shared/exceptions"));
@@ -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) {
@@ -61,6 +62,7 @@ class ExtensionManager {
61
62
  this.apiHooks = [];
62
63
  this.apiEndpoints = [];
63
64
  this.isScheduleHookEnabled = true;
65
+ this.apiEmitter = new emitter_1.Emitter();
64
66
  this.endpointRouter = (0, express_1.Router)();
65
67
  }
66
68
  async initialize({ schedule } = { schedule: true }) {
@@ -92,6 +94,7 @@ class ExtensionManager {
92
94
  logger_1.default.info('Reloading extensions');
93
95
  this.unregisterHooks();
94
96
  this.unregisterEndpoints();
97
+ this.apiEmitter.offAll();
95
98
  if (env_1.default.SERVE_APP) {
96
99
  this.appExtensions = {};
97
100
  }
@@ -139,12 +142,13 @@ class ExtensionManager {
139
142
  return bundles;
140
143
  }
141
144
  async getSharedDepsMapping(deps) {
142
- 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'));
143
146
  const depsMapping = {};
144
147
  for (const dep of deps) {
145
- 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));
146
150
  if (depName) {
147
- 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);
148
152
  depsMapping[dep] = depUrl.toString({ rootRelative: true });
149
153
  }
150
154
  else {
@@ -237,6 +241,7 @@ class ExtensionManager {
237
241
  exceptions: { ...exceptions, ...sharedExceptions },
238
242
  env: env_1.default,
239
243
  database: (0, database_1.default)(),
244
+ emitter: this.apiEmitter,
240
245
  logger: logger_1.default,
241
246
  getSchema: get_schema_1.getSchema,
242
247
  });
@@ -254,6 +259,7 @@ class ExtensionManager {
254
259
  exceptions: { ...exceptions, ...sharedExceptions },
255
260
  env: env_1.default,
256
261
  database: (0, database_1.default)(),
262
+ emitter: this.apiEmitter,
257
263
  logger: logger_1.default,
258
264
  getSchema: get_schema_1.getSchema,
259
265
  });
@@ -27,6 +27,7 @@ const database_1 = __importDefault(require("../database"));
27
27
  const env_1 = __importDefault(require("../env"));
28
28
  const exceptions_1 = require("../exceptions");
29
29
  const async_handler_1 = __importDefault(require("../utils/async-handler"));
30
+ const get_ip_from_req_1 = require("../utils/get-ip-from-req");
30
31
  const is_directus_jwt_1 = __importDefault(require("../utils/is-directus-jwt"));
31
32
  /**
32
33
  * Verify the passed JWT and assign the user ID and role to `req`
@@ -37,7 +38,7 @@ const authenticate = (0, async_handler_1.default)(async (req, res, next) => {
37
38
  role: null,
38
39
  admin: false,
39
40
  app: false,
40
- ip: req.ip.startsWith('::ffff:') ? req.ip.substring(7) : req.ip,
41
+ ip: (0, get_ip_from_req_1.getIPFromReq)(req),
41
42
  userAgent: req.get('user-agent'),
42
43
  };
43
44
  const database = (0, database_1.default)();
@@ -58,22 +59,12 @@ const authenticate = (0, async_handler_1.default)(async (req, res, next) => {
58
59
  throw err;
59
60
  }
60
61
  }
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
- }
62
+ req.accountability.share = payload.share;
63
+ req.accountability.share_scope = payload.share_scope;
73
64
  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;
65
+ req.accountability.role = payload.role;
66
+ req.accountability.admin = payload.admin_access === true || payload.admin_access == 1;
67
+ req.accountability.app = payload.app_access === true || payload.app_access == 1;
77
68
  }
78
69
  else {
79
70
  // 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();
@@ -9,6 +9,7 @@ const env_1 = __importDefault(require("../env"));
9
9
  const exceptions_1 = require("../exceptions");
10
10
  const rate_limiter_1 = require("../rate-limiter");
11
11
  const async_handler_1 = __importDefault(require("../utils/async-handler"));
12
+ const get_ip_from_req_1 = require("../utils/get-ip-from-req");
12
13
  const validate_env_1 = require("../utils/validate-env");
13
14
  let checkRateLimit = (req, res, next) => next();
14
15
  if (env_1.default.RATE_LIMITER_ENABLED === true) {
@@ -16,7 +17,7 @@ if (env_1.default.RATE_LIMITER_ENABLED === true) {
16
17
  exports.rateLimiter = (0, rate_limiter_1.createRateLimiter)();
17
18
  checkRateLimit = (0, async_handler_1.default)(async (req, res, next) => {
18
19
  try {
19
- await exports.rateLimiter.consume(req.ip, 1);
20
+ await exports.rateLimiter.consume((0, get_ip_from_req_1.getIPFromReq)(req), 1);
20
21
  }
21
22
  catch (rateLimiterRes) {
22
23
  if (rateLimiterRes instanceof Error)
@@ -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', '>=', new Date())
210
+ .andWhere((subQuery) => {
211
+ subQuery.whereNull('d.date_end').orWhere('d.date_end', '>=', new Date());
212
+ })
213
+ .andWhere((subQuery) => {
214
+ subQuery.whereNull('d.date_start').orWhere('d.date_start', '<=', new Date());
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]);
@@ -1,8 +1,7 @@
1
1
  import SchemaInspector from '@directus/schema';
2
2
  import { Knex } from 'knex';
3
- import { MutationOptions } from '../services/items';
4
3
  import Keyv from 'keyv';
5
- import { AbstractServiceOptions, Collection, CollectionMeta, SchemaOverview } from '../types';
4
+ import { AbstractServiceOptions, Collection, CollectionMeta, SchemaOverview, MutationOptions } from '../types';
6
5
  import { Accountability, RawField } from '@directus/shared/types';
7
6
  import { Table } from 'knex-schema-inspector/dist/types/table';
8
7
  export declare type RawCollection = {
@@ -359,11 +359,11 @@ class CollectionsService {
359
359
  await fieldsService.deleteField(relation.collection, relation.field);
360
360
  }
361
361
  }
362
- const m2aRelationsThatIncludeThisCollection = this.schema.relations.filter((relation) => {
362
+ const a2oRelationsThatIncludeThisCollection = this.schema.relations.filter((relation) => {
363
363
  var _a, _b;
364
364
  return (_b = (_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_allowed_collections) === null || _b === void 0 ? void 0 : _b.includes(collectionKey);
365
365
  });
366
- for (const relation of m2aRelationsThatIncludeThisCollection) {
366
+ for (const relation of a2oRelationsThatIncludeThisCollection) {
367
367
  const newAllowedCollections = relation
368
368
  .meta.one_allowed_collections.filter((collection) => collectionKey !== collection)
369
369
  .join(',');
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
- import { AbstractServiceOptions, File, PrimaryKey } from '../types';
3
- import { ItemsService, MutationOptions } from './items';
2
+ import { AbstractServiceOptions, File, PrimaryKey, MutationOptions } from '../types';
3
+ import { ItemsService } from './items';
4
4
  export declare class FilesService extends ItemsService {
5
5
  constructor(options: AbstractServiceOptions);
6
6
  /**
@@ -64,14 +64,20 @@ class FilesService extends items_1.ItemsService {
64
64
  payload.filesize = size;
65
65
  if (['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/tiff'].includes(payload.type)) {
66
66
  const buffer = await storage_1.default.disk(data.storage).getBuffer(payload.filename_disk);
67
- const meta = await (0, sharp_1.default)(buffer.content, {}).metadata();
68
- if (meta.orientation && meta.orientation >= 5) {
69
- payload.height = meta.width;
70
- payload.width = meta.height;
67
+ try {
68
+ const meta = await (0, sharp_1.default)(buffer.content, {}).metadata();
69
+ if (meta.orientation && meta.orientation >= 5) {
70
+ payload.height = meta.width;
71
+ payload.width = meta.height;
72
+ }
73
+ else {
74
+ payload.width = meta.width;
75
+ payload.height = meta.height;
76
+ }
71
77
  }
72
- else {
73
- payload.width = meta.width;
74
- payload.height = meta.height;
78
+ catch (err) {
79
+ logger_1.default.warn(`Couldn't extract sharp metadata from file`);
80
+ logger_1.default.warn(err);
75
81
  }
76
82
  payload.metadata = {};
77
83
  try {
@@ -95,7 +101,7 @@ class FilesService extends items_1.ItemsService {
95
101
  }
96
102
  }
97
103
  catch (err) {
98
- logger_1.default.warn(`Couldn't extract metadata from file`);
104
+ logger_1.default.warn(`Couldn't extract EXIF metadata from file`);
99
105
  logger_1.default.warn(err);
100
106
  }
101
107
  }
@@ -70,7 +70,7 @@ export declare class GraphQLService {
70
70
  * Effectively merges the selections with the fragments used in those selections
71
71
  */
72
72
  replaceFragmentsInSelections(selections: readonly SelectionNode[] | undefined, fragments: Record<string, FragmentDefinitionNode>): readonly SelectionNode[] | null;
73
- injectSystemResolvers(schemaComposer: SchemaComposer<GraphQLParams['contextValue']>, { CreateCollectionTypes, ReadCollectionTypes, DeleteCollectionTypes, }: {
73
+ injectSystemResolvers(schemaComposer: SchemaComposer<GraphQLParams['contextValue']>, { CreateCollectionTypes, ReadCollectionTypes, UpdateCollectionTypes, DeleteCollectionTypes, }: {
74
74
  CreateCollectionTypes: Record<string, ObjectTypeComposer<any, any>>;
75
75
  ReadCollectionTypes: Record<string, ObjectTypeComposer<any, any>>;
76
76
  UpdateCollectionTypes: Record<string, ObjectTypeComposer<any, any>>;