@directus/api 28.0.3 → 29.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/drivers/openid.js +12 -6
- package/dist/cli/commands/init/index.js +2 -2
- package/dist/cli/commands/init/questions.d.ts +1 -1
- package/dist/cli/commands/schema/apply.js +1 -1
- package/dist/cli/utils/create-db-connection.d.ts +1 -1
- package/dist/cli/utils/create-env/index.d.ts +1 -1
- package/dist/cli/utils/drivers.d.ts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/controllers/assets.js +1 -1
- package/dist/controllers/extensions.js +4 -4
- package/dist/database/get-ast-from-query/lib/parse-fields.js +3 -1
- package/dist/database/helpers/index.d.ts +4 -4
- package/dist/database/helpers/schema/types.d.ts +1 -2
- package/dist/database/index.d.ts +1 -1
- package/dist/database/migrations/20210225A-add-relations-sort-field.js +1 -1
- package/dist/database/migrations/20240924B-populate-versioning-deltas.js +1 -1
- package/dist/database/migrations/20250718A-add-direction.d.ts +3 -0
- package/dist/database/migrations/20250718A-add-direction.js +10 -0
- package/dist/database/run-ast/lib/apply-query/filter/get-filter-type.d.ts +2 -2
- package/dist/database/run-ast/utils/apply-parent-filters.js +2 -0
- package/dist/database/run-ast/utils/get-field-alias.js +2 -0
- package/dist/database/run-ast/utils/remove-temporary-fields.js +8 -2
- package/dist/extensions/lib/get-extensions-settings.d.ts +1 -1
- package/dist/extensions/lib/sandbox/generate-api-extensions-sandbox-entrypoint.d.ts +1 -1
- package/dist/extensions/manager.d.ts +5 -13
- package/dist/extensions/manager.js +39 -28
- package/dist/flows.d.ts +1 -2
- package/dist/operations/throw-error/index.d.ts +7 -0
- package/dist/operations/throw-error/index.js +11 -0
- package/dist/permissions/modules/validate-access/lib/validate-item-access.js +2 -1
- package/dist/permissions/utils/fetch-share-info.d.ts +1 -1
- package/dist/services/access.d.ts +1 -2
- package/dist/services/access.js +1 -1
- package/dist/services/activity.d.ts +1 -1
- package/dist/services/assets.d.ts +1 -3
- package/dist/services/authentication.d.ts +1 -2
- package/dist/services/collections.d.ts +3 -10
- package/dist/services/comments.d.ts +1 -2
- package/dist/services/dashboards.d.ts +1 -1
- package/dist/services/extensions.d.ts +2 -4
- package/dist/services/fields.d.ts +1 -2
- package/dist/services/fields.js +2 -8
- package/dist/services/files.d.ts +2 -3
- package/dist/services/flows.d.ts +1 -2
- package/dist/services/folders.d.ts +1 -1
- package/dist/services/graphql/errors/format.d.ts +1 -1
- package/dist/services/graphql/errors/format.js +0 -1
- package/dist/services/graphql/index.d.ts +1 -3
- package/dist/services/graphql/resolvers/system-admin.d.ts +1 -1
- package/dist/services/graphql/resolvers/system-global.d.ts +1 -1
- package/dist/services/graphql/resolvers/system.d.ts +1 -1
- package/dist/services/graphql/schema/get-types.d.ts +1 -1
- package/dist/services/graphql/schema/get-types.js +0 -1
- package/dist/services/import-export.d.ts +2 -4
- package/dist/services/import-export.js +5 -3
- package/dist/services/items.d.ts +2 -12
- package/dist/services/items.js +2 -1
- package/dist/services/mail/index.d.ts +2 -4
- package/dist/services/mail/index.js +11 -5
- package/dist/services/meta.d.ts +1 -2
- package/dist/services/notifications.d.ts +1 -2
- package/dist/services/operations.d.ts +1 -2
- package/dist/services/panels.d.ts +1 -1
- package/dist/services/payload.d.ts +9 -17
- package/dist/services/payload.js +1 -1
- package/dist/services/permissions.d.ts +1 -3
- package/dist/services/policies.d.ts +1 -2
- package/dist/services/policies.js +1 -1
- package/dist/services/presets.d.ts +1 -1
- package/dist/services/relations.d.ts +2 -3
- package/dist/services/revisions.d.ts +1 -2
- package/dist/services/roles.d.ts +1 -2
- package/dist/services/roles.js +1 -1
- package/dist/services/schema.d.ts +1 -2
- package/dist/services/server.d.ts +1 -2
- package/dist/services/settings.d.ts +1 -1
- package/dist/services/shares.d.ts +1 -2
- package/dist/services/specifications.d.ts +1 -2
- package/dist/services/tfa.d.ts +1 -2
- package/dist/services/translations.d.ts +1 -3
- package/dist/services/users.d.ts +1 -2
- package/dist/services/users.js +1 -1
- package/dist/services/utils.d.ts +1 -2
- package/dist/services/versions.d.ts +1 -2
- package/dist/services/webhooks.d.ts +1 -2
- package/dist/services/websocket.d.ts +1 -3
- package/dist/types/ast.d.ts +2 -0
- package/dist/types/auth.d.ts +0 -6
- package/dist/types/index.d.ts +0 -7
- package/dist/types/index.js +0 -7
- package/dist/utils/apply-diff.d.ts +1 -2
- package/dist/utils/apply-diff.js +1 -1
- package/dist/utils/apply-snapshot.d.ts +1 -2
- package/dist/utils/get-ip-from-req.js +1 -1
- package/dist/utils/get-service.d.ts +1 -1
- package/dist/utils/get-snapshot-diff.d.ts +1 -1
- package/dist/utils/get-snapshot-diff.js +1 -1
- package/dist/utils/get-snapshot.d.ts +1 -2
- package/dist/utils/get-snapshot.js +8 -3
- package/dist/utils/schedule.js +4 -1
- package/dist/utils/should-clear-cache.d.ts +1 -1
- package/dist/utils/transformations.d.ts +1 -2
- package/dist/utils/validate-diff.d.ts +1 -1
- package/dist/utils/validate-diff.js +1 -1
- package/dist/utils/validate-snapshot.d.ts +1 -1
- package/dist/utils/validate-snapshot.js +1 -1
- package/dist/utils/validate-user-count-integrity.d.ts +1 -8
- package/dist/utils/validate-user-count-integrity.js +1 -9
- package/dist/websocket/authenticate.d.ts +2 -1
- package/dist/websocket/authenticate.js +25 -4
- package/dist/websocket/controllers/base.d.ts +2 -1
- package/dist/websocket/controllers/base.js +12 -15
- package/dist/websocket/controllers/graphql.js +5 -3
- package/dist/websocket/controllers/logs.js +1 -1
- package/dist/websocket/controllers/rest.d.ts +1 -1
- package/dist/websocket/controllers/rest.js +1 -1
- package/dist/websocket/errors.d.ts +1 -1
- package/dist/websocket/handlers/heartbeat.d.ts +1 -1
- package/dist/websocket/handlers/heartbeat.js +1 -1
- package/dist/websocket/messages.d.ts +57 -308
- package/dist/websocket/messages.js +5 -10
- package/dist/websocket/utils/items.d.ts +1 -1
- package/dist/websocket/utils/wait-for-message.d.ts +1 -1
- package/dist/websocket/utils/wait-for-message.js +1 -1
- package/package.json +86 -85
- package/dist/extensions/types.d.ts +0 -19
- package/dist/extensions/types.js +0 -1
- package/dist/types/assets.d.ts +0 -22
- package/dist/types/assets.js +0 -51
- package/dist/types/database.d.ts +0 -3
- package/dist/types/database.js +0 -1
- package/dist/types/graphql.d.ts +0 -14
- package/dist/types/graphql.js +0 -1
- package/dist/types/items.d.ts +0 -52
- package/dist/types/items.js +0 -1
- package/dist/types/services.d.ts +0 -22
- package/dist/types/services.js +0 -1
- package/dist/types/snapshot.d.ts +0 -55
- package/dist/types/snapshot.js +0 -13
- package/dist/types/webhooks.d.ts +0 -15
- package/dist/types/webhooks.js +0 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type DirectusError } from '@directus/errors';
|
|
2
2
|
import type { Bus } from '@directus/memory';
|
|
3
|
-
import type { PrimaryKey } from '@directus/types';
|
|
4
|
-
import type { AbstractServiceOptions, MutationOptions, Webhook } from '../types/index.js';
|
|
3
|
+
import type { AbstractServiceOptions, MutationOptions, PrimaryKey, Webhook } from '@directus/types';
|
|
5
4
|
import { ItemsService } from './items.js';
|
|
6
5
|
export declare class WebhooksService extends ItemsService<Webhook> {
|
|
7
6
|
messenger: Bus;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type { ActionHandler } from '@directus/types';
|
|
2
|
-
import type { WebSocketMessage } from '../websocket/messages.js';
|
|
3
|
-
import type { WebSocketClient } from '../websocket/types.js';
|
|
1
|
+
import type { ActionHandler, WebSocketClient, WebSocketMessage } from '@directus/types';
|
|
4
2
|
export declare class WebSocketService {
|
|
5
3
|
private controller;
|
|
6
4
|
constructor();
|
package/dist/types/ast.d.ts
CHANGED
|
@@ -66,6 +66,8 @@ export type FieldNode = {
|
|
|
66
66
|
type: 'field';
|
|
67
67
|
name: string;
|
|
68
68
|
fieldKey: string;
|
|
69
|
+
/** If the field was created through alias query parameters */
|
|
70
|
+
alias: boolean;
|
|
69
71
|
/**
|
|
70
72
|
* Which permission cases have to be met on the current item for this field to return a value
|
|
71
73
|
*/
|
package/dist/types/auth.d.ts
CHANGED
|
@@ -40,10 +40,4 @@ export type ShareData = {
|
|
|
40
40
|
share_max_uses?: number;
|
|
41
41
|
share_password?: string;
|
|
42
42
|
};
|
|
43
|
-
export type LoginResult = {
|
|
44
|
-
accessToken: string;
|
|
45
|
-
refreshToken: string;
|
|
46
|
-
expires: number;
|
|
47
|
-
id?: string;
|
|
48
|
-
};
|
|
49
43
|
export type AuthenticationMode = 'json' | 'cookie' | 'session';
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
export * from './assets.js';
|
|
2
1
|
export * from './ast.js';
|
|
3
2
|
export * from './auth.js';
|
|
4
3
|
export * from './collection.js';
|
|
5
|
-
export * from './database.js';
|
|
6
4
|
export * from './events.js';
|
|
7
|
-
export * from './graphql.js';
|
|
8
|
-
export * from './items.js';
|
|
9
5
|
export * from './meta.js';
|
|
10
6
|
export * from './migration.js';
|
|
11
7
|
export * from './revision.js';
|
|
12
|
-
export * from './services.js';
|
|
13
|
-
export * from './snapshot.js';
|
|
14
|
-
export * from './webhooks.js';
|
package/dist/types/index.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
export * from './assets.js';
|
|
2
1
|
export * from './ast.js';
|
|
3
2
|
export * from './auth.js';
|
|
4
3
|
export * from './collection.js';
|
|
5
|
-
export * from './database.js';
|
|
6
4
|
export * from './events.js';
|
|
7
|
-
export * from './graphql.js';
|
|
8
|
-
export * from './items.js';
|
|
9
5
|
export * from './meta.js';
|
|
10
6
|
export * from './migration.js';
|
|
11
7
|
export * from './revision.js';
|
|
12
|
-
export * from './services.js';
|
|
13
|
-
export * from './snapshot.js';
|
|
14
|
-
export * from './webhooks.js';
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { SchemaOverview, Snapshot, SnapshotDiff, SnapshotField } from '@directus/types';
|
|
2
2
|
import type { Diff } from 'deep-diff';
|
|
3
3
|
import type { Knex } from 'knex';
|
|
4
|
-
import type { Snapshot, SnapshotDiff, SnapshotField } from '../types/index.js';
|
|
5
4
|
export declare function applyDiff(currentSnapshot: Snapshot, snapshotDiff: SnapshotDiff, options?: {
|
|
6
5
|
database?: Knex;
|
|
7
6
|
schema?: SchemaOverview;
|
package/dist/utils/apply-diff.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DiffKind } from '@directus/types';
|
|
1
2
|
import deepDiff from 'deep-diff';
|
|
2
3
|
import { cloneDeep, merge, set } from 'lodash-es';
|
|
3
4
|
import { flushCaches } from '../cache.js';
|
|
@@ -8,7 +9,6 @@ import { useLogger } from '../logger/index.js';
|
|
|
8
9
|
import { CollectionsService } from '../services/collections.js';
|
|
9
10
|
import { FieldsService } from '../services/fields.js';
|
|
10
11
|
import { RelationsService } from '../services/relations.js';
|
|
11
|
-
import { DiffKind } from '../types/index.js';
|
|
12
12
|
import { transaction } from '../utils/transaction.js';
|
|
13
13
|
import { getSchema } from './get-schema.js';
|
|
14
14
|
const logger = useLogger();
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { SchemaOverview, Snapshot, SnapshotDiff } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
|
-
import type { Snapshot, SnapshotDiff } from '../types/index.js';
|
|
4
3
|
export declare function applySnapshot(snapshot: Snapshot, options?: {
|
|
5
4
|
database?: Knex;
|
|
6
5
|
schema?: SchemaOverview;
|
|
@@ -42,5 +42,5 @@ export function getIPFromReq(req) {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
// IP addresses starting with ::ffff: are IPv4 addresses in IPv6 format. We can strip the prefix to get back to IPv4
|
|
45
|
-
return ip?.startsWith('::ffff:') ? ip.substring(7) : ip ?? null;
|
|
45
|
+
return ip?.startsWith('::ffff:') ? ip.substring(7) : (ip ?? null);
|
|
46
46
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { AbstractServiceOptions } from '@directus/types';
|
|
1
2
|
import { ItemsService } from '../services/index.js';
|
|
2
|
-
import type { AbstractServiceOptions } from '../types/services.js';
|
|
3
3
|
/**
|
|
4
4
|
* Select the correct service for the given collection. This allows the individual services to run
|
|
5
5
|
* their custom checks (f.e. it allows `UsersService` to prevent updating TFA secret from outside).
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { Snapshot, SnapshotDiff } from '
|
|
1
|
+
import type { Snapshot, SnapshotDiff } from '@directus/types';
|
|
2
2
|
export declare function getSnapshotDiff(current: Snapshot, after: Snapshot): SnapshotDiff;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import deepDiff from 'deep-diff';
|
|
2
|
-
import { DiffKind } from '
|
|
2
|
+
import { DiffKind } from '@directus/types';
|
|
3
3
|
import { sanitizeCollection, sanitizeField, sanitizeRelation } from './sanitize-schema.js';
|
|
4
4
|
export function getSnapshotDiff(current, after) {
|
|
5
5
|
const diffedSnapshot = {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { SchemaOverview, Snapshot } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
|
-
import type { Snapshot } from '../types/index.js';
|
|
4
3
|
export declare function getSnapshot(options?: {
|
|
5
4
|
database?: Knex;
|
|
6
5
|
schema?: SchemaOverview;
|
|
@@ -18,9 +18,9 @@ export async function getSnapshot(options) {
|
|
|
18
18
|
fieldsService.readAll(),
|
|
19
19
|
relationsService.readAll(),
|
|
20
20
|
]);
|
|
21
|
-
const collectionsFiltered = collectionsRaw.filter((item) => excludeSystem(item));
|
|
22
|
-
const fieldsFiltered = fieldsRaw.filter((item) => excludeSystem(item));
|
|
23
|
-
const relationsFiltered = relationsRaw.filter((item) => excludeSystem(item));
|
|
21
|
+
const collectionsFiltered = collectionsRaw.filter((item) => excludeSystem(item) && excludeUntracked(item));
|
|
22
|
+
const fieldsFiltered = fieldsRaw.filter((item) => excludeSystem(item) && excludeUntracked(item));
|
|
23
|
+
const relationsFiltered = relationsRaw.filter((item) => excludeSystem(item) && excludeUntracked(item));
|
|
24
24
|
const collectionsSorted = sortBy(mapValues(collectionsFiltered, sortDeep), ['collection']);
|
|
25
25
|
const fieldsSorted = sortBy(mapValues(fieldsFiltered, sortDeep), ['collection', 'meta.id']).map(omitID);
|
|
26
26
|
const relationsSorted = sortBy(mapValues(relationsFiltered, sortDeep), ['collection', 'meta.id']).map(omitID);
|
|
@@ -38,6 +38,11 @@ function excludeSystem(item) {
|
|
|
38
38
|
return false;
|
|
39
39
|
return true;
|
|
40
40
|
}
|
|
41
|
+
function excludeUntracked(item) {
|
|
42
|
+
if (item?.meta === null)
|
|
43
|
+
return false;
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
41
46
|
function omitID(item) {
|
|
42
47
|
return omit(item, 'meta.id');
|
|
43
48
|
}
|
package/dist/utils/schedule.js
CHANGED
|
@@ -13,7 +13,10 @@ export function validateCron(rule) {
|
|
|
13
13
|
export function scheduleSynchronizedJob(id, rule, cb) {
|
|
14
14
|
const clock = new SynchronizedClock(`${id}:${rule}`);
|
|
15
15
|
const job = schedule.scheduleJob(rule, async (fireDate) => {
|
|
16
|
-
const
|
|
16
|
+
const nextInvocation = job.nextInvocation();
|
|
17
|
+
if (!nextInvocation)
|
|
18
|
+
return;
|
|
19
|
+
const nextTimestamp = nextInvocation.getTime();
|
|
17
20
|
const wasSet = await clock.set(nextTimestamp);
|
|
18
21
|
if (wasSet) {
|
|
19
22
|
await cb(fireDate);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { File } from '@directus/types';
|
|
2
|
-
import type { Transformation, TransformationSet } from '../types/index.js';
|
|
1
|
+
import type { File, Transformation, TransformationSet } from '@directus/types';
|
|
3
2
|
export declare function resolvePreset({ transformationParams, acceptFormat }: TransformationSet, file: File): Transformation[];
|
|
4
3
|
/**
|
|
5
4
|
* Try to extract a file format from an array of `Transformation`'s.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Joi from 'joi';
|
|
2
2
|
import { InvalidPayloadError } from '@directus/errors';
|
|
3
|
-
import { DiffKind } from '
|
|
3
|
+
import { DiffKind } from '@directus/types';
|
|
4
4
|
const deepDiffSchema = Joi.object({
|
|
5
5
|
kind: Joi.string()
|
|
6
6
|
.valid(...Object.values(DiffKind))
|
|
@@ -3,7 +3,7 @@ import Joi from 'joi';
|
|
|
3
3
|
import { ALIAS_TYPES } from '../constants.js';
|
|
4
4
|
import { getDatabaseClient } from '../database/index.js';
|
|
5
5
|
import { InvalidPayloadError } from '@directus/errors';
|
|
6
|
-
import { DatabaseClients } from '
|
|
6
|
+
import { DatabaseClients } from '@directus/types';
|
|
7
7
|
import { version } from 'directus/version';
|
|
8
8
|
const snapshotJoiSchema = Joi.object({
|
|
9
9
|
version: Joi.number().valid(1).required(),
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
+
import { UserIntegrityCheckFlag } from '@directus/types';
|
|
1
2
|
import { type FetchUserCountOptions } from './fetch-user-count/fetch-user-count.js';
|
|
2
|
-
export declare enum UserIntegrityCheckFlag {
|
|
3
|
-
None = 0,
|
|
4
|
-
/** Check if the number of remaining admin users is greater than 0 */
|
|
5
|
-
RemainingAdmins = 1,
|
|
6
|
-
/** Check if the number of users is within the limits */
|
|
7
|
-
UserLimits = 2,
|
|
8
|
-
All = 3
|
|
9
|
-
}
|
|
10
3
|
export interface ValidateUserCountIntegrityOptions extends Omit<FetchUserCountOptions, 'adminOnly'> {
|
|
11
4
|
flags: UserIntegrityCheckFlag;
|
|
12
5
|
}
|
|
@@ -1,16 +1,8 @@
|
|
|
1
|
+
import { UserIntegrityCheckFlag } from '@directus/types';
|
|
1
2
|
import { validateRemainingAdminCount } from '../permissions/modules/validate-remaining-admin/validate-remaining-admin-count.js';
|
|
2
3
|
import { checkUserLimits } from '../telemetry/utils/check-user-limits.js';
|
|
3
4
|
import { shouldCheckUserLimits } from '../telemetry/utils/should-check-user-limits.js';
|
|
4
5
|
import { fetchUserCount } from './fetch-user-count/fetch-user-count.js';
|
|
5
|
-
export var UserIntegrityCheckFlag;
|
|
6
|
-
(function (UserIntegrityCheckFlag) {
|
|
7
|
-
UserIntegrityCheckFlag[UserIntegrityCheckFlag["None"] = 0] = "None";
|
|
8
|
-
/** Check if the number of remaining admin users is greater than 0 */
|
|
9
|
-
UserIntegrityCheckFlag[UserIntegrityCheckFlag["RemainingAdmins"] = 1] = "RemainingAdmins";
|
|
10
|
-
/** Check if the number of users is within the limits */
|
|
11
|
-
UserIntegrityCheckFlag[UserIntegrityCheckFlag["UserLimits"] = 2] = "UserLimits";
|
|
12
|
-
UserIntegrityCheckFlag[UserIntegrityCheckFlag["All"] = 3] = "All";
|
|
13
|
-
})(UserIntegrityCheckFlag || (UserIntegrityCheckFlag = {}));
|
|
14
6
|
export async function validateUserCountIntegrity(options) {
|
|
15
7
|
const validateUserLimits = (options.flags & UserIntegrityCheckFlag.UserLimits) !== 0;
|
|
16
8
|
const validateRemainingAdminUsers = (options.flags & UserIntegrityCheckFlag.RemainingAdmins) !== 0;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import type { Accountability } from '@directus/types';
|
|
1
2
|
import type { BasicAuthMessage } from './messages.js';
|
|
2
3
|
import type { AuthenticationState } from './types.js';
|
|
3
|
-
export declare function authenticateConnection(message: BasicAuthMessage & Record<string, any>): Promise<AuthenticationState>;
|
|
4
|
+
export declare function authenticateConnection(message: BasicAuthMessage & Record<string, any>, accountabilityOverrides?: Partial<Accountability>): Promise<AuthenticationState>;
|
|
4
5
|
export declare function authenticationSuccess(uid?: string | number, refresh_token?: string): string;
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { isEqual } from 'lodash-es';
|
|
1
2
|
import { DEFAULT_AUTH_PROVIDER } from '../constants.js';
|
|
3
|
+
import getDatabase from '../database/index.js';
|
|
4
|
+
import emitter from '../emitter.js';
|
|
5
|
+
import { createDefaultAccountability } from '../permissions/utils/create-default-accountability.js';
|
|
2
6
|
import { AuthenticationService } from '../services/index.js';
|
|
3
7
|
import { getAccountabilityForToken } from '../utils/get-accountability-for-token.js';
|
|
4
8
|
import { getSchema } from '../utils/get-schema.js';
|
|
5
9
|
import { WebSocketError } from './errors.js';
|
|
6
10
|
import { getExpiresAtForToken } from './utils/get-expires-at-for-token.js';
|
|
7
|
-
export async function authenticateConnection(message) {
|
|
11
|
+
export async function authenticateConnection(message, accountabilityOverrides) {
|
|
8
12
|
let access_token, refresh_token;
|
|
9
13
|
try {
|
|
10
14
|
if ('email' in message && 'password' in message) {
|
|
@@ -24,9 +28,26 @@ export async function authenticateConnection(message) {
|
|
|
24
28
|
}
|
|
25
29
|
if (!access_token)
|
|
26
30
|
throw new Error();
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
31
|
+
const defaultAccountability = createDefaultAccountability(accountabilityOverrides);
|
|
32
|
+
const authenticationState = {
|
|
33
|
+
accountability: defaultAccountability,
|
|
34
|
+
expires_at: getExpiresAtForToken(access_token),
|
|
35
|
+
refresh_token,
|
|
36
|
+
};
|
|
37
|
+
const customAccountability = await emitter.emitFilter('websocket.authenticate', defaultAccountability, {
|
|
38
|
+
message,
|
|
39
|
+
}, {
|
|
40
|
+
database: getDatabase(),
|
|
41
|
+
schema: null,
|
|
42
|
+
accountability: null,
|
|
43
|
+
});
|
|
44
|
+
if (customAccountability && isEqual(customAccountability, defaultAccountability) === false) {
|
|
45
|
+
authenticationState.accountability = customAccountability;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
authenticationState.accountability = await getAccountabilityForToken(access_token, defaultAccountability);
|
|
49
|
+
}
|
|
50
|
+
return authenticationState;
|
|
30
51
|
}
|
|
31
52
|
catch {
|
|
32
53
|
throw new WebSocketError('auth', 'AUTH_FAILED', 'Authentication failed.', message['uid']);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { Accountability } from '@directus/types';
|
|
2
|
+
import { WebSocketMessage } from '@directus/types';
|
|
2
3
|
import type { IncomingMessage, Server as httpServer } from 'http';
|
|
3
4
|
import type { RateLimiterAbstract } from 'rate-limiter-flexible';
|
|
4
5
|
import type internal from 'stream';
|
|
5
6
|
import WebSocket, { type Server } from 'ws';
|
|
6
|
-
import { WebSocketAuthMessage
|
|
7
|
+
import { WebSocketAuthMessage } from '../messages.js';
|
|
7
8
|
import type { AuthenticationState, UpgradeContext, WebSocketAuthentication, WebSocketClient } from '../types.js';
|
|
8
9
|
export default abstract class SocketController {
|
|
9
10
|
server: Server;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { InvalidProviderConfigError, TokenExpiredError } from '@directus/errors';
|
|
3
|
+
import { WebSocketMessage } from '@directus/types';
|
|
3
4
|
import { parseJSON, toBoolean } from '@directus/utils';
|
|
4
5
|
import cookie from 'cookie';
|
|
5
6
|
import { randomUUID } from 'node:crypto';
|
|
@@ -10,12 +11,10 @@ import emitter from '../../emitter.js';
|
|
|
10
11
|
import { useLogger } from '../../logger/index.js';
|
|
11
12
|
import { createDefaultAccountability } from '../../permissions/utils/create-default-accountability.js';
|
|
12
13
|
import { createRateLimiter } from '../../rate-limiter.js';
|
|
13
|
-
import { getAccountabilityForToken } from '../../utils/get-accountability-for-token.js';
|
|
14
14
|
import { getIPFromReq } from '../../utils/get-ip-from-req.js';
|
|
15
15
|
import { authenticateConnection, authenticationSuccess } from '../authenticate.js';
|
|
16
16
|
import { WebSocketError, handleWebSocketError } from '../errors.js';
|
|
17
|
-
import { AuthMode, WebSocketAuthMessage
|
|
18
|
-
import { getExpiresAtForToken } from '../utils/get-expires-at-for-token.js';
|
|
17
|
+
import { AuthMode, WebSocketAuthMessage } from '../messages.js';
|
|
19
18
|
import { getMessageType } from '../utils/message.js';
|
|
20
19
|
import { waitForAnyMessage, waitForMessageType } from '../utils/wait-for-message.js';
|
|
21
20
|
const TOKEN_CHECK_INTERVAL = 15 * 60 * 1000; // 15 minutes
|
|
@@ -139,8 +138,9 @@ export default class SocketController {
|
|
|
139
138
|
let expires_at = null;
|
|
140
139
|
if (token) {
|
|
141
140
|
try {
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
const state = await authenticateConnection({ access_token: token }, accountabilityOverrides);
|
|
142
|
+
accountability = state.accountability;
|
|
143
|
+
expires_at = state.expires_at;
|
|
144
144
|
}
|
|
145
145
|
catch {
|
|
146
146
|
accountability = null;
|
|
@@ -162,7 +162,6 @@ export default class SocketController {
|
|
|
162
162
|
socket.destroy();
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
165
|
-
Object.assign(accountability, accountabilityOverrides);
|
|
166
165
|
this.server.handleUpgrade(request, socket, head, async (ws) => {
|
|
167
166
|
this.catchInvalidMessages(ws);
|
|
168
167
|
const state = { accountability, expires_at };
|
|
@@ -176,10 +175,7 @@ export default class SocketController {
|
|
|
176
175
|
const payload = await waitForAnyMessage(ws, this.authentication.timeout);
|
|
177
176
|
if (getMessageType(payload) !== 'auth')
|
|
178
177
|
throw new Error();
|
|
179
|
-
const state = await authenticateConnection(WebSocketAuthMessage.parse(payload));
|
|
180
|
-
if (state.accountability) {
|
|
181
|
-
Object.assign(state.accountability, accountabilityOverrides);
|
|
182
|
-
}
|
|
178
|
+
const state = await authenticateConnection(WebSocketAuthMessage.parse(payload), accountabilityOverrides);
|
|
183
179
|
this.checkUserRequirements(state.accountability);
|
|
184
180
|
ws.send(authenticationSuccess(payload['uid'], state.refresh_token));
|
|
185
181
|
this.server.emit('connection', ws, state);
|
|
@@ -268,19 +264,20 @@ export default class SocketController {
|
|
|
268
264
|
}
|
|
269
265
|
async handleAuthRequest(client, message) {
|
|
270
266
|
try {
|
|
271
|
-
|
|
272
|
-
this.checkUserRequirements(accountability);
|
|
267
|
+
let accountabilityOverrides = {};
|
|
273
268
|
/**
|
|
274
269
|
* Re-use the existing ip, userAgent and origin accountability properties.
|
|
275
270
|
* They are only sent in the original connection request
|
|
276
271
|
*/
|
|
277
|
-
if (
|
|
278
|
-
|
|
272
|
+
if (client.accountability) {
|
|
273
|
+
accountabilityOverrides = {
|
|
279
274
|
ip: client.accountability.ip,
|
|
280
275
|
userAgent: client.accountability.userAgent,
|
|
281
276
|
origin: client.accountability.origin,
|
|
282
|
-
}
|
|
277
|
+
};
|
|
283
278
|
}
|
|
279
|
+
const { accountability, expires_at, refresh_token } = await authenticateConnection(message, accountabilityOverrides);
|
|
280
|
+
this.checkUserRequirements(accountability);
|
|
284
281
|
client.accountability = accountability;
|
|
285
282
|
client.expires_at = expires_at;
|
|
286
283
|
this.setTokenExpireTimer(client);
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { CloseCode, MessageType, makeServer } from 'graphql-ws';
|
|
2
2
|
import { useLogger } from '../../logger/index.js';
|
|
3
|
+
import { createDefaultAccountability } from '../../permissions/utils/create-default-accountability.js';
|
|
3
4
|
import { bindPubSub } from '../../services/graphql/subscription.js';
|
|
4
5
|
import { GraphQLService } from '../../services/index.js';
|
|
5
|
-
import { getSchema } from '../../utils/get-schema.js';
|
|
6
6
|
import { getAddress } from '../../utils/get-address.js';
|
|
7
|
+
import { getSchema } from '../../utils/get-schema.js';
|
|
7
8
|
import { authenticateConnection } from '../authenticate.js';
|
|
8
9
|
import { handleWebSocketError } from '../errors.js';
|
|
9
|
-
import { ConnectionParams
|
|
10
|
+
import { ConnectionParams } from '../messages.js';
|
|
10
11
|
import { getMessageType } from '../utils/message.js';
|
|
11
12
|
import SocketController from './base.js';
|
|
12
13
|
import { registerWebSocketEvents } from './hooks.js';
|
|
13
|
-
import { createDefaultAccountability } from '../../permissions/utils/create-default-accountability.js';
|
|
14
14
|
const logger = useLogger();
|
|
15
15
|
export class GraphQLSubscriptionController extends SocketController {
|
|
16
16
|
gql;
|
|
@@ -51,6 +51,8 @@ export class GraphQLSubscriptionController extends SocketController {
|
|
|
51
51
|
if (typeof params.access_token === 'string') {
|
|
52
52
|
const { accountability, expires_at } = await authenticateConnection({
|
|
53
53
|
access_token: params.access_token,
|
|
54
|
+
}, {
|
|
55
|
+
ip: client.accountability?.ip ?? null,
|
|
54
56
|
});
|
|
55
57
|
client.accountability = accountability;
|
|
56
58
|
client.expires_at = expires_at;
|
|
@@ -2,7 +2,7 @@ import { useEnv } from '@directus/env';
|
|
|
2
2
|
import emitter from '../../emitter.js';
|
|
3
3
|
import { useLogger } from '../../logger/index.js';
|
|
4
4
|
import { handleWebSocketError, WebSocketError } from '../errors.js';
|
|
5
|
-
import { AuthMode
|
|
5
|
+
import { AuthMode } from '../messages.js';
|
|
6
6
|
import SocketController from './base.js';
|
|
7
7
|
const logger = useLogger();
|
|
8
8
|
export class LogsController extends SocketController {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { WebSocketMessage } from '@directus/types';
|
|
1
2
|
import type { Server as httpServer } from 'http';
|
|
2
|
-
import { WebSocketMessage } from '../messages.js';
|
|
3
3
|
import SocketController from './base.js';
|
|
4
4
|
export declare class WebSocketController extends SocketController {
|
|
5
5
|
constructor(httpServer: httpServer);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { parseJSON } from '@directus/utils';
|
|
2
|
+
import { WebSocketMessage } from '@directus/types';
|
|
2
3
|
import emitter from '../../emitter.js';
|
|
3
4
|
import { useLogger } from '../../logger/index.js';
|
|
4
5
|
import { getAddress } from '../../utils/get-address.js';
|
|
5
6
|
import { WebSocketError, handleWebSocketError } from '../errors.js';
|
|
6
|
-
import { WebSocketMessage } from '../messages.js';
|
|
7
7
|
import SocketController from './base.js';
|
|
8
8
|
import { registerWebSocketEvents } from './hooks.js';
|
|
9
9
|
const logger = useLogger();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { WebSocketMessage } from '@directus/types';
|
|
1
2
|
import { WebSocketController } from '../controllers/index.js';
|
|
2
|
-
import { WebSocketMessage } from '../messages.js';
|
|
3
3
|
import type { WebSocketClient } from '../types.js';
|
|
4
4
|
export declare class HeartbeatHandler {
|
|
5
5
|
private pulse;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { ServiceUnavailableError } from '@directus/errors';
|
|
3
|
+
import { WebSocketMessage } from '@directus/types';
|
|
3
4
|
import { toBoolean } from '@directus/utils';
|
|
4
5
|
import emitter from '../../emitter.js';
|
|
5
6
|
import { WebSocketController, getWebSocketController } from '../controllers/index.js';
|
|
6
|
-
import { WebSocketMessage } from '../messages.js';
|
|
7
7
|
import { fmtMessage, getMessageType } from '../utils/message.js';
|
|
8
8
|
const env = useEnv();
|
|
9
9
|
const HEARTBEAT_FREQUENCY = Number(env['WEBSOCKETS_HEARTBEAT_PERIOD']) * 1000;
|