Авторизация

В этом разделе рассматриваются темы, связанные с выполняемым фреймворком контролем доступа.

Проверки доступа к данным

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

Операции с сущностями

Атрибуты сущностей

JPQL-политика уровня строк (1)

Предикатная политика уровня строк READ (2)

Предикатная политика уровня строк CREATE/UPDATE/DELETE

DataManager

Да (3)

Нет

Да

Да

Да

UnconstrainedDataManager, EntityManager

Нет

Нет

Нет

Нет

Нет

UI-компоненты, связанные с данными

Да

Да

- (4)

- (4)

- (4)

REST API /entities

Да

Да

Да

Да

Да

REST API /queries

Да

Да

Да

Да

- (5)

REST API /services

Да

Да

- (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".

Ограничения доступа

В этом разделе кратко объясняется, как работает авторизация 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());
}

См. также пример проверки специальной политики.

Некоторые общие классы контекстов, которые могут потребоваться для проверки разрешений пользователя: