Авторизация
В этом разделе рассматриваются темы, связанные с выполняемым фреймворком контролем доступа.
Проверки доступа к данным
В данной таблице объясняется, как разрешения и ограничения на доступ к данным используются различными механизмами фреймворка.
Операции с сущностями |
Атрибуты сущностей |
JPQL-политика уровня строк (1) |
Предикатная политика уровня строк READ (2) |
Предикатная политика уровня строк CREATE/UPDATE/DELETE |
|
|
Да (3) |
Нет |
Да |
Да |
Да |
|
Нет |
Нет |
Нет |
Нет |
Нет |
UI-компоненты, связанные с данными |
Да |
Да |
- (4) |
- (4) |
- (4) |
REST API |
Да |
Да |
Да |
Да |
Да |
REST API |
Да |
Да |
Да |
Да |
- (5) |
REST API |
Да |
Да |
- (6) |
- (6) |
- (6) |
Примечания:
1) JPQL-политика уровня строк влияет только на корневую сущность.
// order is loaded only if it satisfies JPQL policies on the Order entity
Order order = dataManager.load(Order.class).id(orderId).one();
// related customer is loaded regardless of JPQL policies on the Customer entity
assert order.getCustomer() != null;
2) Предикатная политика уровня строк влияет на корневую сущность и все связанные сущности в загруженном графе.
// order is loaded only if it satisfies constraints on the Order entity
Order order = dataManager.load(Order.class).id(orderId).one();
// related customer is not null only if it satisfies predicate policies on the Customer entity
if (order.getCustomer() != null) { /*...*/ }
3) Разрешения на операцию c сущностью в DataManager
выполняется только для корневой сущности.
// loading Order
Order order = dataManager.load(Order.class).id(orderId).one();
// related customer is loaded even if the user has no permission to read the Customer entity
assert order.getCustomer() != null;
4) UI-компоненты не проверяют политики уровня строк сами, но когда данные загружаются стандартным способом, ограничения налагаются в DataManager
. В результате, если некоторый экземпляр сущности отфильтрован политикой уровня строк, соответствующий UI-компонент отображается, но является пустым. Кроме того, для любого действия, основанного на классе SecuredListDataComponentAction
, можно указать определенную операцию с сущностью, используя метод setConstraintEntityOp()
. Так, действие будет доступным только если операция разрешена для выбранного экземпляра сущности.
5) REST-запросы выполняют только чтение данных.
6) Параметры и результаты методов REST-сервисов не проверяются на соответствие политикам уровня строк. Поведение сервиса в отношении безопасности уровня строк определяется тем, как он читает и сохраняет данные, например, использует ли он "DataManager" или "UnconstrainedDataManager".
Security constraints are only applied when using DataManager to retrieve entity data. When using JPQL queries directly against the database (such as in reports) these security constraints are not applied. You will need to ensure you are handling security constraints yourself in those cases. |
Ограничения доступа
В этом разделе кратко объясняется, как работает авторизация Jmix. Эта информация будет полезна, если вам нужно проверить разрешения пользователя в вашем коде, или вы хотите расширить или заменить стандартную систему разрешений на основе ролей и политик.
Механизмы фреймворка содержат точки авторизации, где проверяются разрешения на операцию или данные. Для каждой точки авторизации существует контекст доступа, который представляет собой класс, реализующий интерфейс AccessContext
и имеющий атрибуты, описывающие объект авторизации.
Любой модуль фреймворка, дополнение или целевое приложение могут определять и регистрировать набор ограничений доступа для определенного контекста доступа (т.е. для точки авторизации). Ограничение - это класс, реализующий интерфейс AccessConstraint
с помощью метода applyTo(AccessContext)
. В этом методе реализация ограничения решает, разрешен ли объект авторизации, и обновляет состояние контекста доступа информацией об этом решении.
Авторизуемый механизм применяет существующие ограничения в точке авторизации с помощью бина AccessManager
. В результате в состоянии контекста механизм получает информацию об авторизации от всех ограничений и решает, продолжать или прервать операцию с объектом.
Давайте проиллюстрируем этот процесс примером проверки прав на загрузку сущности с помощью DataManager
.
-
В ядре фреймворка существует класс
CrudEntityContext
, который реализуетAccessContext
и имеет следующие атрибуты:-
entityClass
– здесьDataManager
указывает, какая сущность загружается. Вместе с классом контекста это значение описывает точку авторизации. -
readPermitted
– этот атрибут заполняют ограничения доступа, чтобыDataManager
мог решить, следует ли продолжать загрузку сущности.
-
-
В подсистеме безопасности существует класс
CrudEntityConstraint
, который реализуетAccessConstraint
и его методы:-
getContextType()
возвращаетCrudEntityContext.class
, чтобы обозначить, что ограничение разработано для этого контекста. -
applyTo()
устанавливает атрибутCrudEntityContext.readPermitted
в соответствии с политикой сущностей, определенной для текущего пользователя.
-
-
Когда
DataManager
загружает сущность, он создает экземплярCrudEntityContext
, устанавливает атрибутentityClass
и вызываетAccessManager.applyConstraints()
. После этого он анализирует значение атрибутаCrudEntityContext.readPermitted
и либо продолжает загрузку сущности, либо прерывает операцию.
При таком подходе точки авторизации полностью отделены от информации, необходимой для принятия решений об авторизации. В приведенном выше примере точка авторизации находится в ядре фреймворка, в то время как код, определяющий результат авторизации, находится в дополнительном модуле безопасности. Подобным образом вы можете определить дополнительное ограничение для того же CrudEntityContext
в вашем приложении, чтобы повлиять на стандартный процесс авторизации DataManager
.
Проверка разрешений в коде приложения
В предыдущем разделе объясняется, как авторизация работает в коде фреймворка. Вам может потребоваться воспроизвести в своем коде решения фреймворка об авторизации для того, чтобы проверить, какие объекты доступны текущему пользователю. Для этого необходимо создать экземпляр соответствующего AccessContext
, передать его в метод AccessManager.applyRegisteredConstraints()
, а затем проанализировать состояние контекста. Данная техника продемонстрирована на примерах ниже.
Пример проверки, имеет ли текущий пользователь право читать сущность Customer
:
@Autowired
private AccessManager accessManager;
@Autowired
private Metadata metadata;
public boolean checkCustomerReadPermitted() {
MetaClass metaClass = metadata.getClass(Customer.class);
CrudEntityContext accessContext = new CrudEntityContext(metaClass);
accessManager.applyRegisteredConstraints(accessContext);
return accessContext.isReadPermitted();
}
Пример получения всех доступных пользователю экранов:
@Autowired
private AccessManager accessManager;
@Autowired
private ViewRegistry viewRegistry;
public List<String> getPermittedViews() {
return viewRegistry.getViewInfos().stream()
.map(ViewInfo::getId)
.filter(screenId -> {
UiShowViewContext accessContext = new UiShowViewContext(screenId);
accessManager.applyRegisteredConstraints(accessContext);
return accessContext.isPermitted();
})
.collect(Collectors.toList());
}
См. также пример проверки специальной политики.
Некоторые общие классы контекстов, которые могут потребоваться для проверки разрешений пользователя:
-
CrudEntityContext
- контекст проверки политики операций сущностей. -
EntityAttributeContext
- контекст проверки политики атрибутов сущностей. -
UiShowScreenContext
- контекст проверки политики экранов. -
UiMenuContext
- контекст проверки политики меню. -
InMemoryCrudEntityContext
- контекст проверки предикатной политики.