myDSS SDK iOS

Термины и определения

Тип Описание
DSSUser Объект, содержащий всю необходимую информацию для выполнения действий от имени пользователя. Содержит, в том числе, информацию для доступа к экземпляру DSS (url, корневой сертификат и пр.), "вектор аутентификации" для подтверждения действий и пр. С точки зрения DSS данный объект является устройством, подключенным к учетной записи пользователя DSS
DSSDevice Объект, содержащий информацию об устройствах, подключенных к той же учетной записи, что и объект DSSUser. Данный объект не содержит "вектор аутентификации" и не может использоваться для подтверждения операций или выполнения других действий
DSSOperation Операция DSS, для которой требуется подтверждение/отклонение. Операция может содержать несколько документов. При подтверждении/отклонении операции все содержащиеся в ней документы будут подписаны/отклонены. Операция создается на сервере DSS
DSSOperation.DSSDocument Документ, входящий в операцию, либо обрабатываемый самостоятельно
Document Description "Сниппет" документа, сформированный сервером DSS на основе шаблона для сниппета и содержания документа
Document Preview Визуализированный в человеко-читаемую форму документ, сформированный сервером DSS на основе шаблона для визуализации и содержания документа
Document RawPDF "Сырое" содержание документа (например, текстового файла), преобразованное в формат PDF
DSSCertificate Сертификат, привязанный к ключу подписи в учетной записи на DSS

Установка

Для работы с библиотекой необходимы Xcode 11 и версии новее.

  • Скачайте и распакуйте архив из репозитория
  • Добавьте фреймворк myDSSSDK.xcframework в основной проект в Project Settins -> [Target Name] -> General в секции Frameworks, Libraries and Embedded Content
  • Дополнительные файлы (config.ini и RndmBioViewControllerIPhone.xib) добавьте в проект и прикрепите к приложению в Project Settins -> [Target Name] -> Build Phases в секции Copy Bundle Resources.

Сертификаты

Для взаимодействия с серверами в ресурсы приложения нужно поместить сертификаты. Они должны лежать в директории root-certs в формате PEM.

/root-certs
    /dev            // Директория для сертификатов для взаимодействия с тестовым сервером
        cert_1.crt
        cert_2.crt
        ...
        cert_N.crt
    /prod           // Директория для сертификатов для взаимодействия с продакшн-сервером
        cert_1.crt
        cert_2.crt
        ...
        cert_N.crt

Одновременно может быть использован только один сертификат. Он указывается при инициализации библиотеки:

myDSS.initialize(rootCertificateType: .development)  
// или
myDSS.initialize(rootCertificateType: .production)

Инициализация

Инициализировать библиотеку нужно перед вызовом внутренних функций SDK. Можно выставить уровень логирования и указать тип корневого сертификата.

import UIKit
import myDSSSDK

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // Установка уровня логирования и инициализация библиотеки
        #if DEBUG
        myDSS.setLogLevel([.debug, .keys])
        myDSS.initialize(rootCertificateType: .development)
        #else
        myDSS.initialize(rootCertificateType: .production)
        #endif

        ...

    }

}

Структура

Основные классы для работы:

  • DSSUsersManager: Создаёт и управляет учётными записями пользователя на устройстве
  • DSSOperationsManager: Получает информацию и подписывает операции и документы
  • DSSCertificatesManager: Управляет сертификатами пользователей
  • DSSDevicesManager: Управляет устройствами, привязанными к учётной записи
  • DSSPolicyManager: Получает информацию о сервере DSS

Начало работы

Перед тем, как начать подписывать документы и операции, нужно создать пользователя. Есть 3 способа это сделать:

  1. Создать онлайн с подтверждением
  2. Создать с использованием QR-кода
  3. Создать онлайн с привязкой к другому устройству

Создание пользователя онлайн с подтверждением

DSSUsersManager.createDSSUser(
    serviceURL: serverURL,          // Адрес сервера DSS
    name: name,                     // Имя для сохранения учетной записи
    pushNotificationsData: data,    // Данные для отправки пуш-уведомлений
    deviceName: deviceName,         // Отображаемое дружественное имя устройства
    externalId: externalId,         // Внешний идентификатор
    alias: alias,                   // Человекочитаемый идентификатор устройства
    requirePassword: requirePwd     // Требуется ли установка пароля
) { result in

    // Обрабатываем результат
    switch result {
        case let .success(user): ...    // Пользователь создан и сохранён в хранилище
        case let .failure(error): ...
    }

}

Пользователь создан и сохранён в хранилище, его статус — .installed. Теперь нужно дождаться, когда его статус на сервере DSS сменится на .notVerified. Чтобы проверить текущий статус:

DSSUsersManager.updateStatus(
    user: user
) { result in
    switch result {
        case let .success(user):                // Сведения о пользователе обновлены
            if user.state == .notVerified {     // Проверяем статус

                ...
            }
        case let .failure(error): ...
    }

}

Как только статус пользователя станет .notVerified, мы сможем его верифицировать:

DSSUsersManager.acceptAccountChanges(
    user: user
) { result in

    switch result {
        case let .success(user): ...    // Пользователь подтверждён
        case let .failure(error): ...
    }

}

При верификации может потребоваться QR-код. Это зависит от настроек политики сервера.

Теперь статус пользователя .active, с ним можно работать дальше.

Создание с использованием QR-кода

DSSUsersManager.createDSSUserWithInitQR(
    name: name,                     // Имя для сохранения учетной записи
    pushNotificationsData: data,    // Данные для отправки пуш-уведомлений
    deviceName: deviceName,         // Отображаемое дружественное имя устройства
    externalId: externalId,         // Внешний идентификатор
    alias: alias,                   // Человекочитаемый идентификатор устройства
    requirePassword: true           // Требуется ли установка пароля
) { result in

    switch result {
        case let .success(user): ...    // Пользователь создан и сохранён в хранилище
        case let .failure(error): ...
    }

}

При создании пользователя данным способом потребуется отсканировать QR-код, сгенерированый сервером. После создания статус пользователя — .active, с ним можно работать дальше.

Создание пользователя онлайн с привязкой к другому устройству

Данный способ применяется, когда уже есть устройство с подтверждённым пользователем и нужно привязать новое устройство. Потребуется передать идентификатор данного пользователя на новое устройство.

Получить идентификатор можно у экземпляра пользователя:

// Идентификатор подтверждённого пользователя, к которому привязываем новое устройство
let uid = user.dssUserID

На новом устройстве вызываем метод:

DSSUsersManager.createDSSUserWithApproval(
    serviceURL: serverURL,          // Адрес сервера DSS
    uid: uid,                       // Идентификатор пользователя, к которому привязываем устройство
    name: name,                     // Имя для сохранения учетной записи
    pushNotificationsData: data,    // Данные для отправки пуш-уведомлений
    deviceName: deviceName,         // Отображаемое дружественное имя устройства
    externalID: externalID,         // Внешний идентификатор
    alias: alias,                   // Человекочитаемый идентификатор устройства
    requirePassword: true           // Требуется ли установка пароля
) { result in

    switch result {
        case let .success(newUser): ...    // Пользователь создан и сохранён в хранилище
        case let .failure(error): ...
    }

}

Мы создали нового пользователя, его статус — .approveRequired, требуется подтверждение с основного устройства. На экране отобразится QR-код, который нужно отсканировать основным устройством.

На основном устройстве вызываем метод processAwaitingDevice:

DSSDevicesManager.processAwaitingDevice(
    user: user  // Пользователь к которому привязываем устройство
) { result in
    switch result {
        case .success: ...    // Новое устройство подтверждено/отклонено
        case let .failure(error): ...
    }
}

Далее, на новом устройстве проверяем статус:

DSSUsersManager.checkApprovalStatus(
    user: newUser
) { result in
    switch result {
        case let .success(approvedUser): ...    // Пользователь добавлен
        case let .failure(error): ...           // Пользователь отклонён/не обработан
    }
}

Теперь новое устройство подтверждено, статус пользователя — .active и с ним можно работать на новом устройстве.

Получение списка пользователей и устройств

Пользователи, установленные на устройстве

let users = DSSUsersManager.users

Связанные устройства (в том числе и ожидающие подтверждения)

DSSDevicesManager.listDevices(
    user: user
) { result in
    switch result {
        case let .success(devices): ...
        case let .failure(error): ...
    }
}

Работа с операциями

Получение списка операций

DSSOperationsManager.getOperationsList(
    user: user,         // Пользователь, для которого проверяются операции
    operationType: nil, // Фильтр операций по типу
    operationID: nil    // Фильтр по идентификатору
) { result in
    switch result {
        case let .success(operations): ...   // Получен список операций
        case let .failure(error): ...
    }
}

Подтверждение операции

DSSOperationsManager.confirmOperation(
    operation: operation,   // Операция для подтверждения
    user: user,             // Пользователь, подтверждающий операцию
    signMode: .online       // Способ подтверждения. В данном случае — online
) { result in
    switch result {
        case let .success(approveRequest): ...   // Операция подтверждена, вернули
        case let .failure(error): ...
    }
}

Работа с документами

Загрузка документов

DSSOperationsManager.uploadDocument(
    documentContent: documentData,      // Бинарные данные документа
    title: documentTitle,               // Заголовок документа
    snippetTemplate: documentSnippet,   // HTML-сниппет документа
    previewTemplate: documentPreview,   // HTML-превью документа
    user: user                          // Пользователь-владелец документа
) { result in
    switch result {
        case let .success(documentID):  // Вернётся идентификатор документа
        case let .failure(error): ...
    }
}

Подпись документов

DSSOperationsManager.signDocuments(
    documentsIDs: confirmedDocuments,           // Документы для подписания
    user: user,                                 // Пользователь, подписывающий документы
    signParams: signParams                      // Параметры подписанияы
) { result in
    switch result {
        case let .success(signingResults):      // Вернутся результаты подписания
        case let .failure(error): ...
    }
}

Дополнительная информация

Параметры подписания

Для параметров подписания нужно получить параметры сервера подписания и идентификатор сертификата:

// Получаем параметры сервера подписания
DSSPolicyManager.getDSSSignServerParams(
    user: user  // Пользователь, запрашивающий параметры сервера подписания
) { result in
    switch result {
        case let .success(signServerParams): ... // Вернутся параметры сервера подписания
        case let .failure(error): ...
    }
}

...

// Получаем сертификат
DSSCertificatesManager.listCertificates(
    user: user  // Пользователь, запрашивающий сертификаты
) { result in
    switch result {
        case let .success(certificates): ... // Вернутся сертификаты и запросы на сертификаты
        case let .failure(error): ...
    }
}

...

// Выбираем сертификат, например, первый в списке. Запросы на сертификат игнорируем
let certificate: DSSCertificate = certificates.first(where: {
    $0.type == .certificate
})

...

// Формируем параметры подписания
let processingTemplateId = signServerParams.processingTemplates.first.id
let certificateId = certificate.dssCertificateId

let signParams = DSSSignParams(
    signTemplateId: processingTemplateId,
    certId: certificateId,
    pinCode: "")
)

Создание запроса на сертификат

// Получаем параметры сервера подписания
DSSPolicyManager.getDSSSignServerParams(
    user: user  // Пользователь, запрашивающий параметры сервера подписания
) { result in
    switch result {
        case let .success(signServerParams): ... // Вернутся параметры сервера подписания
        case let .failure(error): ...
    }
}

...

// Подготавливаем параметры. Значения даны для примера
let policyId = signServerParams.caPolicies.first(where: { $0.caType == .DSSOutOfBandEnroll }).id
let templateId = policy.ekuTemplates.first?.value.first
let testDN = ["2.5.4.3": "Test"]

// Создаём запрос на сертификат
DSSCertificatesManager.createCertificate(
    user: user,             // Пользователь, создающий запрос на сертификат
    dn: testDN,             // Различительное имя субъекта
    templateId: templateId, // Идентификатор шаблона сертификата
    caId: policyId          // Идентификатор обработчика УЦ
) { result in
    switch result {
        case let .success(certRequest): ... // Вернётся запрос на сертификат
        case let .failure(error): ...
    }
}

Проверка состояния сертификатов и запросов

DSSCertificatesManager.listCertificates(
    user: user // Пользователь, запрашивающий информацию
) { result in
    switch result {
        case let .success(certificates): ... // Вернётся массив сертификатов и запросов
        case let .failure(error): ...
    }
}

// Отфильтруем выпущеные сертификаты
let activeCertificates = certificates.filter {
    $0.type == .certificate && $0.dssCertificateId != nil
}

Персонализация UI

За внешний вид UI отвечает структура Appearance. Она позволяет изменить внешний вид кнопок, фоновых элементов и изображений.

/// Статический класс для хранения данных об внешнем виде UI
struct Appearance {

    /// Картинки в интерфейсе
    var images: Images

    /// Внешний вид фоновых элементов
    var views: Views

    /// Внешний вид кнопок
    var buttons: Buttons

    /// Внешний вид надписей
    var labels: Labels

    /// Внешний вид контроллеров навигации
    var navigationControllers: NavigationControllers

}

Пример:

/// Изменяем только цвет основных кнопок
myDSS.appearance.buttons.primary.backgroundColor = .red

/// Изменяем весь стиль основных кнопок:
myDSS.appearance.buttons.primary =
    ButtonAppearance(
        titleFont: .systemFont(ofSize: 16, weight: .medium),
        titleColor: .init(named: "button_primary_title"),
        titleColorPressed: .init(named: "button_primary_title_pressed"),
        titleColorDisabled: .init(named: "button_primary_title_disabled"),
        backgroundColor: .init(named: "button_primary_bg"),
        backgroundColorPressed: .init(named: "button_primary_bg_pressed"),
        backgroundColorDisabled: .init(named: "button_primary_bg_disabled"),
        cornerRadius: 25
    )