Handle ws connexion status code & fix token validation

This commit is contained in:
Convly 2023-03-14 12:43:15 +01:00
parent d85c05d246
commit d8614487de
6 changed files with 67 additions and 23 deletions

View File

@ -36,7 +36,7 @@ const FormTransferTokenContainer = ({
value: 'push-pull', value: 'push-pull',
label: { label: {
id: 'Settings.transferTokens.types.push-pull', id: 'Settings.transferTokens.types.push-pull',
defaultMessage: 'Push & pull', defaultMessage: 'Full Access',
}, },
}, },
]; ];

View File

@ -24,6 +24,10 @@ module.exports = {
DAYS_30: 30 * DAY_IN_MS, DAYS_30: 30 * DAY_IN_MS,
DAYS_90: 90 * DAY_IN_MS, DAYS_90: 90 * DAY_IN_MS,
}, },
TRANSFER_TOKEN_TYPE: {
PUSH: 'push',
PULL: 'pull',
},
TRANSFER_TOKEN_LIFESPANS: { TRANSFER_TOKEN_LIFESPANS: {
UNLIMITED: null, UNLIMITED: null,
DAYS_7: 7 * DAY_IN_MS, DAYS_7: 7 * DAY_IN_MS,

View File

@ -8,7 +8,11 @@ const transferTokenCreationSchema = yup
.shape({ .shape({
name: yup.string().min(1).required(), name: yup.string().min(1).required(),
description: yup.string().optional(), description: yup.string().optional(),
permissions: yup.array().of(yup.string()).nullable(), permissions: yup
.array()
.min(1)
.of(yup.string().oneOf(Object.values(constants.TRANSFER_TOKEN_TYPE)))
.required(),
lifespan: yup lifespan: yup
.number() .number()
.min(1) .min(1)
@ -23,7 +27,11 @@ const transferTokenUpdateSchema = yup
.shape({ .shape({
name: yup.string().min(1).notNull(), name: yup.string().min(1).notNull(),
description: yup.string().nullable(), description: yup.string().nullable(),
permissions: yup.array().of(yup.string()).nullable(), permissions: yup
.array()
.min(1)
.of(yup.string().oneOf(Object.values(constants.TRANSFER_TOKEN_TYPE)))
.required(),
}) })
.noUnknown() .noUnknown()
.strict(); .strict();

View File

@ -1,6 +1,6 @@
import { PassThrough, Readable, Transform } from 'stream'; import { PassThrough, Readable } from 'stream';
import { WebSocket } from 'ws'; import { WebSocket } from 'ws';
import { once } from 'lodash/fp';
import type { import type {
IAsset, IAsset,
IMetadata, IMetadata,
@ -11,7 +11,11 @@ import type {
TransferStage, TransferStage,
} from '../../../../types'; } from '../../../../types';
import { client, server } from '../../../../types/remote/protocol'; import { client, server } from '../../../../types/remote/protocol';
import { ProviderTransferError, ProviderValidationError } from '../../../errors/providers'; import {
ProviderInitializationError,
ProviderTransferError,
ProviderValidationError,
} from '../../../errors/providers';
import { TRANSFER_PATH } from '../../remote/constants'; import { TRANSFER_PATH } from '../../remote/constants';
import { ILocalStrapiSourceProviderOptions } from '../local-source'; import { ILocalStrapiSourceProviderOptions } from '../local-source';
import { createDispatcher } from '../utils'; import { createDispatcher } from '../utils';
@ -168,6 +172,37 @@ class RemoteStrapiSourceProvider implements ISourceProvider {
async initTransfer(): Promise<string> { async initTransfer(): Promise<string> {
return new Promise<string>((resolve, reject) => { return new Promise<string>((resolve, reject) => {
this.ws this.ws
?.on('unexpected-response', (_req, res) => {
if (res.statusCode === 401) {
return reject(
new ProviderInitializationError(
'Failed to initialize the connexion: Authentication Error'
)
);
}
if (res.statusCode === 403) {
return reject(
new ProviderInitializationError(
'Failed to initialize the connexion: Authorization Error'
)
);
}
if (res.statusCode === 404) {
return reject(
new ProviderInitializationError(
'Failed to initialize the connexion: Data transfer is not enabled on the remote host'
)
);
}
return reject(
new ProviderInitializationError(
`Failed to initialize the connexion: Unexpected server response ${res.statusCode}`
)
);
})
?.once('open', async () => { ?.once('open', async () => {
const query = this.dispatcher?.dispatchCommand({ const query = this.dispatcher?.dispatchCommand({
command: 'init', command: 'init',
@ -187,7 +222,7 @@ class RemoteStrapiSourceProvider implements ISourceProvider {
}); });
} }
async bootstrap?(): Promise<void> { async bootstrap(): Promise<void> {
const { url, auth } = this.options; const { url, auth } = this.options;
let ws: WebSocket; let ws: WebSocket;
this.assertValidProtocol(url); this.assertValidProtocol(url);
@ -245,10 +280,6 @@ class RemoteStrapiSourceProvider implements ISourceProvider {
return schemas; return schemas;
} }
#startStepOnce(stage: client.TransferPullStep) {
return once(() => this.#startStep(stage));
}
async #startStep<T extends client.TransferPullStep>(step: T) { async #startStep<T extends client.TransferPullStep>(step: T) {
try { try {
return await this.dispatcher?.dispatchTransferStep({ action: 'start', step }); return await this.dispatcher?.dispatchTransferStep({ action: 'start', step });

View File

@ -1,4 +1,4 @@
import { Readable, Transform } from 'stream'; import { Readable } from 'stream';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';
import { Handler } from './abstract'; import { Handler } from './abstract';
@ -62,6 +62,10 @@ export const createPullController = handlerControllerFactory<Partial<PullHandler
async onMessage(this: PullHandler, raw) { async onMessage(this: PullHandler, raw) {
const msg = JSON.parse(raw.toString()); const msg = JSON.parse(raw.toString());
if (!isDataTransferMessage(msg)) {
return;
}
if (!msg.uuid) { if (!msg.uuid) {
await this.respond(undefined, new Error('Missing uuid in message')); await this.respond(undefined, new Error('Missing uuid in message'));
} }
@ -70,12 +74,17 @@ export const createPullController = handlerControllerFactory<Partial<PullHandler
// Regular command message (init, end, status) // Regular command message (init, end, status)
if (type === 'command') { if (type === 'command') {
const { command, params } = msg; const { command } = msg;
await this.executeAndRespond(uuid, () => { await this.executeAndRespond(uuid, () => {
this.assertValidTransferCommand(command); this.assertValidTransferCommand(command);
return this[command](params); // The status command don't have params
if (command === 'status') {
return this.status();
}
return this[command](msg.params);
}); });
} }

View File

@ -143,15 +143,7 @@ export const handlerControllerFactory =
}, },
send(message, cb) { send(message, cb) {
let payload; ws.send(message, cb);
try {
payload = JSON.stringify(message);
} catch {
payload = message;
}
ws.send(payload as string, cb);
}, },
confirm(message) { confirm(message) {