...

Создание провайдера

Тема в разделе "Примеры решений и дополнительных модулей", создана пользователем kuvaev, 6 окт 2023.

  1. kuvaev

    kuvaev Новичок

    В качестве примера интеграции рассмотрим КЦР Контур. Для интеграции удостоверяющего центра с системой ELMA необходимо создать модуль.

    Создайте в настройках поля для конфигурации модуля адрес сервера и API-ключ. Поле Адрес сервера позволит задавать адрес для взаимодействия с сервисом Контур КЦР:

    [​IMG]

    Поле API-ключ необходимо для авторизации в API Контур:

    [​IMG]

    После создания полей они будут доступны при настройке модуля.

    Выпуск сертификата

    Для подписания данных необходима цифровая подпись — сертификат. Для этого в модуле создайте процесс для выпуска сертификата.

    Подписание

    Подпись вычисляется провайдером услуг во время подписания, в нашем случае провайдер — Контур КЦР. Подпись вычисляется с помощью выбранного пользователем сертификата (ключа).

    Для подписания данных необходима цифровая подпись — сертификат. Для этого в модуле создайте процесс для выпуска сертификата.

    Создайте Виджет подписания:

    [​IMG]

    Разместите созданный виджет в расширении для подписания приложения:

    [​IMG]

    Для выбора сертификата пользователем используйте поле типа Категория. Для этого в контексте виджета создайте свойство Сертификаты и дополните форму создания виджета:

    [​IMG]

    Настройте серверный сценарий для получения сертификатов. Сценарий должен выполняться на сервере для обеспечения конфиденциальности заполненных настроек. Ключ от API КЦР Контур не должен распространяться в свободном виде.

    Сценарий получения сертификатов может выглядеть так:
    Код:
    
    // серверный сценарий получения сертификатов для поля Категория
    async function getCertificates(): Promise<void> {
        
    user await System.users.getCurrentUser();
        
    let certificates await System.signs.digitalSigns.search().where((fg) => 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")}`,
                
    codecert.data.__id
            
    };
        });
        
    // если сертификат один, то сразу используем его для подписания
        
    if (Context.fields.certs.data.variants.length === 1) {
            
    Context.data.certs = {nameContext.fields.certs.data.variants[0].namecodeContext.fields.certs.data.variants[0].code};
        };
    };
    Данную функцию необходимо вызвать из клиентского сценария в момент инициализации виджета:
    Код:
    
    async function onInit(): Promise<void> {
        
    await Server.rpc.getCertificates();
    }
    После выбора сертификата необходимо ввести код авторизации для вычисления подписи. Добавьте свойство Код из СМС в контекст виджета и дополните форму:

    [​IMG]

    Чтобы получить код по выбранному сертификату, создайте кнопку, отправляющую запрос на подтверждение операции:

    [​IMG]

    При нажатии на данную кнопку на телефон пользователя отправляется смс с кодом авторизации. Пример функции для отправки смс:
    Код:
    
    async function createFile(): Promise<void> {
        try {
            
    // получение данных о подписываемом приложении
            
    let docApp await Context.data.applicationRef!.fetch();
            
    // извлечение содержимого файла
            
    let fileFileItem await docApp.data.__file.fetch();
            
    let fileLink await file.getDownloadUrl();
            
    Context.data.file_link fileLink;
            
    let fileBuffer await fetch(fileLink).then(=> 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 fullUrlstring = `${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.__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.__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}`);
            }
    };