Как стать автором
Обновить

Триггеры, права доступа и версионность в точке доступа SPARQL

Время на прочтение4 мин
Количество просмотров2.9K
Тому, кто попытается использовать точку доступа SPARQL в качестве замены базы данных в каком-нибудь индустриальном проекте, придется столкнуться с несколькими неприятностями. Одна из них — отсутствие в арсенале средств такого продукта контроля прав доступа, триггеров, и возможностей организации версионности. Изучив все, что предлагается на рынке сегодня, мы пришли к необходимости реализовать такой функционал собственными силами.
В качестве «подопытного кролика» выступает Apache Fuseki, хотя тот же принцип можно применить к любой другой SPARQL endpoint.

Архитектура и функционал

Единственным способом реализации задуманного, если не лезть внутрь самого продукта, является создание прокси-слоя над программным интерфейсом точки доступа. Это мы и проделали. Все SPARQL-запросы, обращенные к сервису, проходят через прокси, где подвергаются анализу и дополнительной обработке.
При обращении к прокси приложение может авторизоваться под определенной учетной записью пользователя — для этого стандартный программный интерфейс пришлось немного расширить — а может обратиться анонимно, оставаясь в рамках стандарта.
Прокси имеет собственный back-end, который предоставляет возможность настройки прав доступа пользователей (групп пользователей) к классам онтологии. Права наследуются. Уровень доступа можно установить в следующие значения:

  • права доступа отсутствуют;
  • чтение;
  • изменение с модерацией;
  • изменение.

Понятно, что каждый объект онтологии может быть одновременно экземпляром любого количества классов (в т.ч. с учетом наследования). Из всех применимых к родительским классам вариантов прав выбирается наиболее строгий.
Возможность редактирования определений классов и свойств регулируется путем предоставления прав доступа к стандартным типам, таким, как owl:Class.

В нашем случае важно было обеспечить возможность коллективной работы над онтологией. Уровень прав «изменение с модерацией» позволяет пользователю выполнять запросы DELETE/INSERT, но их результат не применяется немедленно к базе данных, а поступает на утверждение пользователям, имеющим надлежащие права. Раз в сутки back-end информирует таких пользователей о поступивших изменениях, и они имеют возможность применить их, или отвергнуть.
Все изменения, внесенные пользователями в онтологию, сохраняются в журнале, который лежит в сервисной БД back-end'а (реляционной; настройки прав доступа хранятся в ней же). В результате можно построить историю изменений для всех свойств каждого объекта онтологии, с указанием даты и автора каждого изменения.

Возвращаясь к правам доступа: любой запрос, поступивший на прокси, проходит проверку прав или перед исполнением, или после него. Если запрос направлен на выборку данных (SELECT, ASK, CONSTRUCT), то из набора его результатов исключаются решения, содержащие те объекты, к которым текущий пользователь не имеет права доступа (если запрос был анонимным — возвращаются только решения, целиком состоящие из экземпляров классов, на которые не установлены ограничения прав). Если запрос имеет тип DELETE/INSERT/UPLOAD, то сначала определяется набор триплетов, которые он затронет, и если хотя бы к одному из них нет прав доступа на редактирование — отменяется запрос в целом. Конечно, front-end'ы, которые работают с нашим прокси, пришлось «научить» интерпретировать сообщения об ошибках, а также предупреждения о том, что изменения ушли на модерацию.
Парные запросы DELETE/INSERT выявляются, и при отмене запроса INSERT (а вдруг?) отменяется и парный ему DELETE. Вообще, при написании прокси пришлось применить несколько интересных workaround'ов; например, ответ на запрос типа SELECT может не включать объекты, доступ к которым запрещен, однако в вычислении решения они будут задействованы. Такая ситуация возникнет, например, при выполнении запроса

SELECT ?prop WHERE { ?object <has_property_1> "some value". 
      ?object <has_property_2> ?prop }


в случае, если пользователь не имеет прав доступа к части объектов ?object. Наш прокси разворачивает такие запросы, и вернет свойства ?prop только доступных объектов. Похожую обработку пришлось применить для запросов, возвращающих значение COUNT(*).

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

Результаты и производительность


С точки зрения функционала, мы достигли всех намеченных результатов. Система обеспечивает контроль прав доступа независимо от того, какое приложение отправляет запрос к точке доступа, а также рассылает уведомления об изменениях в данных. Журнал изменений позволяет восстанавливать состояние любого объекта на произвольный момент времени. Функционал «редактирования с подтверждением» обеспечивает полноценную модерацию изменений в онтологии.
Осталось выяснить, насколько дополнительная обработка повлияет на скорость выполнения запросов. Прежде всего, мы были заинтересованы в том, чтобы не пострадала скорость SELECT-запросов, поскольку наш продукт функционирует как каталог мастер-данных для нескольких других информационных систем.
Проанализировав поступающие под реальной нагрузкой SELECT-запросы, а также те запросы к реальной точке доступа SPARQL, которые выполняет сам прокси, мы выяснили, что больше половины из них составляют простые выражения типа «A является подклассом B», и «A является членом класса B». Разумеется, такие запросы легко кэшировать, и обновлять кэш при изменении содержания реальной БД при помощи нашего же механизма триггеров. В результате, прокси отвечает на запросы такого рода (а также некоторые более сложные) без обращения к реальной точке доступа, а также широко использует кэш в алгоритмах вычисления прав доступа. Результат превзошел наши ожидания: под реальной нагрузкой система работает всего лишь на 13% процентов медленнее, чем при прямом обращении к настоящей endpoint без контроля прав.

С запросами на изменение данных дело обстоит не столь оптимистично: их выполнение стало медленнее в 6 раз, поскольку обработка на DELETE/INSERT (и, тем более, UPLOAD) существенно сложнее, и не может быть оптимизирована. Что ж, с этим на рабочей системе пришлось смириться, потеряв некоторую часть производительности в обмен на функциональность.
Теги:
Хабы:
+4
Комментарии7

Публикации

Истории

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн