В качестве примера интеграции рассмотрим КЦР Контур. Для интеграции удостоверяющего центра с системой ELMA необходимо создать модуль.
Создайте в настройках поля для конфигурации модуля адрес сервера и API-ключ. Поле Адрес сервера позволит задавать адрес для взаимодействия с сервисом Контур КЦР:
Поле API-ключ необходимо для авторизации в API Контур:
После создания полей они будут доступны при настройке модуля.
Выпуск сертификата
Для подписания данных необходима цифровая подпись — сертификат. Для этого в модуле создайте процесс для выпуска сертификата.
Подписание
Подпись вычисляется провайдером услуг во время подписания, в нашем случае провайдер — Контур КЦР. Подпись вычисляется с помощью выбранного пользователем сертификата (ключа).
Для подписания данных необходима цифровая подпись — сертификат. Для этого в модуле создайте процесс для выпуска сертификата.
Создайте Виджет подписания:
Разместите созданный виджет в расширении для подписания приложения:
Для выбора сертификата пользователем используйте поле типа Категория. Для этого в контексте виджета создайте свойство Сертификаты и дополните форму создания виджета:
Настройте серверный сценарий для получения сертификатов. Сценарий должен выполняться на сервере для обеспечения конфиденциальности заполненных настроек. Ключ от API КЦР Контур не должен распространяться в свободном виде.
Сценарий получения сертификатов может выглядеть так:
Код:
// серверный сценарий получения сертификатов для поля Категория
async function getCertificates(): Promise<void> {
user = await System.users.getCurrentUser();
let certificates = await System.signs.digitalSigns.search().where((f, g) => g.and(
f.__deletedAt.eq(null), // сертификат не удален
f.__createdBy.eq(user), // создан текущим пользователем
f.certStatus.eq("released") // выпущен
)).size(10).all();
certificates = certificates.filter(cert => cert.data.signProvider?.code === ‘Kontour’); // выбор сертификата для КЦР КОНТУР
// преобразование в формат поля Категория
// name - форматируется из имени пользователя и даты выпуска сертификата
// идентификатор значения — идентификатор сертификата
Context.fields.certs.data.variants = certificates.map((cert) => {
return {
name: `${user.data.__name} ${cert.data.__createdAt.format("DD.MM.YY")}`,
code: cert.data.__id
};
});
// если сертификат один, то сразу используем его для подписания
if (Context.fields.certs.data.variants.length === 1) {
Context.data.certs = {name: Context.fields.certs.data.variants[0].name, code: Context.fields.certs.data.variants[0].code};
};
};
Данную функцию необходимо вызвать из клиентского сценария в момент инициализации виджета:
Код:
async function onInit(): Promise<void> {
await Server.rpc.getCertificates();
}
После выбора сертификата необходимо ввести код авторизации для вычисления подписи. Добавьте свойство Код из СМС в контекст виджета и дополните форму:
Чтобы получить код по выбранному сертификату, создайте кнопку, отправляющую запрос на подтверждение операции:
При нажатии на данную кнопку на телефон пользователя отправляется смс с кодом авторизации. Пример функции для отправки смс:
Код:
async function createFile(): Promise<void> {
try {
// получение данных о подписываемом приложении
let docApp = await Context.data.applicationRef!.fetch();
// извлечение содержимого файла
let file: FileItem = await docApp.data.__file.fetch();
let fileLink = await file.getDownloadUrl();
Context.data.file_link = fileLink;
let fileBuffer = await fetch(fileLink).then(r => r.arrayBuffer());
let fileSize = fileBuffer.byteLength.toString();
let fileHash = await file.getFileMD5Hash();
let fileName = file.data.__name;
// вызов API Контур для запроса СМС-кода
let response = await fetch(`${konturSignUrl}/CreateFile?md5=${fileHash}&fileName=${fileName}&length=${fileSize}`, {
headers,
method: "POST"
});
if (!response.ok) {
throw new Error(await response.text());
};
// создание черновика подписи с параметрами из контекста
await createSignDraft();
} catch (err) {
Context.data.error = err.message;
Context.data.sign_error = true;
return;
};
};
Чтобы запустить процедуру подписания, создайте на форме кнопку для подписи, вызывающую сценарий подписания:
Код:
// функция получает статус подписи и загружает её содержимое в elma365
async function closeDraft(): Promise<void> {
// запрос на получение статуса подписи
let fullUrl: string = `${konturSignUrl}/GetStatus?operationId=${Context.data.operation_id}`;
let responseCheck = await fetch(fullUrl, {headers});
// извлечение необходимых данных для получения подписи
let responseJson = await responseCheck.json();
let resultId = responseJson["FileStatuses"][0].ResultId;
let resultSize = responseJson["FileStatuses"][0].ResultSize;
fullUrl = `${konturSignUrl}/GetResult?resultId=${resultId}&offset=0&size=${resultSize}`
let responseGet = await fetch(fullUrl, {headers});
// сохранение подписи в elma365
try {
let signBuffer = await responseGet.arrayBuffer();
Context.data.debug = signBuffer.byteLength.toString();
let entitySign = await System.signs.entitySigns.search().where(f => f.__id.eq(Context.data.inner_sign_id!)).first();
await entitySign!.uploadSign(signBuffer);
await entitySign!.setStatus(<EntitySignOperationStatus>"completed");
let fetchedSign = await System.signs.entitySigns.search().where(f => f.__id.eq(Context.data.inner_sign_id!)).first();
await fetchedSign!.createSignFile();
Context.data.isDone = true;
} catch (err) {
throw new Error(`error in methods: ${err.message}`);
}
};