Весной 2024 года российский производитель оборудования и ПО для ИТ-инфраструктуры и информационной безопасности «Инферит» выпустил новую версию операционной системы «МСВСфера» 9 на базе ядра Linux. В продуктовом портфеле вендора две редакции: ОС «МСВСфера АРМ» 9, разработанная для рабочих станций, и версия ОС «МСВСфера Сервер» 9 для серверной инфраструктуры.
Наша миссия — сделать использование компьютера простым, безопасным и приятным для каждого пользователя. Интуитивно понятный интерфейс и широкий спектр функций российской операционной системы «МСВСфера АРМ» 9 позволяет легко и эффективно решать задачи любой сложности, учиться, работать и отдыхать.
В этой статье мы хотим рассказать о том, как интегрировали сервисы Яндекса в ОС «МСВСфера АРМ» 9.
GNOME Online Accounts
GNOME Online Accounts представляет собой удобный инструмент управления учётными записями пользователя в онлайн-сервисах в среде рабочего стола GNOME. Пользователь может подключиться к своим аккаунтам и управлять контактами и календарём, отправлять почту, работать с документами и совершать другие действия прямо в среде рабочего стола.
На момент разработки GNOME Online Accounts не предоставлял доступ к российским сервисам, таким как Яндекс или ВКонтакте. Мы не нашли в российском сегменте подобных доработок, поэтому решили добавить их поддержку. И начали с поддержки аккаунтов Яндекса.
Доработка кода GNOME Online Accounts для поддержки сервисов Яндекса
Для интеграции с сервисами Яндекса решили использовать Yandex Auth через OAuth—токен.
Мы сформировали список поддерживаемых свойств провайдера, адреса авторизации и получения токена, характерных для Яндекса.
Например, идентификатор пользователя извлекается из поля default_email, возвращаемого при запросе страницы https://login.yandex.ru/info.
Давайте теперь посмотрим, как выглядит список сервисов, к которым можно подключиться.
После добавления аккаунта через центр управления, он появляется в списке доступных системе аккаунтов.
[user1]@gmail.com at Google (google) AccessToken: [googletoken] ClientId: [clientid].apps.googleusercontent.com ClientSecret: **** Mail 0x55a1ff539d50 Mail name Mail addr [user1]@gmail.com Mail imap imap.gmail.com Mail imap host [user1]@gmail.com [user2]@yandex.ru at Yandex (yandex) AccessToken: [yandextoken] ClientId: ********** ClientSecret: **** Mail 0x55a1ff56d950 Mail name Mail addr [user2]@yandex.ru Mail imap imap.yandex.ru Mail imap host [user2]@yandex.ru
Интеграция с почтой
В пакет ОС «МСВСфера АРМ» 9 входит почтовый клиент Evolution, который имеет встроенную интеграцию с GNOME Online Accounts — поэтому мы выбрали его для проверки интеграции с почтой. Сразу подключиться к Яндексу не удалось, Evolution не увидел нового провайдера.
Тогда мы решили вручную добавить провайдера Яндекс и для этого доработали пакет evolution-data-server. Теперь Evolution может извлекать из GNOME Online Accounts токен и параметры учётной записи и далее взаимодействовать с необходимыми сервисами, используя функции провайдера.
Для корректной работы почты также оказалось необходимым в Яндекс Почте разрешить получать почту сторонним сервисам по IMAP.
Теперь при подключенной учётной записи Яндекса Evolution получает информацию из GNOME Online Accounts и принимает почту. Никаких дополнительных действий по авторизации предпринимать не нужно.
Интеграция с почтой оказалась довольно простой. И, воодушевлЁнные, мы приступили к следующему этапу — интеграции с календарём.
Интеграция с календарём
Первые сложности возникли с Яндекс Календарём, так как у него есть свои особенности передачи заголовка авторизации. Для проверки работоспособности API Яндекс Календаря мы написали скрипт, который должен прочитать события:
#!/usr/bin/env ruby $LOAD_PATH.unshift File.expand_path("lib", __dir__) require "calendav" u = 'username@yandex.ru' credentials = Calendav::Credentials::Standard.new( host: "https://caldav.yandex.ru/", username: u, password: "**************token***************", authentication: :oauth_token ) client = Calendav.client(credentials) puts "principals", client.principal_url puts "calendars" calendars = client.calendars.list calendars.each do |calendar| puts calendar.url puts calendar.display_name end
Тип аутентификации был выбран как :oauth_token, он формирует OAuth-заголовок:
def authenticated case credentials.authentication when :basic_auth HTTP.basic_auth(user: credentials.username, pass: credentials.password) when :bearer_token HTTP.auth("Bearer #{credentials.password}") when :oauth_token HTTP.auth("OAuth #{credentials.password}")) else raise "Unexpected authentication approach:"\ "#{credentials.authentication}" end end
При установке параметра authentication как :oauth_token в заголовке Authorization передается следующее значение OAuth given_in_password_token. Работа скрипта выглядит так:
[user@gui calnew]$ '/home/user/ruby_code/calnew/test.rb' principals https://caldav.yandex.ru/principals/users/user%40yandex.ru/ calendars https://caldav.yandex.ru/calendars/user%40yandex.ru/events-23342418/ test1 https://caldav.yandex.ru/calendars/user%40yandex.ru/events-23426104/ my1 https://caldav.yandex.ru/calendars/user%40yandex.ru/todos-6109195/ Не забыть
Если заменить authentication на :bearer_token, то получится такой вывод:
[user@gui calnew]$ '/home/user/ruby_code/calnew/test.rb' /home/user/ruby_code/calnew/lib/calendav/error_handler.rb:19:in `call': 401 Unauthorized (Calendav::RequestError)
Так стало понятно, что в Evolution для провайдеров была задана Bearer-авторизация в файле e-soup-auth-bearer.c. Алгоритмы данного типа авторизации подходят для Яндекса за исключением ключевого слова Bearer в заголовке. Так выглядит оригинал функции, которая формирует заголовок:
static gchar * e_soup_auth_bearer_get_authorization (SoupAuth *auth, SoupMessage *message) { ESoupAuthBearer *bearer; gchar *res; bearer = E_SOUP_AUTH_BEARER (auth); g_mutex_lock (&bearer->priv->property_lock); res = g_strdup_printf ("Bearer %s", bearer->priv->access_token); g_mutex_unlock (&bearer->priv->property_lock); return res; }
Строка
res=g_strdup_printf("Bearer %s", bearer→priv→access_token);
формирует заголовок с ключевым словом Bearer, а нужно с OAuth. Поэтому во избежание копирования всего файла мы сделали небольшую правку, которая позволила задавать специфический ключ для заголовка авторизации.
Новый код выглядит так:
static gchar * e_soup_auth_bearer_get_authorization (SoupAuth *auth, SoupMessage *message) { ESoupAuthBearer *bearer; gchar *res; bearer = E_SOUP_AUTH_BEARER (auth); g_mutex_lock (&bearer->priv->property_lock); if (!bearer->priv->is_custom_bearer[0]) res = g_strdup_printf ("Bearer %s", bearer->priv->access_token); else res = g_strdup_printf ("%s %s", bearer->priv->is_custom_bearer, bearer->priv->access_token); g_mutex_unlock (&bearer->priv->property_lock); return res; }
Также была добавлена проверка на то, что Bearer является настраиваемым и вставка собственного заголовка авторизации вместо Bearer:
res=g_strdup_printf("%s %s, bearer→priv→is_custom_bearer, bearer→priv→access_token);
Теперь календарь работает.
Также Яндекс Календарь стал доступным из окружения GNOME.
Интеграция с диском
Для интеграции с Яндекс Диском мы рассматривали два варианта: подключение через Яндекс REST API и через Яндекс WebDAV API.
Основной пакет, который реализует подключение виртуальных файловых систем в GNOME, это — gvfs. В этом пакете уже имеется инструментарий по работе с WebDAV-протоколом (реализован для Nextcloud). Поэтому в первую очередь мы заинтересовались именно WebDAV-протоколом от Яндекса.
Для проверки работы WebDAV от Яндекса использовался простой скрипт, выводящий список файлов корневого каталога диска.
#!/usr/bin/env ruby $LOAD_PATH.unshift File.expand_path("net_dav/lib", __dir__) require 'net/dav' url = "https://webdav.yandex.ru/" user = "[username]" pass = "[token]" dav = Net::DAV.new(url, :curl => false) dav.verify_server = false dav.credentials(user, pass) dav.find('.',:recursive=>true,:suppress_errors=>true,:filename=>/.*/) do | item | puts "Checking: " + item.url.to_s end
Для реализации аутентификации был добавлен код в модуль Net проверочного тестового скрипта.
case @authorization when :basic req.basic_auth @user, @pass when :digest digest_auth(req, @user, @pass, response) when :oauth oauth_auth(req, @user, @pass) end
…где функция oauth_auth возвращала следующее:
def oauth_auth(request, user, password) request['Authorization'] = "OAuth " + password end
Из вывода видно, что аутентификация, характерная для Яндекса, работает.
Для определения типа авторизации используется заголовок www-authenticate, который возвращает строку WWW-Authenticate: Basic realm=»Yandex.Disk», где аутентификация идентифицирует себя как Basic, но в качестве realm указывается Yandex.Disk. Поэтому для идентификации типа авторизации в WebDAV для Яндекса был добавлен следующий код:
when Net::HTTPUnauthorized then response.error! unless @user response.error! if req['authorization'] new_req = clone_req(req.path, req, headers) if response['www-authenticate'] =~ /^basic/i if disable_basic_auth raise "server requested basic auth, but that is disabled" end if response['www-authenticate'] =~ /Yandex/i @authorization = :oauth else @authorization = :basic end else @authorization = :digest end return handle_request(req, headers, limit - 1, &block)
Получается, что кроме анализа на наличие Basic, нужно выполнять еще и анализ на наличие Yandex. Это довольно важный момент, так как в реализации WebDAV в gvfs-пакете алгоритм идентичный.
Итак, алгоритм работы обрисован, код, отвечающий за WebDAV в gvfs-пакете найден.
Остаётся дело за реализацией. И после внесения изменений и пересборки пакета мы видим, что интеграция GNOME Online Accounts с Яндекс Диском работает как требуется и диск отображается в файловом менеджере Nautilus.
На иллюстрации выше:
-
Область 1 — меню, в котором отображаются подключённые через GNOME Online Accounts источники данных.
-
Область 2 — содержимое Яндекс Диска.
-
Область 3 — в адресной строке отображается содержимое (корень) смонтированного Яндекс Диска.
Подключённый Яндекс Диск отображается как папка на рабочем столе:
Считаем, что интеграция с Яндекс Диском прошла успешно.
Интеграция с адресной книгой
Следующий этап — интеграция адресной книги Яндекса.
Evolution поддерживает протокол carddav, поэтому подходит для интеграции с данным сервисом. В GNOME Online Accounts можно задать поддержку адресной книги или контактов, но при попытке доступа с OAuth-токеном к контактам Яндекса получить эти данные не получилось. Пришлось проводить более глубокое исследование.
В приложении Яндекса в списке разрешений кроме calendar:all нет ничего, что относится к контактам. В документации Яндекса получилось найти информацию по «Контактам CardDAV».
Для проверки доступа мы создали тестовый аккаунт и написали два скрипта на Ruby.
Первый скрипт для проверки доступа с помощью пароля, сгенерированного app-passwords, обращается к адресной книге аккаунта в Яндексе посредством carddav-протокола с помощью базовой авторизации.
#!/usr/bin/env ruby $LOAD_PATH.unshift File.expand_path("lib", __dir__) require 'carddav' service = Carddav.service(:yandex1, 'user1@yandex.ru', 'password_generated_by_yandex_app_passwords') p service.addressbook_url
Второй скрипт для проверки доступа через OAuth и токен обращается к адресной книге аккаунта в Яндексе посредством carddav-протокола с помощью OAuth-авторизации.
#!/usr/bin/env ruby $LOAD_PATH.unshift File.expand_path("lib", __dir__) require 'carddav' service = Carddav.service(:yandex, 'user1@yandex.ru', 'token_generated_by_goa') p service.addressbook_url
Типы авторизации :yandex и :yandex1 — это два типа формирования авторизационного заголовка (Basic или OAuth).
def request(url, method: 'PROPFIND', depth: 0) req = Curl::Easy.perform url.to_s do |c| c.headers['Content-Type'] = 'application/xml' c.headers['Depth'] = depth if @service == :yandex #c.userpwd = "OAuth #{password}".strip c.headers['Authorization'] = "OAuth #{password}".strip elsif @service == :yandex1 pss = Base64.encode64("#{username}:#{password}").strip c.headers['Authorization'] = "Basic #{pss}" #c.userpwd = "#{username}:#{password}" else c.userpwd = "#{username}:#{password}" end c.set :customrequest, method c.set :postfields, yield if block_given? end
Нам было интересно, как отработают оба скрипта.
В листинге ниже вывод, сформированный первым скриптом, с паролем, сгенерированным через app-passwords для CardDAV — при аутентификации типа Basic от сервиса приходит ответ HTTP/1.1 207 Multi-Status. Это корректный и ожидаемый ответ.
https://carddav.yandex.ru/addressbook/user1%40yandex.ru/addressbook/ {"Content-Type"=>"application/xml", "Depth"=>1, "Authorization"=>"Basic **….**"}"} +++++++++++++++ HTTP/1.1 207 Multi-Status Content-Length: 1049 Content-Type: text/xml;charset=utf-8 DAV: 1,addressbook,calendar-access,... Date: Tue, 11 Jul 2023 16:49:05 GMT Set-Cookie: _yasc=NjF9Txq0zqfcgvr67ZyjuNB5Z7nc... <?xml version='1.0' encoding='utf-8'?> <D:multistatus xmlns:D="DAV:">... <D:response><href xmlns="DAV:">/addressbook/user1%40yandex.ru/addressbook/</href>... <status xmlns="DAV:">HTTP/1.1 200 OK</status>
При аутентификации типа OAuth в листинге ниже можно увидеть, что ответ приходит иной — HTTP/1.1 404 Not Found. И далее работа тестового скрипта прекращается. Это не даёт возможности использовать CardDAV с OAuth-токеном.
https://carddav.yandex.ru/addressbook/user1%40yandex.ru/addressbook/ {"Content-Type"=>"application/xml", "Depth"=>1, "Authorization"=>"OAuth **...**"}"} +++++++++++++++ HTTP/1.1 404 Not Found DAV: 1,addressbook,calendar-access,... Date: Tue, 11 Jul 2023 16:57:29 GMT Set-Cookie: _yasc=tj9L+WR9sAD... Transfer-Encoding: chunked X-Request-Id: 16890946496…
То есть при запуске с Basic-авторизацией в ответ на запрос к https://carddav.yandex.ru/addressbook/user1%40yandex.ru/addressbook/ был возвращен список адресных книг, а при OAuth — 404. С этой же ошибкой сталкивается и Evolution при попытке получить список адресных книг.
Получается, что для работы Адресной книги OAuth не подходит, нужен отдельный пароль и иной способ авторизации. Из публичной документации Яндекса не удалось понять, было ли такое поведение задумано и контакты не должны быть доступны при OAuth-авторизации, или же не было добавлено разрешение для приложения.
Мы отправили запрос в техническую поддержку Яндекса и приостановили доработку интеграции с Адресной книгой.
Как теперь выглядит процесс регистрации и доступа к сервисам Яндекса в ОС «МСВСфера АРМ» 9
Регистрация
Переключатели в настройках учётной записи позволяют быстро включить или выключить необходимый функционал.
Почта
Календарь
Диск
Что дальше?
-
Мы планируем отправить изменения в репозитории доработанных пакетов: gnome-online-accounts, evolution-data-server, gvfs.
-
Планируем завершить интеграцию Адресной книги Яндекса.
-
Добавить поддержку REST API Яндекса.
-
Оптимизировать работу с протоколом webDAV для Яндекс Диска.
-
Добавить поддержку других отечественных сервисов.
Заключение
Мы рассказывали о том, как мы интегрировали GNOME Online Accounts с сервисами Яндекса на ХIX конференции разработчиков свободных программ. Вы можете посмотреть и послушать доклад.
Ссылки на проекты:
-
Исходные коды gnome‑online‑accounts: https://git.inferitos.ru/rpms/gnome‑online‑accounts/src/branch/i9
-
Исходные коды evolution‑data‑server: https://git.inferitos.ru/rpms/evolution‑data‑server/src/branch/i9
-
Исходные коды gvfs: https://git.inferitos.ru/rpms/gvfs/src/branch/i9
Вы можете бесплатно протестировать последнюю версию ОС «МСВСфера» 9 от «Инферит» для серверов и рабочих станций, скачав нужный образ дистрибутива на нашем официальном сайте по ссылке.
Приглашаем подписаться на наш телеграм‑канал «Инферит ОС».
Если есть вопросы и предложения — мы будем рады вашим отзывам.
Спасибо за внимание!
ссылка на оригинал статьи https://habr.com/ru/articles/846280/
Добавить комментарий