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

    Тому, кто попытается использовать точку доступа 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) существенно сложнее, и не может быть оптимизирована. Что ж, с этим на рабочей системе пришлось смириться, потеряв некоторую часть производительности в обмен на функциональность.
    Метки:
    Поделиться публикацией
    Комментарии 7
    • +1
      Интересно. У нас была такая же проблема, но мы не решаем вопрос добавления авторицации к запросам на обновление и удаление, так как наша система используется не как первичный каталог данных, а как точка сбора информации из многих внутренних систем. Аутентификация и процессы работы с данными происходят не у нас.
      Таким образом мы только забираем данные из подключённых систем (с помощью SDShare: sdshare.org/ ) и доверяем запросам на обновление и удаление от клиентов.
      • 0
        А так как напрямую никто с нашим endpoint тоже не говорит (наружу мы показываем данные только как SDShare потоки, очень похожие на Atom feed c RDF/XML или n3 в теле поста), то прокси нам был не нужен, мы зашиваем наши проверки авторизации в SPARQL, который генерирует списки документов и содержание документов.
      • +1
        Ясно, интересно.
        У нас тоже кое-какие данные поступают снаружи, от других систем; поэтому и понадобилось делать контроль на уровне triple store, а не в пользовательском интерфейсе редактора данных. Каждая внешняя система имеет свой пользовательский аккаунт, который разрешает ей вносить изменения только в определенную часть данных — так страхуемся от чужих багов, или злоумышленников с доступом к сторонней системе. Тут все внутри компании, и весьма строго по политике безопасности.

        SDShare, насколько я помню, реплицирует данные между несколькими хранилищами в виде, по сути, файлов RDF. У нас не совсем такая ситуация, поскольку данные в семантической форме присутствуют пока только в одной точке — в самом MDM. Для остальных систем MDM выглядит как сервис, т.е. все данные он никогда никому не отдает — только по частям, и чаще всего — в ответ на запрос (или, наоборот, в активном режиме — по подписке).

        Кстати, с репликацией и де-факто кластеризацией Triple Store мы тоже наплясались, но это тема, которую я пока не готов раскрывать. Там у нас распределенная архитектура, т.е. сам Triple Store живет на нескольких серверах, хотя снаружи выглядит как одна целостная система.

        Проект, где идет в чистом виде сбор информации со сторонних источников, у нас тоже был: http://energopravda.ru
        Там, конечно, все проще. Роботы разбирают разные источники, и льют контент в общую базу.

        • 0
          Про уч записи из многих систем: Да, мы тоже собираем такую информацию. Особенно полезно её использовать потом для single sign on (так как можно установить owl:sameAs между записями в разных системах) и для экспорта в поисковую машину.

          Да, строго говоря, SDShare создан для синхронизации хранилищ троек, но в нашем случае мы пишем адаптеры от внешних систем в RDF, которые позволяют нам представить, например, Active Directory или БД как хранилище троек.

          Я правильно понимаю, что Вы как правило используете TDB как хранилище троек, так как пользуетесь Fuseki? Я понимаю что всегда можно начать говорить с другим endpoint, но всегда есть нюансы. Любопытно потому, что мы обычно используем Virtuosо, но он нас не очень устраивает порой.
          • 0
            Да, всегда TDB как хранилище (Jena). Смотрели на Oracle еще. Пока остаемся в рамках Open Source решения, в связи с требованиями большинства проектов.
            • 0
              Забавно, но вот именно сейчас я пытаюсь понять подходит ли нам Exadata 12c в качестве хранилища.
              • 0
                Тут не знаком с предметом, к сожалению. 11g смотрел в качестве Triple store — работает, но нам не подошел, т.к. нужен Open Source.

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