Статьи

Безопасность ACL в шве, часть 1

Seam всегда был о решении общих проблем, с которыми сталкиваются разработчики веб-приложений. Предоставляя выбор решений «Best Practice» для решения различных задач разработки в унифицированной компонентной модели, разработчик может свободно работать над бизнес-логикой своего приложения, не беспокоясь о проблемах, которые должны быть правильно решены платформой. , Seam позволяет легко создавать такие документы, как PDF-документы, создавать и отправлять электронные письма, а также интернационализировать ваше приложение. Он также интегрируется со сторонними проектами, такими как jBPM и Drools, для поддержки долгосрочных бизнес-процессов и бизнес-правил. Также есть поддержка CAPTCHA и язык разметки в вики-стиле, а также ряд способов сделать AJAX.

Однако одной из наиболее важных областей разработки корпоративных приложений является безопасность. Seam уже давно предоставляет надежный API безопасности, позволяющий защищать компоненты и представления, из которых состоит типичное приложение, с помощью безопасности пользователей и ролей или разрешений безопасности на основе правил. Однако недавно (начиная с версии 2.1.0.GA) Seam пересмотрел свой механизм безопасности, чтобы предоставить ряд новых функций, предлагающих еще больше способов защиты ваших конфиденциальных данных. В этой статье мы рассмотрим одну из этих новых функций, постоянные разрешения, чтобы увидеть, как ACL или безопасность на основе «экземпляров» можно использовать для защиты вашего приложения на уровне объектов.

Для начала давайте рассмотрим различия между безопасностью на основе правил и ACL. Безопасность на основе правил отлично подходит для применения общих разрешений к определенному классу объектов. Например, давайте посмотрим на следующее правило безопасности из одного из примеров Seam:


  Правило DeleteImage:
    «права доступа» группы активации

    без петель,
  когда
    acct: MemberAccount ()
    image: MemberImage (mbr: member -> (mbr.memberId.equals (acct.member.memberId)))
    check: PermissionCheck (target == image, action == «delete», предоставлено == false)
  затем
    check.grant ();
  конец








Условная часть этого правила (которая позволяет пользователям удалять ранее загруженные изображения), по сути, гласит: «Если вы являетесь владельцем этого изображения, вы можете удалить его». В этом примере разрешение безопасности применяется ко всем изображениям и зависит от факта наличия реляционной связи между изображением и его владельцем. Именно через это отношение правило безопасности может определить, что текущий пользователь является владельцем изображения, и, в свою очередь, предоставить разрешение на выполнение действия. Тем не менее, что, если не было никакой связи между целью проверки разрешений и пользователем (которого мы будем называть принципалом далее)? Это где безопасность ACL приходит.

ACL (Access Control List) — это список явных разрешений, назначенных определенному объекту. Каждая запись в списке содержит получателя (принципал, которому предоставлено разрешение) и действие. Если вы когда-либо использовали операционную систему на основе * nix, то вы уже должны быть знакомы с определенным типом ACL — файловая система содержит таблицу разрешений на чтение, запись и выполнение для каждого файла (да, Windows имеет аналогичную защиту файлов, но это не так очевидно). Безопасность на основе ACL в Seam работает почти так же, за исключением того, что она используется для защиты экземпляров объектов вместо файлов. В типичном приложении эти экземпляры объектов обычно являются объектами, однако вскоре мы увидим, что можно защитить любой тип объекта.

Прежде чем мы сможем начать назначать разрешения для наших объектов, нам сначала нужно немного подготовиться. Самое главное, нам нужно место для хранения самих разрешений. Seam предоставляет интерфейс PermissionStore, который объявляет методы, необходимые для управления разрешениями объекта ACL. Хотя теоретически возможно хранить разрешения в любом типе постоянного хранилища (например, в файлах, в LDAP и т. Д.), В большинстве случаев имеет смысл использовать реляционную базу данных. Для этого Seam поставляется с реализацией PermissionStore, называемой JpaPermissionStore, которая позволяет хранить разрешения в базе данных с использованием JPA. Чтобы использовать JpaPermissionStore, нам нужно сделать две вещи; создайте объектный компонент для хранения записей разрешений и настройте этот объектный компонент в файле Seam component.xml.

[Img_assist | NID = 7273 | название = | убывание = | ссылка = нет | ALIGN = не определено | ширина = 431 | высота = 207]

Специальный набор аннотаций используется для настройки того, какие свойства объекта представляют аспекты разрешения. В следующем коде показан минимальный пример (для краткости аннотации показаны в полях, а методы getter / setter опущены):

  @Entity
public class AccountPermission implements Serializable
{
@Id @GeneratedValue public Integer permissionId;
@PermissionUser @PermissionRole public String recipient;
@PermissionTarget public String target;
@PermissionAction public String action;
@PermissionDiscriminator public String discriminator;
}

 

Аннотации @PermissionUser и @PermissionRole указывают свойство, содержащее получателя разрешения. В этом примере мы используем одну таблицу для хранения разрешений как пользователей, так и ролей, поэтому мы помещаем обе аннотации в поле получателя — и поскольку мы храним оба типа разрешений в одной таблице, нам также нужен дискриминатор (аннотированный @PermissionDiscriminator), чтобы Seam мог определить, какие записи предназначены для пользователей, а какие для ролей. Наконец, нам нужно свойство для хранения цели разрешения (с аннотацией @PermissionTarget) и свойство для действия разрешения (с аннотацией @PermissionAction). 

После того как мы создали нашу сущность, нам просто нужно настроить JpaPermissionStore для ее использования, добавив следующую запись в файл конфигурации Seam’s component.xml:

 <security:jpa-permission-store user-permission-class="com.acme.AccountPermission"/>

  
Теперь, когда мы создали и настроили нашу сущность хранилища разрешений, мы можем начать назначать разрешения. API безопасности Seam предоставляет удобный компонент PermissionManager, который позволяет нам легко управлять разрешениями объектов. Его методы выглядят очень похоже на методы, имеющиеся в интерфейсе PermissionStore, и фактически они, по сути, делегируют базовому PermissionStore, однако, с одним небольшим ограничением — каждая вызываемая операция, связанная с разрешениями, сначала проверяется, чтобы убедиться, что у вызывающего пользователя есть необходимые привилегии для вызвать эту операцию. Более подробную информацию об этом можно найти в Справочном руководстве по шву, однако достаточно сказать, что не только любой пользователь может управлять разрешениями объекта, но для этого он должен сначала обладать необходимыми привилегиями.

Давайте дадим наше первое разрешение! Представьте, что ваше приложение содержит таблицу клиентов (представленную компонентом с именем Customer), и вы хотели бы предоставить привилегии обновления для определенных клиентов вашим различным продавцам. Допустим, вы хотите позволить продавцу Бобу управлять информацией о вашем лучшем клиенте, учетной записи Jones (у которого идентификатор клиента равен 1234). Мы даем разрешение Бобу управлять этим клиентом, используя следующий метод:

  PermissionManager.instance().grantPermission(new Permission(entityManager.find(Customer.class, 1234), 
"update", new SimplePrincipal("bob")));

 При этом PermissionManager делегирует вызов grantPermission () настроенному хранилищу JpaPermissionStore (после проверки того, что текущий пользователь имеет для этого необходимые привилегии), и в базе данных будет создана новая запись разрешения. Давайте на минуту рассмотрим, что именно записывается в таблицу AccountPermission:


AccountPermission

=================

ДИСКРИМИНАТОР ДЕЙСТВИЙ
ЦЕЛЕВА ПОЛУЧАТЕЛЯ

—————————- ——————-

Боб Заказчик: 1234 обновление пользователя

Здесь мы можем ясно видеть из значений столбца, что получатель — «bob», действие — «update», а дискриминатор — «пользователь» (потому что Боб — пользователь, а не роль). Содержимое целевого столбца немного интереснее. Отображаемое здесь значение называется идентификатором объекта и используется для уникальной идентификации конкретного экземпляра объекта. В этом случае рассматриваемая цель является сущностью, и к счастью для нас, Seam уже знает, как генерировать уникальные идентификаторы объектов для сущностей. Однако, если целью разрешения является не сущность, а экземпляр какого-либо другого типа класса, тогда Seam должен сказать, как создать идентификатор объекта для этого класса. Это делается путем добавления аннотации @Identifier к рассматриваемому классу,и указание реализации IdentifierStrategy следующим образом:

  @Identifier(CustomIdentifierStrategy.class)
public class MyNonEntityClassThatIWantToAssignPermissionsTo {
public String getUniqueProperty() { return foo; }
}

Интерфейс IdentifierStrategy предельно прост, объявляя только два метода. Метод canIdentify () возвращает true, если реализация IdentifierStrategy способна генерировать идентификатор для указанного класса, а метод getIdentifier () возвращает уникальный идентификатор String для указанного объекта. Например, реализация EntityIdentifierStrategy, включенная в Seam, создает идентификаторы для сущностей путем объединения имени сущности со свойством ID.

[Img_assist | NID = 7274 | название = | убывание = | ссылка = нет | ALIGN = не определено | ширина = 263 | высота = 117]

Вот пример того, как может выглядеть реализация:

  public class CustomIdentifierStrategy implements IdentifierStrategy {
public boolean canIdentify(Class targetClass) {
return targetClass.equals(MyNonEntityClassThatIWantToAssignPermissionsTo.class);
}
public String getIdentifier(Object target) {
return ((MyNonEntityClassThatIWantToAssignPermissionsTo) object).getUniqueProperty();
}
}

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

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

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