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

В этой статье речь пойдёт о самом скучном интересном в ИТ – об архитектуре ПО, а именно, об одной из самых важных её частей – 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 — Зависимые списки доступа


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

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

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


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

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


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

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

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

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


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

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

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

UPD: Следующая статья
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 9

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

    Жизнь гораздо более жестока… часто пользователь, не имеющий прав доступа к документу, должен не только знать что он есть, знать кто его может прочитать, но и иметь доступ к некоторым его данным.
      0
      А какие сценарии этого могут требовать? Если речь идёт о том, что непосредственно сам пользователь должен, не обладая правами, иметь возможность зачитать данные какого-то объекта — то это ведь дырка в безопасности.
      А вот если речь идёт о прикладном коде, выполняющемся от имени пользователя — то да, такая штука бывает. Можно для этого реализовать отключение проверки прав внутри прикладного кода логики. Грубо говоря, пользователь инициирует операцию, на первом этапе проверяются права этого пользователя в плане возможности выполнить эту операцию, а затем прикладной код работает в «безопасном режиме», без проверки прав на другие объекты. Или можно завести системного пользователя, обладающего всеми правами и имперсонироваться в него в бизнес-логике, когда нужно.
        +1
        Сценарий может быть такой: объект — документ, часть полей которого заполняет секретарь, а часть руководитель.
        Секретарь имеет только частичный доступ к полям объекта, например, редактирует входящий номер.
        В этом случае, видимо, необходимо реализовывать список доступа к полям объекта.
          0
          Да, можно реализовать список доступа к полям объекта
          А можно сделать разные UI объекта — для создания один, а для редактирования — другой. В документообороте, по крайней мере, это хорошо ложиться на требования. Регистратор по правам мог бы изменить эти поля, но вот «чисто случайно» их в UI при регистрации нету. И наоборот.
          Но если система имеет API — то в этом случае, конечно, в нём будет дырка :)
            0
            Проще тогда доступ давать не к объекту, а к его представлению. Одно представление для секретаря, другое для руководителя, третье для прочих лиц.
      0
      Насколько понял TreeSupport — это кеширование всех прав доступа всех пользователей на все ресурсы.

      В своем варианте («Дьявольский» ACL — мой вариант проверки прав) я решил таким образом: при запросе сначала проверяю по этим кешируемым данным и если в них нет, тогда запускаю долгий алгоритм определения, после которого записываю результат в кеш. В этом случае TreeSupport строиться по мере необходимости. И при изменении прав не нужно его полностью перестраивать. Однако я кеширую результат доступа на ресурс для группы, а не для конкретного пользователя.
        0
        за ссылку спасибо, зачитаю )
        Но TreeSupport — это другое. Это именно развертка иерархической структуры в плоскую, там НЕ содержатся права по отношению к объектам, иначе она была бы слишком здоровая и тормозная.
        Грубо говоря, связь Группа -> Пользователь И Группа -> Группа там содержатся, но не Объект -> Группа и не Объект -> Пользователь
          0
          >>развертка иерархической структуры в плоскую
          В общем то это и получается типа кеширования. По крайней мере в моем понимании. Т.е. все права заданы в иерархической форме, а в таблице для быстрой выборки это все плоском виде храниться.

          >>там НЕ содержатся права по отношению к объектам
          это значит я не разобрался.Я просто поглядел запрос join, там фигурирует и пользователь и Id какие-то (ObjectId и SecuritySubjectId) и запрашиваемая операция a.CanRead = 1, вот и решил что таблица содержит права на ресурсы. Нужно будет внимательнее почитать. :)
        +1
        Зависимые списки доступа — в первую очередь организационная проблема. Ну а далее обычно — добавление нужных групп в ACL.

        Последние две проблемы решаются легко — не используйте в ACL конкретных пользователей. Создайте для каждого отдельную группу и делегируйте полномочия через неё. Иногда можно сделать несколько групп для передачи части полномочий.

        Only users with full accounts can post comments. Log in, please.