SwiftUI в 2022. Что нового?

Всем привет, с вами я, Наиль Габутдинов, iOS разработчик.

Apple выпустила первый SwiftUI в 2019 году, предлагая тем самым новый подход создания приложений в декларативном стиле. SwiftUI 4.0, представленный этим летом на WWDC 2022 вместе с iOS 16 и macOS 13 включает в себя много нововведений для более качественных и универсальных приложений под разные платформы Apple с меньшим количеством кода.

Давайте посмотрим, что нового.

Swift Charts

Swift Charts — это целый фреймворк, который позволяет преобразовывать данные в информативные и настраиваемые графики и диаграммы с очень небольшим количеством кода. Теперь можно создавать линейные графики, гистограммы или точечные диаграммы. Фреймворк позволяет гибко настраивать визуализацию данных по всевозможным параметрам а также добавлять анимации.

Доступные виды графиков: BarMark, LineMark, AreaMark, PointMark, RectangleMark, RuleMark. Рассмотрим некоторые поближе:

  • BarMark используется для создания гистограммы по значениям x и y.

  • LineMark используется для создания линейного графика. Поменяв BarMark на LineMark, можем преобразовать гистограмму в линейную диаграмму.

Charts API настолько гибок, что вы можете накладывать несколько диаграмм в одном view. Вот пример:

Навигация

Пожалуй, одним из самых проблемных мест SwiftUI до iOS 16 была навигация: отсутствовал доступ к стеку навигации, было невозможно разделить UI и слой навигации, возникали случайные проблемы с работой NavigationView. Компонент NavigationView стал deprecated в iOS 16 и заменен на NavigationStack и NavigationSplitView. Основные изменения в API навигации позволяют добавлять и убирать view из стека, гибко управлять роутингом между view, программно делать data-driven структуру экранов и переходов в приложении.

NavigationStack

NavigationStack работает аналогично старому NavigationView. Чтобы реализовать простой push-переход из списка, мы можем использовать стек со списком, в котором лежат NavigationLink.

NavigationLink тоже обновилась. Вместо прямого указания destinationView теперь она может принимать в качестве параметра value, а целевой view может объявляться в модификаторе navigationDestination стэка в зависимости от этого value. В приведенном ниже коде у нас есть три модификатора navigationDestination один для текстового элемента, второй для элемента целочисленного типа и третий для элемента типа Color.

NavigationSplitView

Появилась новая структура NavigationSplitView, которая позволяет организовать Master-Detail интерфейс для iPad или Mac в два или три столбца. При этом она будет автоматически трансформироваться в привычный поэкранный интерфейс для iPhone или Apple Watch, что очень удобно, можно делать одну навигацию для разных платформ.

Новый навигационный API из SwiftUI 4.0 выглядит как более удобное и гибкое решение упрощающее работу с диплинками. К сожалению, как и большинство обновлений, новая навигация SwiftUI доступна только для iOS/iPadOS 16

Новые View и Controls

Многострочные TextField

В SwiftUI 4 решена еще одна раздражающая проблема: возможность создания многострочных текстовых полей с плейсхолдерами. Теперь, используя модификатор lineLimit, это сделать просто.

TextView будет расширяться по мере заполнения пока не достигнет максимального количества строк. Если пользователь продолжит вводить текст на новые строки, то поле станет прокручиваемым не увеличиваясь в размере. Используя три точки мы можем установить минимальное и максимальное число строк, чтобы определить минимальную и максимальную высоту текстового поля.

MultiDatePicker

SwiftUI также порадовал нас новым компонентом с множественным выбором дат в календаре, который можно добавить в несколько строк кода:

Gauge

Представлен новый view, называемый Gauge, он используется для отображения прогресса в разных форматах. Вот простой способ добавить его:

Можно указать текущее, минимальное и максимальное значения, чтобы они отображались в режиме просмотра прогресса.

Можно, например, поменять стиль на круговой:

ShareLink

В SwiftUI представлена новая ShareLink, которая позволяет легко добавить меню шеринга. Установка потребует минимум кода, ниже пример:

AnyLayout

Новая версия SwiftUI предоставляет компонент AnyLayout, который соответствует протоколу Layout. С его помощью разработчики создают индивидуальные и сложные макеты. Вы можете использовать AnyLayout для создания динамического лэйаута, который реагирует на действия пользователей или изменения среды (размеров и ориентаций экрана).

В примере ниже при нажатии, Layout переключается с вертикального на горизонтальный, и расположение в нем прямоугольников динамически меняется:

Обратите внимание, что начиная с Xcode 14, VStack и HStack не соответствуют протоколу Layout, как указывалось ранее. Для условных лэйаутов теперь можно использовать VStackLayout и HStackLayout.

Вывод

В этом году Apple снова сделала значительное обновление SwiftUI. Charts API, переработанная навигация, появление AnyLayout и другие нововведения определенно помогут нам делать код интерфейсов более элегантным и универсальным. Надеюсь, что при этом радикально снизится количество проблем с базовыми вещами, с которыми мы сталкивались в ранних версиях SwiftUI, и работа приложений на этой современной и декларативной технологии станет стабильней.  

Я продолжаю изучать новые возможности API SwiftUI и буду рассматривать их более подробно в следующих статьях.


ссылка на оригинал статьи https://habr.com/ru/post/708770/

Очень кратенький обзор Orange Pi 5

Всем привет и с наступающим !

Дома живут две raspberry pi 4 на одном из которых живет в DMZ nexcloud + некоторое публичное барахлишко, а на другом home assistant. Есть так же один raspberry pi 3 с octoprint для управления 3д принтера. Брал до. Сейчас цены на малинку вообще не гуманные, тот же dns-shop хочет 17 999 ₽ за 4 с 4 гигами памяти. А тут, в ноябре, анонсы Orange Pi 5 и предзаказы на али и железо мощнее и цена намного ниже. Ну и заказал 8 гиговую версию 22 ноября за 5104,56 ₽ + доставка 756,16 ₽. Отправили 15 декабря, 31 декабря забрал в Саратове на почте, xотя обещали в другой пункт выдачи. Оказалось не страшно, героически пришедших на почту в 9:12 утра 31 декабря было всего трое, даже в очереди стоять не пришлось.

C помощью dd залил на 128 гиговую micro sd карту серверную Ubuntu 22.04 c официального сайта http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/service-and-support/Orange-pi-5.html

sudo dd if=/tmp/Orangepi5_1.0.2_ubuntu_jammy_server_linux5.10.110.img  \  of=/dev/sde status=progress conv=fsync

Потом через parted отресайзил /dev/sde2

Все сразу же загрузилось. `apt upgrade; apt update` прошел без проблем, репы прописаны http://repo.huaweicloud.com. Небольшой косяк был с монитором 3440×1440 через HDMI. Консоль внизу заканчивалась где-то гораздо ниже монитора.

Пробовал подключить монитор 2560×1440 через кабель Type-С — DisplayPort, тут все работает норм

Внешний вид полностью соответствует http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-5.html

Встроенного WiFi нет, но можно воткнуть USB или M2 адаптер

Пробовал воткнуть в M2 порт Samsung 860 EVO [MZ-N6E500BW], ноль реакции, хотя он точно рабочий, через внешний адаптер и Type-C виден и работает. Нашел только одно упоминание что все норм работает вот тут https://www.reddit.com/r/OrangePI/comments/zliitn/orange_pi_5_2280_nvme_ssd/. У меня же и lspci и nvme list пустые, хотя по мануалу lspci должен показывать контроллер. Оказывается работают только SSD с NVMe поддержки которого нет в MZ-N6E500BW. Надо что то вроде Samsung 960 EVO [MZ-V6E250BW]

Ядро — 5.10.110

Немного DevOpsятины 🙂

Docker уже стоял, загрузил minikube и kubectl для arm64, все чудесно работает

orangepi@orangepi5:~$ minikube status minikube type: Control Plane host: Running kubelet: Running apiserver: Running kubeconfig: Configured  orangepi@orangepi5:~$ kubectl version WARNING: This version information is deprecated and will be replaced with the output from kubectl version --short.  Use --output=yaml|json to get the full version. Client Version: version.Info{Major:"1", Minor:"26", GitVersion:"v1.26.0", GitCommit:"b46a3f887ca979b1a5d14fd39cb1af43e7e5d12d", GitTreeState:"clean", BuildDate:"2022-12-08T19:58:30Z", GoVersion:"go1.19.4", Compiler:"gc", Platform:"linux/arm64"} Kustomize Version: v4.5.7 Server Version: version.Info{Major:"1", Minor:"25", GitVersion:"v1.25.3", GitCommit:"434bfd82814af038ad94d62ebe59b133fcb50506", GitTreeState:"clean", BuildDate:"2022-10-12T10:49:09Z", GoVersion:"go1.19.2", Compiler:"gc", Platform:"linux/arm64"} orangepi@orangepi5:~$ kubectl get ns NAME              STATUS   AGE default           Active   68m ingress-nginx     Active   59m kube-node-lease   Active   68m kube-public       Active   68m kube-system       Active   68m orangepi@orangepi5:~$ kubectl get nodes -o wide NAME       STATUS   ROLES           AGE   VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION             CONTAINER-RUNTIME minikube   Ready    control-plane   69m   v1.25.3   192.168.49.2   <none>        Ubuntu 20.04.5 LTS   5.10.110-rockchip-rk3588   docker://20.10.20

Проц

orangepi@orangepi5:~$ lscpu | head -n 5 Architecture:                    aarch64 CPU op-mode(s):                  32-bit, 64-bit Byte Order:                      Little Endian CPU(s):                          8 On-line CPU(s) list:             0-7

Память

orangepi@orangepi5:~$ free -h                total        used        free      shared  buff/cache   available Mem:           7.5Gi       1.0Gi       2.2Gi        46Mi       4.3Gi       6.4Gi Swap:          3.8Gi          0B       3.8Gi 
btop
btop

После 15 минут `stress-ng -c 8` без радиаторов и без обдува самая большая температура на ядрах — 86.8°C. при 24°C в комнате.

orangepi@orangepi5:~$ sensors gpu_thermal-virtual-0 Adapter: Virtual device temp1:        +79.5°C  littlecore_thermal-virtual-0 Adapter: Virtual device temp1:        +84.1°C  bigcore0_thermal-virtual-0 Adapter: Virtual device temp1:        +86.8°C  tcpm_source_psy_6_0022-i2c-6-22 Adapter: rk3x-i2c in0:           0.00 V  (min =  +0.00 V, max =  +0.00 V) curr1:         0.00 A  (max =  +0.00 A)  npu_thermal-virtual-0 Adapter: Virtual device temp1:        +81.3°C  center_thermal-virtual-0 Adapter: Virtual device temp1:        +80.4°C  bigcore1_thermal-virtual-0 Adapter: Virtual device temp1:        +86.8°C  soc_thermal-virtual-0 Adapter: Virtual device temp1:        +83.2°C  (crit = +115.0°C)  


ссылка на оригинал статьи https://habr.com/ru/post/708766/

Как можно ускорить Python сегодня


Python не перестаёт удивлять многих своей гибкостью и эффективностью. Лично я являюсь приверженцем С и Fortran, а также серьёзно увлекаюсь C++, поскольку эти языки позволяют добиться высокого быстродействия. Python тоже предлагает такие возможности, но дополнительно выделяется удобством, за что я его и люблю.

Этот инструмент способен обеспечивать хорошее быстродействие, поскольку имеет в арсенале ключевые оптимизированные библиотеки, а также возможность динамической компиляции основного кода, который предварительно не компилировался. Однако скорость Python значительно падает, когда дело доходит до обработки крупных датасетов или более сложных алгоритмов. В текущей статье мы разберём:

  1. Почему столь важно думать о «будущем разнородных вычислений».
  2. Две ключевых сложности, которые необходимо преодолеть в открытом решении.
  3. Параллельное выполнение задач для более эффективного задействования CPU.
  4. Использование ускорителя для дополнительного повышения быстродействия.

Один только третий пункт позволил увеличить быстродействие в 12 раз притом, что четвёртый позволяет добиться ещё большего за счёт ускорителя. Эти простые техники могут оказаться бесценными при работе с Python, когда требуется добиться дополнительного ускорения программы. Описанные здесь приёмы позволяют нам уверенно продвигаться вперёд без длительного ожидания результатов.

Размышляя о будущем разнородных вычислений

Несмотря на то, что понимание разнородности не особо важно, если нас интересует лишь ускорение кода Python, стоит отметить значительный сдвиг, который сейчас наблюдается в сфере вычислительных наук. Компьютеры с каждым годом становятся все быстрее. Поначалу этот прирост производительности обеспечивали грамотные и более сложные архитектуры. Затем где-то в период между 1989 и 2006 годами основным ускоряющим фактором стала растущая тактовая частота. Однако в 2006 её увеличение вдруг перестало иметь смысл, и для повышения скорости снова потребовалось изменение архитектур.

В результате многоядерные процессоры обеспечили повышенное быстродействие за счёт увеличения количества (однородных) ядер в процессоре. В отличие от повышения частот получение дополнительного ускорения за счёт многоядерности потребовало перестройки ПО. Классическая статья Херба Саттера «The Free Lunch Is Over» акцентировала потребность в многопоточности. И хотя этот сдвиг был необходим, он существенно усложнил жизнь разработчикам программного обеспечения.

Затем появились ускорители, способные выполнять вычисления в дополнение к CPU. Наиболее успешными из них пока что являются графические процессоры (GPU). Изначально GPU были разработаны для разгрузки основного процессора путём снятия с него задачи по обработке графики, передаваемой на дисплей компьютера. Для задействования этой дополнительной вычислительной мощности появилось несколько программных моделей, но вместо отправки на дисплей их результаты передавались программе, выполняющейся на CPU. Сегодня «разнородные» процессоры в одной системе больше не являются равноценными. Тем не менее все популярные языки программирования обычно предполагают одно вычислительное устройство, поэтому, когда мы выбираем часть кода для выполнения на отдельном таком устройстве, то используем термин «разгрузка».

Несколько лет назад два легендарных представителя индустрии, Джон Хеннеси и Дэвид Паттерсон, заявили о вступлении в «A New Golden Age for Computer Architecture» (новый золотой век компьютерной архитектуры). Разнородные вычисления начинают набирать обороты благодаря появлению множества идей для специализированных под разные области процессоров. Некоторые эти тенденции ждёт успех, другие же обречены на провал, но суть в том, что сфера вычислений изменилась бесповоротно, поскольку больше не ориентирована на выполнение всех вычислений на одном устройстве.

Две ключевых сложности, преодолеваемые одним хорошим решением

И хотя сегодня популярна технология CUDA, она относится лишь к GPU Nvidia. Нам же нужны открытые решения для обработки целой волны новых архитектур ускорителей от различных вендоров. Программам, выполняющимся на разнородных платформах, необходим способ определения доступных в среде выполнения устройств. Им также нужен способ снять с этих устройств нагрузку.

CUDA игнорирует возможность обнаружения устройств, предполагая, что доступны только GPU Nvidia. Python-программистам для использования GPU с CUDA (Nvidia) или ROCm (AMD) доступна библиотека CuPy. Но хоть CuPy и является открытым решением, она не повышает быстродействие CPU и не охватывает других производителей или архитектуры. Нам бы больше подошла программа с возможностью использования на устройствах разных вендоров и поддержкой новых аппаратных решений. Однако, прежде чем радоваться разгрузке с помощью ускорителя, давайте убедимся, что получаем максимум от нашего CPU, так как понимание принципов использования параллелизма и скомпилированного кода также поможет разобраться с использованием многопоточности на ускорителях.

Numba является открытым JIT-компилятором кода Python и NumPy, разработанным Anaconda. Он может компилировать всевозможный численный код Python, включая многие функции NumPy. Numba также поддерживает автоматическое распараллеливание циклов, генерацию ускоряемого GPU кода, создание универсальных функций (ufuncs) и обратных вызовов Си.

В рамках проекта Numba компания Intel разработала автоматический параллелизатор, который включается установкой опции parallel=True в @numba.jit. Этот инструмент анализирует распараллеливаемые по данным области кода в скомпилированной функции и планирует их параллельное выполнение. Есть два типа операций, которые Numba может распараллеливать автоматически:

  1. Неявные распараллеливаемые области, такие как выражения массивов NumPy, NumPy ufunc и функции редукции NumPy.
  2. Явные циклы параллельных данных, определяемые с помощью выражения numba.prange.

Вот пример простого цикла Python:

def f1(a,b,c,N):     for i in range(N):         c[i] = a[i] + b[i]

Его можно сделать явно параллельным, изменив последовательный диапазон (range) на параллельный (prange) и добавив директиву njit:

@njit(parallel=True) def add(a,b,c,N):     for i in prange(N):         c[i] = a[i] + b[i]

В итоге время выполнения сократилось с 24.3 до 1.9 секунды, но результаты от системы к системе могут отличаться. Чтобы опробовать этот приём, клонируйте репозиторий с образцами oneAPI (git clone) и откройте блокнот AI-and-Analytics/Jupyter/Numba_DPPY_Essentials_training/Welcome.ipynb. Проще всего сделать это, зарегистрировав бесплатный аккаунт на Intel® DevCloud for oneAPI.

Дополнительное повышение быстродействия с помощью ускорителя

Ускоритель оказывается особенно эффективен, когда приложение загружено достаточным объёмом работы, чтобы её разгрузка оправдала необходимые для этого затраты. Первым шагом мы компилируем выбранные вычисления (фрагмент программы), чтобы их можно было разгрузить на другие устройства. Продолжая предыдущий пример, мы используем расширения Numba (numba-dpex) для обозначения разгружаемого фрагмента программы (подробнее читайте в Jupyter notebook training).

@dppy.kernel def add(a, b, c):     i = dppy.get_global_id(0)     c[i] = a[i] + b[i]

Фрагмент программы компилируется и распараллеливается так, будто ранее он использовал @njit для подготовки к выполнению на CPU, только на сей раз он подготавливается к переносу на устройство. Код компилируется в промежуточный язык (SPIR-V), который среда выполнения в установленный для его запуска момент сопоставляет с устройством. Это универсальное решение, которое позволяет переносить вычисления на ускоритель любого вендора.

Аргументами для фрагмента программы могут выступать массивы NumPy или массивы унифицированной разделяемой памяти (USM) (тип массивов, явно помещаемых в унифицированную разделяемую память), в зависимости от того, что больше соответствует нашим потребностям. Этот выбор повлияет на настройку данных и активацию фрагментов программы.

Далее мы задействуем решение C++ для открытого мультивендорного и мультиархитектурного программирования под названием SYCL, используя опенсорсную библиотеку dpctl (подробнее читайте в документации GitHub и «Interfacing SYCL and Python for XPU Programming»). Эта библиотека позволяет программам Python обращаться к устройствам SYCL, очередям и источникам памяти, а также выполнять операции с массивами/тензорами Python. Такой подход позволяет избежать изобретения новых решений, уменьшить потребность в освоении новых техник и повысить совместимость.

Подключение к устройству выполняется просто:

device = dpctl.select_default_device() print("Using device ...") device.print_device_info()

Дефолтное устройство можно установить с помощью переменной среды SYCL_DEVICE_FILTER, если мы хотим контролировать выбор устройства без изменения этой простой программы. Библиотека dpctl также поддерживает программное управление для анализа и выбора доступного устройства на основе свойств аппаратного обеспечения.

Фрагмент программы также можно активировать (перенести и выполнить) на устройстве буквально парой строк кода:

with dpctl.device_context(device):     dpar_add[global_size,dppy.DEFAULT_LOCAL_SIZE](a,b,c)

Использование device_context приводит к тому, что среда выполнения создаёт все необходимые копии данных (наши данные по-прежнему находились в стандартных массивах NumPy). Кроме этого, dpctl предлагает возможность явного выделения USM-памяти для устройств и управления ею. Это особенно пригождается, когда при углублении в оптимизацию возникают сложности с поручением её обработки для стандартных массивов NumPy среде выполнения.

Асинхронность и синхронность

Написание кода в стиле Python легко поддерживается описанными выше синхронными механизмами. При этом, если мы готовы слегка изменить код, перед нами также открываются асинхронные возможности и сопутствующие им преимущества (сокращение или исключение задержек при перемещении данных и активации фрагментов кода). Подробнее об асинхронном выполнении можете узнать из примера кода в dpctl gemv.

А что насчёт CuPy?

Библиотека CuPy является NumPy/SciPy-совместимой библиотекой, позволяющей выполнять их код на платформах Nvidia CUDA или AMD ROCm. Однако серьёзные усилия, необходимые для повторной реализации CuPy под новые платформы, существенно мешают обеспечить мультивендорную поддержку в одной программе Python, так что две вышеупомянутых сложности она не решает. CuPy может выбирать только среди GPU-устройств с CUDA. При этом она не даёт возможности прямого контроля памяти, хотя и может автоматически выполнять её пулинг с целью сокращения числа вызовов cudaMalloc. При переносе выполнения кода она не позволяет выбирать для этого устройство и в случае отсутствия доступного GPU с CUDA даст сбой. Большую универсальность для приложения можно достичь за счёт использования более удобного инструмента, решающего указанные сложности, связанные с разнородностью устройств.

А что насчёт scikit-learn?

В целом программирование на Python отлично подходит для реализации вычислений, направленных на конкретные данные (методика «compute-follows-data»). Библиотека dpctl поддерживает тензоры, которые мы связываем с заданным устройством. Если мы в своей программе можем привести данные к тензору для устройства (например, dpctl.tensor.asarray(data, device="gpu:0")), они будут ассоциированы с этим устройством и помещены на него. При использовании пропатченной версии scikit-learn, способной распознавать эти тензоры, задействующие такой тензор методы вычисляются на связанном устройстве автоматически.

Это прекрасный вариант использования динамической типизации в Python, позволяющий понять, где располагаются данные, и направить вычисления именно туда. Единственное, что изменяется в нашем коде Python – это место, где наши тензоры преобразуются в тензоры для устройства. Судя по текущим отзывам, мы ожидаем, что методика «compute-follows-data» станет наиболее популярной среди Python-программистов.

Открытость, мультивендорность и мультиархитектурность

Python может выполнять роль инструмента, объединяющего мощность аппаратного разнообразия и позволяющего задействовать потенциал надвигающегося «Кембрийского взрыва» в сфере ускорителей. Здесь стоит иметь ввиду использование распараллеливания данных через Numba совместно с dpctl и техниками «compute-follows-data» из scikit-learn, поскольку все эти решения не ограничены какими-либо вендорами или архитектурами.

При том, что Numba предоставляет отличную поддержку для NumPy, в перспективе будет нелишним рассмотреть дополнительные возможности для SciPy и прочих направлений программирования на Python. Фрагментация API массивов в Python подтолкнула к стандартизации этих API (подробнее читайте здесь) на фоне возникшего стремления разработчиков разделять рабочие нагрузки с устройствами, помимо CPU. Стандарт API массивов во многом помогает расширить область применения Numba и dpctl, а также их влияние. В NumPy и CuPy поддержка API массивов уже внедрена, и на очереди стоят dpctl с PyTorch. По мере охвата всё большего числа библиотек, реализация поддержки разнородных вычислений (ускорителей всех типов) становится всё доступнее.

Простого использования dpctl.device_context оказывается недостаточно в более сложном коде Python со множеством потоков или асинхронных задач (вот соответствующая тема на GitHub). Здесь будет лучше следовать политике «compute-follows-data», по крайней мере в сложном многопоточном коде Python. Этот подход может стать предпочтительным в сравнении с использованием device_context.

Перед нами есть масса возможностей по оптимизации способов повышения быстродействия Python. Все эти решения являются опенсорсными и на сегодня работают очень хорошо.

Дополнительные материалы

Самое лучшее обучение – это практика. Ну а чтобы было проще начать, ниже я привожу некоторые полезные вспомогательные источники информации.

Для Numba и dpctl есть 90-минутное видео, в котором разбираются описанные выше принципы: «Data Parallel Essentials for Python».

Ещё есть прекрасное видео «Losing your Loops Fast Numerical Computing with NumPy» от Джейка Вандерпласа (автора «Python Data Science Handbook»), где он рассказывает о способах эффективного использования NumPy.

Все описанные в данной статье решения для использования Python в сфере разнородных вычислений являются опенсорсными и по умолчанию включены в наборы инструментов Intel® oneAPI Base и Intel® AI Analytics. NumPy с поддержкой SYCL размещена на GitHub. Расширения компилятора Numba для выделения фрагмента программы и автоматического переноса соответствующей вычислительной нагрузки также находятся на GitHub. Что касается открытых инструментов для управления распараллеливанием данных, то по этой теме доступна документация на GitHub и исследовательская работа «Interfacing SYCL and Python for XPU Programming». Они позволяют программам Python обращаться к устройствам SYCL, очередям, памяти, а также выполнять операции с массивами/тензорами, используя ресурсы SYCL.

Помимо этого, в них реализована обработка исключений, включая асинхронные ошибки в коде устройств. Эти ошибки перехватываются при повторном выбрасывании в виде синхронных исключений соответствующими функциями-обработчиками. Это поведение обеспечивается генераторами расширений Python и хорошо описано в документации сообщества: Cython и Pybind11.

▍ P.S. от переводчика🎄

Уважаемые читатели, поздравляю вас с наступающим Новым годом! По всей видимости, он будет не менее ярким, чем уходящий, и с учётом такого положения дел хочется пожелать всем конструктивной оптимизации жизни и успешной адаптации под быстроменяющиеся реалии. Пусть спутниками в этом процессе для вас станут стойкость, оптимизм и простая взаимная человеческая любовь, которой в нашей чувственной палитре мира стало явно нехватать. Балансируя на правильной смеси этих трёх компонентов, будет куда проще как преодолевать сложности, так и покорять новые вершины.

Отдельно хочу выразить признательность всем постоянным читателям, в особенности тем, кто помогает повышать качество публикуемых мной материалов с помощью объективной критики, рекомендаций и банального указания на ошибки. Я осознаю неполноту своих знаний и стремлюсь при каждой возможности их расширить. Спасибо вам и Всех Благ!


ссылка на оригинал статьи https://habr.com/ru/company/ruvds/blog/708358/

Postgresso 12 (49)

В этом, декабрьском номере мы собрали интересные ссылки со всех Postgresso, начиная с январского. Вот с этого:

ЯНВАРЬ

Postgresso 1 (38)

Итоги 2021 начинает подводить Энди Павло, тот, который поддерживает Базу данных баз данных:

Databases in 2021: A Year in Review

Напомним, что Энди ещё и сооснователь компании «Выдронастройка» — OtterTune, где все ключевые посты занимают выдры — см. их страницу НАША КОМАНДА:

A short summary of the Year 2021 in the PostgreSQL ecosystem

Обзор сделан авторами с MigOps, поэтому не без уклона в свои разработки. Что не умаляет актуальности [на 2021-й год (как, возможно, догадался проницательный читатель, курсивом набран «декабрьский» текст, а «прямой» текст взят из соответствующих выпусков)].

В этом выпуске есть целый раздел с вот таким названием:

Заклятые друзья: TOAST и JSONB

В нём, в том числе, о статьях Олега Бартунова:

Проклятье TOAST и с каким маслом его ест JSONB и

Борьба с TOAST или будущее JSONB в PostgreSQL.

ФЕВРАЛЬ

Postgresso 2 (39)

Изменился тон обложки и появился огромный раздел:

Миграция

А в нём ссылки на фундаментальный документ Oracle to Postgres Conversion и на тоже по-своему фундаментальную полуторачасовую презентацию Петра Петрова Oracle to Postgres Migrations. С тех пор этот раздел появляется чуть ли не в каждом Postgresso. В #6 появилась ссылка на серию из 4 статей Петра Перенос данных с Oracle на PostgreSQL:

В разделе

Образование

впервые:

Появилась уже и в бумажном варианте PostgreSQL изнутри — 660 страниц, 29 глав, одно Содержание занимает 11 страниц.

По этому поводу 8-го февраля состоялся прямой эфир на Постгрес-вторнике у Николая Самохвалова и Ильи КосмодемьянскогоПостгрес-вторник с Егором Роговым. Внутри PostgreSQL.

Состояние на конец декабря 2022:

Переведена на английский 4-я часть PostgreSQL 14 internals Егора Рогова. Осталось перевести 5-ю часть и Заключение. Постоянная ссылка здесь. Русский вариант — PostgreSQL 14 изнутри — здесь. 

UPD: свежайшее: новогодний привет от автора. Прямо сегодня:

Чтобы нашим читателям было чем заняться на новогодних праздниках, я выложил PostgreSQL 15 изнутри. В новом издании учтены замечания читателей и исправлены опечатки, а также отражены изменения, произошедшие в версии PostgreSQL 15. Бумажная версия выйдет в новом году.

МАРТ

Postgresso 3 (40)

Feature Freeze

7 апреля произошла Заморозка функциональности. Главными достижениями недавнего времени стали 2 серии мощных патчей, добавляющие важную функциональность и ещё плотней приближающие к стандартам SQL — SQL/JSON и MERGE.

SQL/JSON patches committed to PostgreSQL 15!

Увы, радость преждевременная. В #8-9 мы читаем:

Патчи отложили на PostgreSQL 16 (можно смотреть всю ветку, дискуссия здесь. Чтобы корректно откатить SQL/JSON, пришлось изменить 60 (!) файлов. Среди того, что откатили:

Ну а в #10 мы узнаём вот что:

Postgres Pro Standard 15.0.1 — они возвращаются

Об этом релизе расскажем подробнее — там важные изменения. И важнейшее из отличий — SQL/JSON: в версию Pro вошли патчи, которые откатили в PostgreSQL 15. А их очень много, и есть существенные. В документации новшества, связанные с SQL/JSON, вошли в раздел 9.16. Функции и операторы JSON. Теперь всё о (SQL) JSON(B) сгруппировано в документации так:

9.16.1. Обработка и создание данных JSON
9.16.2. Язык путей SQL/JSON
9.16.3. Функции и выражения SQL/JSON
9.16.4. JSON_TABLE (это точно last-but-not-least).

UPD: коллеги подсказывают, есть свежие новости: Амит Лангот (Amit Langote, EDB, автор секционирования) взялся довести эти патчи до 16-й версии. И не на пустом месте. Основную проблему (обработка ошибок вызова функций без использования вложенных транзакций) уже решил и закоммитил Том Лейн (Tom Lane). Как и еще десяток последующих патчей.

Возвращаемся, однако, в март:

Merge: До появления MERGE INTO при миграции с Oracle в Postgres его приходилось имитировать. Например, в статье Из Oracle да в Postgres есть пример с RETURNING и WITH.

В 2015, в PostgreSQL 10, появился INSERT … ON CONFLICT DO … В 2018 в PoostgreSQL 11 чуть не попал MERGE. Но из-за проблем патч откатили обратно. Это была настоящая драма, почти детективная история, и мы об этом писали в Битва при MERGE. Хроника с выводами и моралью. Бился за MERGE глава компании 2ndQuadrant (теперь в EDB) Саймон Риггс (Simon Riggs), он настойчиво и изобретательно, всеми силами пытался протащить в версию патч, реализующий синтаксис команды MERGE. И вот — свершилось! Документация прилагается.

АПРЕЛЬ

Postgresso 4 (41)

PG, AI и ML

Тема, которая не скоро устареет.

RelationalAI Raises $122M to Redefine How Intelligent Data Apps Are Built

PostgresML

Между прочим, старушка Vertica, запущенная ещё Майклом Стоунбрейкером в 2005-м, теперь позиционируется как платформа продвинутой аналитики и машинного обучения.

МАЙ

Postgresso 5 (42)

В этом номере много о конференциях:

PGConf.Russia 2022

HighLoad++ Foundation 2022

PGCon 2022 (Канада)

В разделе

POC-DB, превью-релизы и игрушечные DB:

Introducing AlloyDB for PostgreSQL

и далее в этом разделе впервые появляется Neon:

SELECT ’Hello, World’. Serverless Postgres built for the cloud

Напомним: Сооснователи компании — Хайкки Линнакангас (Heikki Linnakangas, коммитер PostgreSQL) и Стас Кельвич, в команде много знакомых фамилий российских разработчиков. Из западных звёзд — Питер Гейган (Peter Geoghegan, основной (major) контрибьютор и коммитер PostgreSQL).

С тех пор Neon не раз появлялась в Postgresso. Но майское описание осталось самым подробным. Последнее в 2022 не далее, как в #11:

Neon’s Serverless Postgres Platform Now Live to All

Проект, о котором мы относительно подробно писали в Postgresso #5 (42), продолжает развиваться. Приглашения теперь больше не нужны, можно регистрироваться просто с экаунтом Google или Githab.

Ну и на закуску в том же разделе: Let’s build a distributed Postgres proof of concept и toyDB.

ИЮНЬ

Postgresso 6 (43)

Postgres.FM и Postgres.TV, четверги и вторники

Slow queries and slow transactions 5-го июля Николай Самохвалов (Postgres.ai) и Майкл Кристофайдис (Michael Christofides, основатель pgMustard) представили свой аудио-проект Postgres FM (дополняющий Postgres TV Николая и Ильи Космодемьянского)

Да, неравнодушны мы к журфиксам, к дням недели. Позже от вторников и четвергов перешли к ПятнЕцам:

Живёт своей жизнью затея Райана Буза (Ryan Booz) — его Пятнецы (PGSQL-Phridays). На 3-м этапе этого флеш-моба ход Пэта Райта (Pat Wright). В отличие от обычных пятниц и PG-пятнец Шона Томаса, PGSQL-пятнецы случаются раз в месяц, и эта, 3-я пятнеца — последняя в 2022-м.

Но это уже #11 — забежали вперёд. Почитайте сначала #6 — он вышел объёмный и разнообразный.

И опять UPD: Генриетта Домбровская не согласилась с тем, что эта пятнеца последняя и уже запустила 4-ю фазу флешмоба: PGSQL Phriday #004: PostgreSQL and Software Development.

ИЮЛЬ

Postgresso 7 (44)

PostgreSQL 16: Часть 1 или Коммитфест 2022-07 Еще не вышла официально 15-я версия, но уже закончился первый коммитфест 16-й. И мы можем посмотреть на самые интересные изменения — пишет автор обзора Павел Лузанов.

Советуем за обзорами коммитфестов Павла Лузанова следить всегда. И показывать англоговорящим коллегам английские версии (они довольно оперативно переводятся, спасибо Александру Мелешко). Итак, по Postgres 16:

  • Коммитфест 2022-07 (ru / en),

  • Коммитфест 2022-09 (ru / en),

  • Коммитфест 2022-11 (ru / en).

Хаос полезный и вредный

В эту рубрику попали:

How To Corrupt Your PostgreSQL Database

Chaos testing of a Postgres cluster managed by the Zalando Postgres Operator

PostgreSQL JDBC versions 42.4.1/42.2.26 Security Update (необычная дыра в безопасности)

и ChaosDB, который вообще не DB. А что это? Вот и почитайте, если ещё не читали.

СЕНТЯБРЬ-ОКТЯБРЬ

Postgresso 8-9 (45-46)

PostgreSQL 15 Released!

Официальный выход 15-й, до этого была бесконечная серия Beta и RC, в которых, впрочем, и происходили важнейшие события (отмены — см. выше, #3).

В этом сдвоенном номере случился огромный (и, смеем надеяться, полезный и интересный) раздел

Образование

— и в нём ссылки на классиков:

Architecture of a Database System,

The Design Of Postgres,

Database System Concepts,

плейлисты CMU Group с многочисленными лекциями,

серия Foundations and Trends in Databases.

И о парочке Pagila / Sakila, и приглашение поиграть в Learn Postgres at the Playground, и присоединиться к PostgresPro EDU.

Открытия/Закрытия

— эта рубрика существует пока только в #8-9:

Distributed Postgres goes full open source with Citus: why, what & how (открытие)

Removal of Heroku free product plans (закрытие)

А жаль, пожалуй. Можно было в неё же подгрузить вот эту новость из #6:

Citus 11 for Postgres goes fully open source, with query from any node

А вот раздел

Мониторинг и тюнинг

не уникален, но там немало информации для интересующихся.

Postgres-зверушки

Здесь собралась уже целая коллекция биографий зверушек, помогавших разрабатывать Postgres. Недавние [то есть на август-сентябрь]:

Холли (курица), Эмба (пёс), Milo (котик). Статистически наибольший вклад в Postgres внесли собаки.

В сентябре этот выдающийся проект по каким-то причинам замёрз.

ОКТЯБРЬ

Postgresso 10 (47)

Postgres Pro Standard 15.0.1 — они возвращаются

Они — это те самые функции SQL/JSON.

Но, оказывается, придумали ещё и BSON:

How FerretDB stores BSON in JSONB

Логическая репликация: параллельное чтение Капилы и Лузанова

— наша экспериментальная рубрика, можно сказать. Читать предлагалось:

Logical Replication Improvements in PostgreSQL-15 Амита Капилы (Amit Kapila) и обзоры коммитфестов Павла Лузанова (2021-072021-09, 2021-11), на чтении которых мы настаивали выше. Цель такого параллельного чтения — наглядно показать читателю, зачем такие обзоры делаются, в чём не пересекаются с тематическими статьями, а в чём — выходит — пересекаются.

Нечёткий поиск

Ищем имена с опечатками в PostgreSQL

PGroonga 2.4.1

NULL, изменчивый и (почти) непредсказуемый

— тоже рубрика уникальней некуда.

NULL-значения в PostgreSQL: правила и исключения

Алексей Борщев преобразовал свой доклад на PgConf.Russia 2022 в статью. На мой вкус это был один из самых увлекательных докладов (может, самый). Пересказывать не буду: читайте, смотрите, если можете (слайды доклада доступны всем, а видео тем, у кого есть личный кабинет на конференции).

А 4 ноября Чарли Батиста (Charly Batista, Percona) опубликовал статью: PostgreSQL: Are All NULLs the Same?

Железо

— о нём пишем не часто (может и зря)

Сравнение процессора Байкал-S и HiSilicon Kunpeng 920

НОЯБРЬ

Postgresso 11 (48)

Что для вас PostgreSQL-комьюнити?

опрос, который устроил Пэт Райт (Pat Wright) в финале флешмоба Райана Буза (Ryan Booz).

NoSQL: Малыш Ахиллес пока не догнал Большую Черепаху

— то есть пока что не догнал и в ближайшие годы не догонит «классичесские» СУБД.

NoSQL Databases Gain Usability, Speed

Cassandra to Get ACID Transactions via New Accord Consensus Protocol

Databricks SQL Now GA, Bringing Traditional BI to the Lakehouse

Отступники

— в данном случае это Instacart

Instacart on Dropping Postgres for Amazon DynamoDB

Компания Instacart перешла с Postgres на DynamoDB. Разобраться в причинах этого неприятного перехода было бы полезно.

Добавим уж ссылку, не попавшую в предыдущие наши выпуски, будем считать, что это в Декабрьском:

I Migrated from a Postgres Cluster to Distributed SQLite with LiteFS

А вот и …

ДЕКАБРЬ

Это уже почти рекурсия. В этом номере мы решили напомнить о том, что публиковали за год. Какие-то из событий/статей декабря 2022 попадут уже в январский номер.

Всё же: не одни мы подводим итоги 2022. Вот и

Postgres Weekly Issue 486: : The Best of Postgres Weekly in 2022

А вот Кирилл Боровиков aka Kilor:

PostgreSQL в «Тензоре» — публикации за год

Пожалуй, всё: не смеем отвлекать от приготовлений к встрече Нового Года.


С наступающим 2023 годом! Да будет он лучше нынешнего!


ссылка на оригинал статьи https://habr.com/ru/company/postgrespro/blog/707670/

Интеграция с ЕСИА для .Net: запутаннее, чем кажется

Предисловие

Передо мной стояла задача по интеграции нашего сервиса с госуслугами. Казалось ничего сложного не предстоит, но учитывая что наш сервис базируется на технологии ASP.NET всё было не так оптимистично. В начале были поиски.. много поисков, которые привели к множеству разрозненной и чаще всего неактуальной информации. Так же были найдены уже готовые решения, но как заявляли некоторые товарищи на форумах за такое могут и по головке погладить. Поэтому было решено писать самому.

Эта статья скорее больше актуализация и дополнение информации из этой статьи.

Введение

На сайте Минцифр есть методичка максимально раздутая и очень запутанная, но пользоваться её нам всё равно придётся. Мы будем работать с ЕСИА версии 3.11 (актуальная на момент написания статьи). Кратко наши действия заключаются вот в чем:

  1. Регистрация ИС в регистре информационных систем ЕСИА

  2. Регистрация ИС в тестовой среде

  3. Выполнение доработки системы для взаимодействия с ЕСИА

Звучит довольно просто, но каждый шаг целая отдельная история приключений. Регистрация ИС в ЕСИА приключение для бюрократа. Поэтому в этой статье мы немного посмотрим на второй шаг, и детально распишем реализацию.

Содержание

Всё необходимое

КриптоПРО CSP + КриптоПРО .Net + КриптоПРО .NetSDK. Всё это можно скачать с офф. сайта КриптоПРО. На время разработки лучше использовать триал версию.

Наш инвентарь для путешествия:

  • КриптоПРО CSP

  • КриптоПРО .Net

  • КриптоПРО .NetSDK

  • Контейнер закрытого ключа с сертификатом нашей организации

  • Много терпения

Немного о КриптоПРО CSP + .Net Core 5+

Вот тут и начинаются первые проблемы. На момент написания статьи у КриптоПРО .Net нет поддержки .Net Core 5 и выше. Есть сборка под .Net Core 3.1 но и она выглядит сомнительно. Поэтому было решено поднять сервис для .Net Framework 4.8 который будет использовать средства КриптоПРО CSP для подписания с использованием экспортировать контейнер с такого токена запрещено ФНС. Поэтому необходимо заранее получить токен на имя сотрудника с экспортируемым контейнером. Так как его необходимо будет скопировать на сервер.

Приступаем

Начнём с того, что вы уже отправили заявку регистрации ИС в ЕСИА и её приняли. А так же отправили заявка на тестовую среду. Приступим к этапу настройки ИС в тестовом кабинете электронного правительства. Вот ссылка на тестовую страницу. Логинимся под тестовой учетной записью тестового пользователя 006(все данные лежат в приложении к работе с тестовой средой), так как он имеет доступ к управлением ИС.

Кабинет тестовой среды - Технологический порталйт
Кабинет тестовой среды — Технологический порталйт

Здесь ищем нашу систему по Мнемонике или полному названию, если таковой нет то создаём. Напротив нашей системы есть две кнопки:
Первая кнопка — изменить нашу ИС (информация о ИС, редиректы и тд)
Вторая кнопка — наши сертификаты с помощью которых мы подписываем сообщения в ЕСИА

Настройка ИС

Есть важный момент в настройки ИС. Это URL системы. Тут мы указываем ссылки куда ЕСИА может делать переадресацию при запросе от нашей ИС. На эти точки будет приходить авторизационный код (Если он указан в запросе).

Сертификаты ИС

Здесь мы можем загрузить наш сертификаты или же удалить их. Есть один важный момент, каждая ИС может иметь только один уникальный сертификат. А связи с тем, что на тестовой среде все системы регистрируются под одним пользователем и сертификаты тестовые одни на всех часта такая ситуация, что кто-то удаляет у вас сертификат и загружает к себе. А ваши запросы теперь падают с ошибкой) Но если у вас уже готов ЭЦП на сотудника, то лучше используйте её.

Реализуем

Мы закончили с настройки нашей ИС и можем приступить к реализации. Надеюсь вы уже установили КриптоПРО и всё необходимое для него. Если нет, я подожду…

Устанавливаем сертификаты

Такс~ Всё готово. Качаем сертификаты по ссылке из методички. Специально не буду вставлять, так как может измениться.

Здесь нам интересен сертификат ТЕСИА ГОСТ 2012.cer — это сертификат с помощью которого ЕСИА подписывает сообщения отправляя в нашу ИС. (Соответственно для продуктовой среды свой сертификат). Устанавливаем сертификат как доверенный. Здесь ничего сложного думаю разберётесь.

Теперь устанавливаем тестовый контейнер и сертификат. Для примера будем использовать предоставленные ЕСИА контейнеры, но вы можете использовать свои. Всё это лежит внутри архива.

Сам архив со всеми тестовыми контейнерами
Сам архив со всеми тестовыми контейнерами
Мы возьмём именно 006 так как на него зарегистрирована наша ИС
Мы возьмём именно 006 так как на него зарегистрирована наша ИС

В архиве лежит папка d1f73ca5.000 — это контейнер нам необходимо его переместить по пути C:\Users\User\AppData\Local\Crypto Pro

Теперь открываем КриптоПРО CSP. Выбираем установить личный сертификат и указываем Тестовое ведомство Фамилия006 ИО.cer и нажимаем найти автоматически. Выполняем оставшиеся шаги сами.

Механизм подписания

Пожалуй начинается самая важная и самая запутанная часть всего пути. Здесь мы реализуем сервис для работы с подписью. И так делаю выжимку из методических материалов, чтобы Вам не пришлось читать много текста.

Для получения авторизационный ссылки — ссылка на которую мы будем переадресовывать пользователя для авторизации в ЕСИА. Нам необходимо собрать ссылку из параметров.

  1. client_id — наша Мнемоника

  2. client_secret — Отсоединённая подпись от параметров запроса в кодировке UTF-8

  3. redirect_uri — ссылка на которую ЕСИА будет переадресовывать пользователя вместе с авторизационным кодом

  4. scope — перечень запрашиваемой информации. Например fullname birthdate gender

  5. response_type — тип ответа от ЕСИА, в нашем случае это просто строчка code

  6. state — Идентификатор текущего запроса. Генерируется таким образом Guid.NewGuid().ToString("D");

  7. timestamp — время запроса авторизационного кода в формате yyyy.MM.dd HH:mm:ss Z. Генерируется таким образом DateTime.UtcNow.ToString("yyyy.MM.dd HH:mm:ss +0000");

  8. client_certificate_hash — это fingerprint сертификата в HEX-формате.

Обозначили наш зоопарк. Самый важный зверь здесь client_secret

Получаем client_certificate_hash

В методическом указании от Минцифр есть ссылка на специальную утилиту с помощью которой мы можем получить этот хэш. Разархивировали архив и видем перед нами sh. Windows пользователи не пугаемся, на самом деле тут же лежит .exe файл. Чтобы вычислить хэш нашего сертификат просто необходимо из cmd запустить вот такой скрипт:

cpverify.exe test.cer -mk -alg GR3411_2012_256 -inverted_halfbytes 0

Формирование client_secret

Такс перед тем как просто получит client_secret нам необходимо сделать:

  • ASP.Net Framework 4.8 WebAPI — тот самый сервис который будет работать с КриптоПРО CSP

Пропустим множество шагов создания этого сервиса и перейдём сразу к его настройки для работы с КриптоПРО CSP.

Настройка сервиса для работы с КриптоПРО CSP

Добавляем ссылки на DLL КриптоПРО.
Переходим по пути C:\Program Files (x86)\Crypto Pro.NET SDK\Assemblies\4.0
Выбираем всё что нам нужно. (подробная информация)

Теперь мы имеем доступ к API КриптоПРО CSP из кода .Net Framework

Теперь создаём контроллер:

Код контроллера

Итак нам необходимо получать строку для подписания. Создадим метод

const string CertSerialNumber = "01f290e7008caed0904b967783fd0e4ad6"; const string EsiaCertSerialNumber = "0125657e00a1ae59804d92116214e53466";  [HttpGet] public string Get(string msg) {     msg = Base64UrlEncoder.Decode(msg);      var data = Encoding.UTF8.GetBytes(msg);      var client_secret = Sign(data);      return client_secret; }

Мы заранее укажем константами серийные номера сертификатов.
В методе Get получаем строку в Base64Url формате, чтобы спокойно передавать наши длинные сообщения.
Декодируем строку из Base64Url в текст. После чего переводим текст в байты используя UTF-8. А теперь подписываем.

string Sign(byte[] data) {     var gost3411 = new Gost3411_2012_256CryptoServiceProvider();     var hashValue = gost3411.ComputeHash(data);     gost3411.Clear();     var signerCert = GetSignerCert();     var SignedHashValue = GostSignHash(hashValue,         signerCert.PrivateKey as Gost3410_2012_256CryptoServiceProvider, "Gost3411_2012_256");     var client_secret = Base64UrlEncoder.Encode(SignedHashValue);      return client_secret; }

И так что мы тут делаем. С помощью ГОСТ 34.11-2012 мы вычисляем хэш нашего сообщения. И используя полученный сертификат подписываем сообщение.

X509Certificate2 GetSignerCert() {     var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);     store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);     var certificates = store.Certificates.Find(X509FindType.FindBySerialNumber, CertSerialNumber, false);      if (certificates.Count != 1)     {         return null;     }      var certificate = certificates[0];      if (certificate.PrivateKey == null)     {         return null;     }      return certificate; }

Здесь мы открываем наш склад с контейнерами и ищем именно тот где лежит наш сертификат. После чего извлекаем из него сертификат.

byte[] GostSignHash(byte[] HashToSign, Gost3410_2012_256CryptoServiceProvider key, string HashAlg) {     try     {         //Создаем форматтер подписи с закрытым ключом из переданного          //функции криптопровайдера.         var Formatter = new Gost2012_256SignatureFormatter(             (Gost3410_2012_256CryptoServiceProvider) key);          //Устанавливаем хэш-алгоритм.         Formatter.SetHashAlgorithm(HashAlg);          //Создаем подпись для HashValue и возвращаем ее.         return Formatter.CreateSignature(HashToSign);     }     catch (CryptographicException e)     {         Console.WriteLine(e.Message);         return null;     } }

С помощью этого кода как раз и создаётся наша подпись на хэш строки. Здесь используется ГОСТ 34.10-2012.

Итак контроллер готов. Теперь переходим в наш основной проект на .Net Core

Создаём строку подписания. Просто выполняем конкатенацию параметры без разделителей. Здесь я использую IOptions чтобы брать параметры из appsettings.json.

var msg = $"{esiaSettings.Value.ClientId}{esiaSettings.Value.Scope}{timestamp}{state}{redirectUri}";

Мы получил строку для подписания. Теперь нам необходимо эту строку закодировать в Base64Url и отправляем её на подписание в написанный нами заранее сервис

private string GetClientSecret(string msg){   var client = new HttpClient();    var msgBase64 = Base64UrlEncoder.Encode(msg);      var response = await client.GetAsync($"{cryptoProSettings.Value.BaseUrl}/Get?msg={msgBase64}");      var clientSecret = await response.Content.ReadAsStringAsync();      clientSecret = JsonConvert.DeserializeObject<string>(clientSecret);      return clientSecret; }

Собираем ссылку для авторизации в Госуслугах

Наконец-то мы получили этот долгожданный секрет. Но вы могли бы подумать это всё, дальше всё просто и ясно. Не тут то было! Дело в том, что ЕСИА требует Base64 Url Safe кодироку. И она немного отличается от Base64Url кодировки доступной из коробки .Net
Итак дело за малым, собираем нашего гомункула из секрета и параметров.

Класс помощник для сборки ссылки

Возможно излишне, но мне понравился метод сбора вот таким способом.

public class RequestBuilder {     List<RequesItemClass> items = new List<RequesItemClass>();     public void AddParam(string name, string value)     {         items.Add(new RequesItemClass { name = name, value = value });     }     public override string ToString()     {         return string.Join("&", items.Select(a => a.name + "=" + a.value));     } }  public class RequesItemClass {     public string name;     public string value; }

Код сборки ссылки
async Task<string> UrlBuild(string redirectUri) {     using var client = new HttpClient();      var timestamp = DateTime.UtcNow.ToString("yyyy.MM.dd HH:mm:ss +0000");     var state = Guid.NewGuid().ToString("D");      var msg = $"{esiaSettings.Value.ClientId}{esiaSettings.Value.Scope}{timestamp}{state}{redirectUri}";      var clientSecret = await GetClientSecret(msg);      var builder = new RequestBuilder();     builder.AddParam("client_secret", clientSecret);     builder.AddParam("client_id", esiaSettings.Value.ClientId);     builder.AddParam("scope", esiaSettings.Value.Scope);     builder.AddParam("timestamp", timestamp);     builder.AddParam("state", state);     builder.AddParam("redirect_uri", redirectUri);     builder.AddParam("client_certificate_hash", esiaSettings.Value.ClientCertificateHash);     builder.AddParam("response_type", "code");     builder.AddParam("access_type", "online");      //Вот тут самый важный момент на который было потрачено множество времени. Просто заменяем символы на безопасные     var url = esiaSettings.Value.EsiaAuthUrl + "?" + builder.ToString().Replace("+", "%2B")         .Replace(":", "%3A")         .Replace(" ", "+");      return url; }

Получаем ссылку на подобии вот такой:
Здесь https://esia-portal1.test.gosuslugi.ru/aas/oauth2/v2/ac ссылка на конечную точку получения авторизационно кода, указана в методическом материале.

https://esia-portal1.test.gosuslugi.ru/aas/oauth2/v2/ac?client_secret=v_c33_-LpkyKJbopTEYqBMbGZrBy9r9u1pzbRmMLNlJPcBnPTJj6Xx5DuxXba3EZZoXdMsb0YIwPDCoF0dfYjQ&client_id=MEMONIKA&scope=fullname+birthdate+gender&timestamp=2022.12.23+16%3A37%3A45+%2B0000&state=3a19c4d7-594b-496f-aa6e-970c75a925a4&redirect_uri=https%3A//api.site/users/esia&client_certificate_hash=EED1079A4FF154E117EAA196DCB551930807825DE1DE15EAF7607F354BA47423&response_type=code&access_type=online

Теперь перенаправляем пользователя по этой ссылке и ожидаем пока он авторизуется. После авторизации ЕСИА переадресует его на нашу ссылку и отправит туда в виде аргументов авторизационный код и state.

Получение токена доступа

Теперь время получить токен взамен на авторизационный код.

Метод для получение токена
public async Task<EsiaAuthToken> GetToken(string authorizationCode, string redirectUrl) {     var timestamp = DateTime.UtcNow.ToString("yyyy.MM.dd HH:mm:ss +0000");     var state = Guid.NewGuid().ToString("D");      var msg =         $"{esiaSettings.Value.ClientId}{esiaSettings.Value.Scope}{timestamp}{state}{redirectUrl}{authorizationCode}";      var clientSecret = await GetClientSecret(msg);      var requestParams = new List<KeyValuePair<string, string>>     {         new KeyValuePair<string, string>("client_id", esiaSettings.Value.ClientId),         new KeyValuePair<string, string>("code", authorizationCode), //Здесь мы передаём полученный код         new KeyValuePair<string, string>("grant_type", "authorization_code"),  //Просто указываем тип         new KeyValuePair<string, string>("state", state),         new KeyValuePair<string, string>("scope", esiaSettings.Value.Scope),         new KeyValuePair<string, string>("timestamp", timestamp),         new KeyValuePair<string, string>("token_type", "Bearer"),  //Какой токен мы хотим получить         new KeyValuePair<string, string>("client_secret", clientSecret),         new KeyValuePair<string, string>("redirect_uri", redirectUrl),         new KeyValuePair<string, string>("client_certificate_hash", esiaSettings.Value.ClientCertificateHash)     };      using var client = new HttpClient();     using var response = await client.PostAsync(esiaSettings.Value.EsiaTokenUrl,         new FormUrlEncodedContent(requestParams));     response.EnsureSuccessStatusCode();     var tokenResponse = await response.Content.ReadAsStringAsync();      var token = JsonConvert.DeserializeObject<EsiaAuthToken>(tokenResponse);      if (!await ValidatingAccessToken(token))     {         throw new Exception("Ошибка проверки маркера индентификации");     }      return token; }

Тут всё простенько, снова генерируем client_secret указываем остальные параметры и отправляем запрос в ЕСИА на получение токена. Тестовый Uri https://esia-portal1.test.gosuslugi.ru/aas/oauth2/v3/te

Класс токена
public class EsiaAuthToken {     /// <summary>     /// Токен доступа     /// </summary>     [JsonProperty("access_token")]     public string AccessToken { get; set; }      /// <summary>     /// Идентификатор запроса     /// </summary>     public string State { get; set; }      string[] parts => AccessToken.Split('.');      /// <summary>     /// Хранилище данных в токене     /// </summary>     public EsiaAuthTokenPayload Payload     {         get         {             if (string.IsNullOrEmpty(AccessToken))             {                 return null;             }              if (parts.Length < 2)             {                 throw new Exception($"При расшифровке токена доступа произошла ошибка. Токен: {AccessToken}");             }              var payload = Encoding.UTF8.GetString(Base64UrlEncoder.DecodeBytes(parts[1]));             return JsonConvert.DeserializeObject<EsiaAuthTokenPayload>(payload);         }     }      /// <summary>     /// Сообщение для проверки подписи     /// </summary>     [Newtonsoft.Json.JsonIgnore]     public string Message     {         get         {             if (string.IsNullOrEmpty(AccessToken))             {                 return null;             }              if (parts.Length < 2)             {                 throw new Exception($"При расшифровке токена доступа произошла ошибка. Токен: {AccessToken}");             }              return parts[0] + "." + parts[1];         }     }      /// <summary>     /// Сигнатура подписи     /// </summary>     [Newtonsoft.Json.JsonIgnore]     public string Signature     {         get         {             if (string.IsNullOrEmpty(AccessToken))             {                 return null;             }              if (parts.Length < 2)             {                 throw new Exception($"При расшифровке токена доступа произошла ошибка. Токен: {AccessToken}");             }              return parts[2];         }     }      public class EsiaAuthTokenPayload     {         [JsonConstructor]         public EsiaAuthTokenPayload(string tokenId, string userId, string nbf, string exp, string iat, string iss,             string client_id)         {             TokenId = tokenId;             UserId = userId;             BeginDate = EsiaHelper.DateFromUnixSeconds(double.Parse(nbf));             ExpireDate = EsiaHelper.DateFromUnixSeconds(double.Parse(exp));             CreateDate = EsiaHelper.DateFromUnixSeconds(double.Parse(iat));             Iss = iss;             ClientId = client_id;         }          /// <summary>         /// Идентификатор токена         /// </summary>         [JsonProperty("urn:esia:sid")]         public string TokenId { get; private set; }          /// <summary>         /// Идентификатор пользователя         /// </summary>         [JsonProperty("urn:esia:sbj_id")]         public string UserId { get; private set; }          /// <summary>         /// Время начала действия токена         /// </summary>         [JsonPropertyName("nbf")]         public DateTime BeginDate { get; private set; }          /// <summary>         /// Время окончания действия токена         /// </summary>         [JsonPropertyName("exp")]         public DateTime ExpireDate { get; private set; }          /// <summary>         /// Время выпуска токена         /// </summary>         [JsonPropertyName("iat")]         public DateTime CreateDate { get; private set; }          /// <summary>         /// Организация, выпустившая маркер         /// </summary>         [JsonPropertyName("iss")]         public string Iss { get; private set; }          /// <summary>         /// Адресат маркера         /// </summary>         [JsonPropertyName("client_id")]         public string ClientId { get; private set; }     }   }    public static class EsiaHelper   {       public static DateTime DateFromUnixSeconds(double seconds)       {           var date = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);            return date.AddSeconds(seconds).ToLocalTime();       }   }

Проверка токена

Итак помимо того, что нам нужно получить токен, нам так же необходимо проверить его.

Сам токен состоит из 3 частей.
1 часть — заголовок JWT токена
2 часть — payload токена, там вся основная информация о токене
3 часть — RAW подпись в формате UTF-8

Код конечной точки для проверки подписи
[HttpPost] public bool Verify(VerifyMessage message) {     try     {         return VerifyRawSignString(message.Message, message.Signature);     }     catch (Exception ex)     {         return false;     } }  public class VerifyMessage {     public string Signature { get; set; }     public string Message { get; set; } }

Код проверки подписи на нашем сервисе
/// <summary> /// Проверка подписи JWT в формате HEADER.PAYLOAD.SIGNATURE. /// </summary> /// <param name="message">HEADER.PAYLOAD в формате Base64url</param> /// <param name="signature">SIGNATURE в формате Base64url</param> bool VerifyRawSignString(string message, string signature) {     var signerCert = GetEsiaSignerCert();      var messageBytes = Encoding.UTF8.GetBytes(message);     var signatureBytes = Base64UrlEncoder.DecodeBytes(signature);     //Переварачиваем байты, так как используется RAW подпись     Array.Reverse(signatureBytes, 0, signatureBytes.Length);      using (var GostHash = new Gost3411_2012_256CryptoServiceProvider())     {         var csp = (Gost3410_2012_256CryptoServiceProvider) signerCert.PublicKey.Key;         //Используем публичный ключ сертификата для проверки           return csp.VerifyData(messageBytes, GostHash, signatureBytes);     } }

Код получения сертификата ЕСИА
X509Certificate2 GetEsiaSignerCert() {     var store = new X509Store(StoreName.AddressBook, StoreLocation.CurrentUser);     store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);     var certificates = store.Certificates.Find(X509FindType.FindBySerialNumber, EsiaCertSerialNumber, false);      var certificate = certificates[0];      return certificate; }

Здесь используем введённые ранее константы. И Получаем сертификат из доверенных сертификатов.

Отправка токена на проверку
public async Task<bool> ValidatingAccessToken(EsiaAuthToken token) {     if (token.Payload.ExpireDate <= DateTime.Now ||         token.Payload.BeginDate >= DateTime.Now ||         token.Payload.CreateDate >= DateTime.Now ||         token.Payload.ExpireDate <= token.Payload.BeginDate ||         token.Payload.CreateDate > token.Payload.BeginDate ||         token.Payload.CreateDate > token.Payload.ExpireDate ||         token.Payload.Iss != esiaSettings.Value.ISS ||         token.Payload.ClientId != esiaSettings.Value.ClientId)     {         return false;     }      var client = new HttpClient();      var requestParams = new List<KeyValuePair<string, string>>     {         new KeyValuePair<string, string>("signature", token.Signature),         new KeyValuePair<string, string>("message", token.Message)     };      var response = await client.PostAsync($"{cryptoProSettings.Value.BaseUrl}/Verify",         new FormUrlEncodedContent(requestParams));      response.EnsureSuccessStatusCode();     var resultResponse = await response.Content.ReadAsStringAsync();      var result = JsonConvert.DeserializeObject<bool>(resultResponse);      return result; }

Этот код используем в нашем основном сервисе.
Проверяем поля токена на актуальность, чтобы его не могли подделать. А потом уже проверяем подпись токена, как указано в методических указаниях.

Получение данных пользователя из ЕСИА

Имея токен мы может отправить запрос на получение данных о пользователе указанных в scope токена. Пример кода, где мы получаем данные пользователя. Здесь esiaUserId содержится в самом токене, это уникальный идентификатор пользователя ЕСИА. Наш токен указываем в заголовке авторизации.

public async Task<EsiaUser> ExecuteAsync(string esiaUserId, string accessToken) {     using (var client = new HttpClient())     {         client.DefaultRequestHeaders.Clear();         client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);          var response = await client.GetStringAsync($"{esiaSettings.Value.EsiaRestUrl}/prns/{esiaUserId}");         var user = JsonConvert.DeserializeObject<EsiaUser>(response);         user.Id = user.Id ?? esiaUserId;          return user;     } }
Код класса EsiaUser
public class EsiaUser {     /// <summary>     /// Идентификатор     /// </summary>     [JsonProperty("oid")]     public string Id { get; set; }      /// <summary>     /// Фамилия     /// </summary>     [JsonProperty("firstName")]     public string FirstName { get; set; }      /// <summary>     /// Имя     /// </summary>     [JsonProperty("lastName")]     public string LastName { get; set; }      /// <summary>     /// Отчество     /// </summary>     [JsonProperty("middleName")]     public string MiddleName { get; set; }      /// <summary>     /// Дата рождения     /// </summary>     [JsonProperty("birthdate")]     public string Birthdate { get; set; }      /// <summary>     /// Пол     /// </summary>     [JsonProperty("gender")]     public string Gender { get; set; }      /// <summary>     /// Подтвержден ли пользователь     /// </summary>     [JsonProperty("trusted")]     public bool Trusted { get; set; } }

Заключение

Наконец мы закончили интеграцию с ЕСИА. Это был длинный путь полный странных вещей. Неясных решений и множество потраченного времени. Надеюсь этой статьёй я помог Вам реализовать задачу интеграции гораздо быстрее и легче. Спасибо за потраченное время.


ссылка на оригинал статьи https://habr.com/ru/post/708774/