Авторизация
В этом разделе рассматриваются темы, связанные с выполняемым фреймворком контролем доступа.
Проверки доступа к данным
В данной таблице объясняется, как разрешения и ограничения на доступ к данным используются различными механизмами фреймворка.
Операции с сущностями |
Атрибуты сущностей |
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-компонент отображается, но является пустым. Кроме того, для любого действия, основанного на классе ItemTrackingAction
, можно указать определенную операцию с сущностью, используя метод setConstraintEntityOp()
. Так, действие будет доступным только если операция разрешена для выбранного экземпляра сущности.
5) REST-запросы выполняют только чтение данных.
6) Параметры и результаты методов REST-сервисов не проверяются на соответствие политикам уровня строк. Поведение сервиса в отношении безопасности уровня строк определяется тем, как он читает и сохраняет данные, например, использует ли он "DataManager" или "UnconstrainedDataManager".
Ограничения доступа
В этом разделе кратко объясняется, как работает авторизация 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 WindowConfig windowConfig;
public List<String> getPermittedScreens() {
return windowConfig.getWindows().stream()
.map(WindowInfo::getId)
.filter(screenId -> {
UiShowScreenContext accessContext = new UiShowScreenContext(screenId);
accessManager.applyRegisteredConstraints(accessContext);
return accessContext.isPermitted();
})
.collect(Collectors.toList());
}
См. также пример проверки специальной политики.
Некоторые общие классы контекстов, которые могут потребоваться для проверки разрешений пользователя:
-
CrudEntityContext
- контекст проверки политики операций сущностей. -
EntityAttributeContext
- контекст проверки политики атрибутов сущностей. -
UiShowScreenContext
- контекст проверки политики экранов. -
UiMenuContext
- контекст проверки политики меню. -
InMemoryCrudEntityContext
- контекст проверки предикатной политики.