[Из песочницы] Проблемы разграничения доступа на основе списка доступа в ECM системах

2ee6754999cd4655a3277688b4fa2dcf.jpgВ этой статье речь пойдёт о самом скучном интересном в ИТ — об архитектуре ПО, а именно, об одной из самых важных её частей — security.

Определимся с терминами


Под ПО я буду понимать в первую очередь ECM системы, и будем мы рассматривать security только в части разграничения доступа к объектам предметной области.

Немного о ECM


Из википедии
04622a1603bb46d5ad2831db19faa823.jpg

Управление корпоративным контентом (англ. Enterprise content management, ECM) — управление цифровыми документами и другими типами контента, а также их хранение, обработка и доставка в рамках организации [1]


MS SharePoint, Alfresco — это всё ECM системы. Чтобы не рассматривать некие сферические ECM в вакууме с одной стороны, и в то же время не ограничивать статью каким-то существующим решением — придумаем свою простую ECM систему.

Немного о предметной области


Итак, пусть наша ECM система будет трудится на ниве обеспечения документооборота какой-то организации. Принес девочке-секретарше курьер письмецо от налоговой, а она раз его — и в систему, чтобы бухгалтер посмотрел и сгенерировал ответ (конечно — же тоже через систему).

Придумал начальник новую идею оптимизации — и тоже в систему, обсудить с приближенными. Майские праздники скоро? Девочка секретарша выпускает приказ о нерабочих днях. И тоже через систему, чтобы все увидели.

Чуть более формально


Как видно выше, система наша работает с документами, часть из которых поступает «снаружи» организации, должна быть зарегистрирована в системе и рассмотрена нужными людьми, часть создаётся в самой организации и предназначена для внутреннего пользования, ещё часть создаётся также в самой организации, но предназначена для отправки «наружу» на каком-то этапе своей жизни.

Разграничение доступа к экземплярам


Если посмотреть на пример выше повнимательнее, то видно, что письмо девочки-секретарши про майские праздники, а также идея начальника про оптимизацию кого-нибудь — это всё «внутренние» документы, предназначенные только для сотрудников организации. Но что-то намекает, что эти два документа имеют несколько разный круг лиц, допущенных к их просмотру и изменению. Таким образом очень скоро мы приходим к выводу, что для вроде бы однотипных объектов предметной области требуется разграничивать доступ к разным экземплярам по-разному.

Access Control List


Итак, мы плавно приходим к такой структуре данных:

dfac04c3cebb481b8686486e1d7a610c.JPG

Вроде бы всё сразу стало хорошо — теперь для каждого объекта предметной области можно для отдельных пользователей системы назначить разные права. Но сразу встаёт проблема: девочка-секретарша вопит, что:

  • ей для регистрации (создания) в системе нового документа (объекта) требуется в список доступа прописать кучу людей, особенно если документ предназначен для всех сотрудников
  • она стала создавать документы в системе ещё и за начальника, потому что он заявил, что «система гавно и он в ней работать не будет»


Список доступа по умолчанию


Напрягаем мозжечок и придумываем такое решение:

f6763283de4748458310889d9a1f7e6d.JPG

Находим какой-нибудь классифицирующий признак для наших объектов, выделяем его в отдельную сущность, заводим на нём «Список доступа по умолчанию» и организовываем формирование списка доступа нашего объекта при его создании на основании списка доступа по умолчанию этого самого классифицирующего признака.

Девочка секретарша отстала, но вопит, зараза, админ, и жалуется он вот на что:

  • После очередной идеи начальства по оптимизации кого-то в организации, админу требуется руками пройтись по всем созданным документам и удалить оттуда оптимизированного сотрудника руками
  • Конечно же для этого админа пришлось добавить в список доступа по умолчанию для всех видов наших документов, что плохо — ведь оптимизировать могут и админа…


Группы пользователей


Решаем проблему следующим образом:

c20aa667af714705986ed1ad29b44523.JPG

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

Админ от нас отстал вслед за девочкой-секретаршей, т.к. в системе были заведены группы пользователей и в доступ по умолчанию стали добавляться преимущественно группы пользователей. Админ же просто удалял\добавлял пользователей из групп и больше не имел доступа ко всем документам системы.

Но резко завопили все остальные, т.к. система стала тормозить.

Дело в том, что в системе есть очень разумное требование, заключающееся в том, что если пользователь НЕ может читать документ по правам, то значит он вообще не должен знать о его существовании, а значит — результаты поисков объектов в системе должны фильтроваться на основании прав пользователя.

Конечно программисты нашей системы грамотные и сделали фильтрацию на уровне БД, но всё же для этого им пришлось использовать сложные рекурсивные запросы (мы помним, что пользователи у нас могут входить в группы, а группы — в другие группы).

TreeSupport


Что делать? Смутно вспоминаем чему нас учили в институте и реализовываем механизм развертывание иерархической структуры в плоскую. Этот механизм имеет своё название, к сожалению, я его так и не вспомнил. Назовём его TreeSupport:

7054ce501d6c45ea9c7472918baac6d0.JPG

Правила формирования таблицы TreeSupport такие:

  • Для каждого Субъекта Безопасности в таблице создаётся запись, где Parent = Child = этому субъекту безопасности
  • Для каждой Группы создаётся столько записей, сколько у неё вложенных пользователей или групп (рекурсивно до самого низа), в которых
    Parent = группе, Child = вложенной группе или пользователю


Пример:

Иерархическая структура:

  • Пользователь 1
  • Группа 1
    • Пользователь 2
    • Группа 2
      • Пользователь 3


TreeSupport

Parent Child
Пользователь 1 Пользователь 1
Группа 1 Группа 1
Группа 2 Группа 2
Пользователь 3 Пользователь 3
Группа 1 Пользователь 2
Группа 1 Группа 2
Группа 1 Пользователь 3
Группа 2 Пользователь 3


Реализовали? Ура! Теперь фильтрация объектов при поиске происходит у нас быстро — за один джойн, примерно такой:

select Id    from Object o 
                join ACL a on o.Id = a.ObjectId
                join TreeSupport t on t.ParentId = a.SecuritySubjectId
where t.ChildId = <текущий пользователь> and a.CanRead = 1


Правда ценой того, что сложнее стало изменять нашу иерархическую структуру пользователей и групп — надо актуализировать TreeSupport. Хорошо хоть изменяется она редко.

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

Проблема 1 — Зависимые списки доступа


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

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

Проблема 1 — Делегирование полномочий


Или другой сценарий — пользователь является начальником и у него есть заместители. Поэтому все объекты, к которым он имеет доступ, автоматически должны быть доступны его заместителям с теми же или с ограниченными правами.

Проблема 2 — Предоставление доступа к большому количеству объектов


Очень частая ситуация, когда пользователь работал-работал с системой пару лет, засветился в списке доступа, например, в 100к объектов, а затем… уволился.

На его место назначен другой человек и теперь он должен иметь доступ к тем же объектам, что и уволившийся. Чтобы обеспечить ему доступ к тем-же объектам — мы должны запустить продолжительный процесс, заключающийся в переборе всех объектов и в модификации их списка доступа (с учётом зависимых объектов, заместителей и т.д.).

Частенько этот процесс выполняется очень долго. А в некоторых сценариях — неприемлемо долго.

Ручное редактирование списка доступа


И напоследок, комментарий по поводу идеи, которая прямо напрашивается –, а давайте чуть что заставлять пользователя самого редактировать список доступа. Так вот — это не работает.

Список доступа — эта та вещь, которая в 99% процентах времени работы с системой должна быть скрыта от пользователя. Поэтому все модификации списков доступа при реализации типичных сценариев работы с системой должны происходить автоматически. А архитектурные решения, стоящие за решением вышеозначенных проблем абсолютно точно не должны влиять на скорость фильтрации объектов при поисках и должны минимально влиять на скорость совершений операций над объектами и над списками объектов (в том числе, большими).

Как я писал выше — буду просто счастлив услышать ваши мысли по поводу решения этих проблем.

© Habrahabr.ru