Security News
Input Validation Vulnerabilities Dominate MITRE's 2024 CWE Top 25 List
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
@astral/editor-integration
Advanced tools
Набор сервисов предназначен для физической развязки общения двух сервисов по схеме клиент-сервер. Самый главный кейс использования - это обмен данными с сервисом другого приложения через iframe. В основе работы сервиса лежит метод window.postMessage, и соответственно используются стандартные механизмы браузера для доставки сообщений из одного приложения в другое. Далее мы подробно рассмотрим варианты использования сервиса. Библиотека предназначена для использования с TypeScript.
В данном разделе рассматриваются основные используемые термины данного модуля.
Стоит придерживаться строго правила именования сервисов.
Шаблон для именования выглядит следующим образом СторонаДействиеТип где:
Пример, нам надо реализовать сервис печати, реализацию которого пишет интегратор:
Еще один пример, получение метаинформации на стороне редактора:
В данном разделе рассматриваются различные варианты использования.
Как правило, для более удобного использования библиотеки в нескольких проектах стоит выделить шаблоны (Template). Шаблоны представляют собой обычные абстрактные классы и нужны главным образом для распространения общих контрактов.
Контракт может выглядеть следующим образом:
/** Контракты для выполнения отправки. */
@frameExchangeService('HostSendV1')
export abstract class HostSendContract {
/** Проверяет можно ли отправить. */
@frameExchangeRequest()
public abstract checkAllowSendRequest: (name: string) => Promise<boolean>;
/** Оповещает о успешной отправке. */
@frameExchangeEvent()
public abstract sendReportSuccessEvent: (name: string) => void;
}
Обратите внимание, что шаблон содержит все необходимые декораторы, в такой ситуации устанавливать их повторно в сервисах обработки нет необходимости. Далее необходимо создать сервис, который будет заниматься реальной обработкой входящих запросов. Выглядеть он может например вот так:
/** Сервис для выполнения отправки. */
export class HostSendHandler extends HostSendContract {
/** Проверяет можно ли отправить. */
checkAllowSendRequest = (name: string): Promise<boolean> => {
console.log(`Проверили сервис ${name} и все хорошо.`);
return Promise.resolve(true);
};
/** Оповещает о успешной отправке. */
sendReportSuccessEvent = (name: string): void => {
console.log(`Оповестили пользователя, что ${name} успешно отправлен.`);
};
}
Обратите внимание, что сервис может использовать любые механизмы и вспомогательные методы, вызываться будут только те методы, что помечены атрибутами @frameExchangeRequest и @frameExchangeEvent. Теперь нам необходимо всего лишь подключить все это дело. Сразу стоит оговориться, что подписываться надо на один и тот же элемент, со стороны iframe это window, а со стороны хоста iframe. Предположим, что в нашей ситуации сервер находится на стороне хоста, а клиент на стороне приложения в iframe.
На стороне сервера будет следующий код:
// Создание сервера (например в componentDidMount).
const iframe = window.frames[0];
const hostSendHandler = new HostSendHandler();
const frameServer = new FrameServer('server', [hostSendHandler], iframe);
// Закрытие открытых соединений (например в componentWillUnmount)
frameServer.closeConnection();
Закрытие соединения выполняет отписку от событий "message", хотя в случае с iframe вероятно новых сообщений не прилетит после unmount. Так или иначе вызов этой функции на усмотрение разработчика, например появилась необходимость прекратить любой обмен. На стороне клиента, который у нас обернут в iframe код может выглядеть следующим образом:
// Создание клиента (generic параметр нужен исключительно для типизации).
const frameClient = new FrameClient('client', window);
const hostSendClient =
frameClient.createClient<HostSendContract>(HostSendContract);
// Использование клиента.
const isAllow = await hostSendClient.checkAllowSendRequest('someName');
if (isAllow) {
hostSendClient.sendReportSuccessEvent('someName');
}
// Закрытие клиента, при необходимости.
frameClient.closeConnection();
В принципе это все, что необходимо для использования данных сервисов.
В принципе особой разницы нет, все сценарии использования одинаковы, но если требуется физическая развязка в рамках одной системы (без импортов шаблонов), то можно просто создать два отдельных класса, а после просто создать соответствующие клиент и сервер. Код может выглядеть так:
/** Клиент. */
@frameExchangeService('HostSendV1')
export abstract class HostSendClient {
/** Проверяет можно ли отправить. */
@frameExchangeRequest()
public abstract checkAllowSendRequest: (name: string) => Promise<boolean>;
/** Оповещает о успешной отправке. */
@frameExchangeEvent()
public abstract sendReportSuccessEvent: (name: string) => void;
}
/** Сервер. */
@frameExchangeService('HostSendV1')
export class HostSendHandler {
/** Проверяет можно ли отправить. */
@frameExchangeRequest()
checkAllowSendRequest = (name: string): Promise<boolean> => {
console.log(`Проверили сервис ${name} и все хорошо.`);
return Promise.resolve(true);
};
/** Оповещает о успешной отправке. */
@frameExchangeEvent()
sendReportSuccessEvent = (name: string): void => {
console.log(`Оповестили пользователя, что ${name} успешно отправлен.`);
};
}
// Создание сервера.
const hostSendHandler = new HostSendHandler();
const frameServer = new FrameServer('server', [hostSendHandler], window);
// Создание клиента.
const frameClient = new FrameClient('frameClient', window);
const hostSendClient = frameClient.createClient<HostSendClient>(HostSendClient);
Также при желании можно использовать различные названия для методов, декоратор позволяет дать другое имя, которое будет использоваться при обмене. В конечном счете важно только, чтобы совпадали контракты (названия методов и сервиса в декораторах, а также наборы аргументов). В случае, если не совпадут названия сервисов или методов, то вызов просто не будет произведен, а методы request типа, будут падать по таймауту (который по умолчанию 30 секунд). В случае, если не совпадут аргументы, то на вход в метод просто прилетят не те аргументы, которые ожидаются, проверки соответствия контрактов нет, так JS подобное не поддерживает. Специальных декораторов для проверки контрактов также нет, так как это по сути далеко от типового использования данной библиотеки.
Пример работы через хуки:
import {
// Общие контракты предоставляемые второй стороной.
ExternalContract1,
ExternalContract2,
ExternalContract3,
// Хуки для подключения обмена через iframe.
useFrameClient,
useFrameServer,
// Общий уникальный ключ для интеграторов (можно вместо него использвоать любой свой).
// Но он обязательно должен совпадать и для клиента (useFrameClient) и для сервера (useFrameServer),
// чтобы они не потребляли сообщения друг друга, так как обмен идет через общее окно.
INTEGRATOR_FRAME_EXCHANGE_ID
} from "@astral/editor-integration";
import {
// Собственные реализации предоставляемые второй стороне.
ImplementedHandler1,
ImplementedHandler2,
ImplementedHandler3,
} from "editor/services/frame";
/**
* Компонент обёртка для рендеринга отчётов редактора.
* @param frameSrc - Адрес страницы, которая будет вставлена в iframe.
*/
export const ExampleWrapper = ({ frameSrc }: ExampleWrapperProps) => {
// Инициализация ссылки на iframe через который будет производится обмен.
const iframeRef = useRef<HTMLIFrameElement>(null);
// Регистрация клиента.
const [frameClient, forceClientInit] = useFrameClient(
INTEGRATOR_FRAME_EXCHANGE_ID, // Уникальный id для стороны обмена (именно стороны обмена, а не сервиса).
(createClient): HostFrameClient => ({ // Создание клиентов используя предоставленные контракты.
externalClient1: createClient(ExternalContract1),
externalClient2: createClient(ExternalContract2),
externalClient3: createClient(ExternalContract3),
}),
() => iframeRef.current!.contentWindow!
);
const [frameServer, forceServerInit] = useFrameServer(
INTEGRATOR_FRAME_EXCHANGE_ID, // Уникальный id для стороны обмена (именно стороны обмена, а не сервиса).
(): HostFrameServer => { // Создание обработчиков сервера (которые реализуют предоставленные контракты).
const implementedHandler1 = new ImplementedHandler1();
const implementedHandler2 = new ImplementedHandler2();
const implementedHandler3 = new ImplementedHandler3();
return {
implementedHandler1,
implementedHandler2,
implementedHandler3,
};
},
() => iframeRef.current!.contentWindow!
);
// Метод для жесткой инициализации
// при обновлении компонента и смены src у iframe.
const forceInit = () => {
forceClientInit();
forceServerInit();
};
return <iframe src={frameSrc} iframeRef={iframeRef} onLoad={forceInit} />;
};
FAQs
Сервисы обмена данными через iframe
We found that @astral/editor-integration demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.