В этой статье речь пойдёт о самом скучном интересном в ИТ – об архитектуре ПО, а именно, об одной из самых важных её частей – security.
Определимся с терминами
Под ПО я буду понимать в первую очередь ECM системы, и будем мы рассматривать security только в части разграничения доступа к объектам предметной области.
Немного о ECM
Управление корпоративным контентом (англ. Enterprise content management, ECM) — управление цифровыми документами и другими типами контента, а также их хранение, обработка и доставка в рамках организации [1]
MS SharePoint, Alfresco – это всё ECM системы. Чтобы не рассматривать некие сферические ECM в вакууме с одной стороны, и в то же время не ограничивать статью каким-то существующим решением – придумаем свою простую ECM систему.
Немного о предметной области
Итак, пусть наша ECM система будет трудится на ниве обеспечения документооборота какой-то организации. Принес девочке-секретарше курьер письмецо от налоговой, а она раз его – и в систему, чтобы бухгалтер посмотрел и сгенерировал ответ (конечно – же тоже через систему).
Придумал начальник новую идею оптимизации – и тоже в систему, обсудить с приближенными. Майские праздники скоро? Девочка секретарша выпускает приказ о нерабочих днях. И тоже через систему, чтобы все увидели.
Чуть более формально
Как видно выше, система наша работает с документами, часть из которых поступает «снаружи» организации, должна быть зарегистрирована в системе и рассмотрена нужными людьми, часть создаётся в самой организации и предназначена для внутреннего пользования, ещё часть создаётся также в самой организации, но предназначена для отправки «наружу» на каком-то этапе своей жизни.
Разграничение доступа к экземплярам
Если посмотреть на пример выше повнимательнее, то видно, что письмо девочки-секретарши про майские праздники, а также идея начальника про оптимизацию кого-нибудь – это всё «внутренние» документы, предназначенные только для сотрудников организации. Но что-то намекает, что эти два документа имеют несколько разный круг лиц, допущенных к их просмотру и изменению. Таким образом очень скоро мы приходим к выводу, что для вроде бы однотипных объектов предметной области требуется разграничивать доступ к разным экземплярам по-разному.
Access Control List
Итак, мы плавно приходим к такой структуре данных:
Вроде бы всё сразу стало хорошо – теперь для каждого объекта предметной области можно для отдельных пользователей системы назначить разные права. Но сразу встаёт проблема: девочка-секретарша вопит, что:
- ей для регистрации (создания) в системе нового документа (объекта) требуется в список доступа прописать кучу людей, особенно если документ предназначен для всех сотрудников
- она стала создавать документы в системе ещё и за начальника, потому что он заявил, что «система гавно и он в ней работать не будет»
Список доступа по умолчанию
Напрягаем мозжечок и придумываем такое решение:
Находим какой-нибудь классифицирующий признак для наших объектов, выделяем его в отдельную сущность, заводим на нём «Список доступа по умолчанию» и организовываем формирование списка доступа нашего объекта при его создании на основании списка доступа по умолчанию этого самого классифицирующего признака.
Девочка секретарша отстала, но вопит, зараза, админ, и жалуется он вот на что:
- После очередной идеи начальства по оптимизации кого-то в организации, админу требуется руками пройтись по всем созданным документам и удалить оттуда оптимизированного сотрудника руками
- Конечно же для этого админа пришлось добавить в список доступа по умолчанию для всех видов наших документов, что плохо – ведь оптимизировать могут и админа…
Группы пользователей
Решаем проблему следующим образом:
Даём возможность объединять пользователей системы в группы, которые также могут быть указаны как в списке доступа объектов, так и в списке доступа по умолчанию. Группы могут объединять как пользователей, так и другие группы.
Админ от нас отстал вслед за девочкой-секретаршей, т.к. в системе были заведены группы пользователей и в доступ по умолчанию стали добавляться преимущественно группы пользователей. Админ же просто удалялдобавлял пользователей из групп и больше не имел доступа ко всем документам системы.
Но резко завопили все остальные, т.к. система стала тормозить.
Дело в том, что в системе есть очень разумное требование, заключающееся в том, что если пользователь НЕ может читать документ по правам, то значит он вообще не должен знать о его существовании, а значит – результаты поисков объектов в системе должны фильтроваться на основании прав пользователя.
Конечно программисты нашей системы грамотные и сделали фильтрацию на уровне БД, но всё же для этого им пришлось использовать сложные рекурсивные запросы (мы помним, что пользователи у нас могут входить в группы, а группы – в другие группы).
TreeSupport
Что делать? Смутно вспоминаем чему нас учили в институте и реализовываем механизм развертывание иерархической структуры в плоскую. Этот механизм имеет своё название, к сожалению, я его так и не вспомнил. Назовём его TreeSupport:
Правила формирования таблицы 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% процентах времени работы с системой должна быть скрыта от пользователя. Поэтому все модификации списков доступа при реализации типичных сценариев работы с системой должны происходить автоматически. А архитектурные решения, стоящие за решением вышеозначенных проблем абсолютно точно не должны влиять на скорость фильтрации объектов при поисках и должны минимально влиять на скорость совершений операций над объектами и над списками объектов (в том числе, большими).
Как я писал выше — буду просто счастлив услышать ваши мысли по поводу решения этих проблем.
Автор: Dronopotamus