Заполнение кастомных атрибутов сущности User

Всем привет!
Есть задача - заполнять кастомные вычисляемые атрибуты сущности User для того чтобы потом использовать их в предикатах Row-level Policy. Предполагаю, что эти атрибуты должны заполняться после прохождения аутентификации и авторизации. Куда можно встроить вызов кода для заполнения этих атрибутов.
Спасибо!

Нашёл такое событие AuthenticationSuccessEvent. Т.е. можно написать обработчик:

@Component
public class UserEventListener {

    @EventListener
    public void onAuthenticate(AuthenticationSuccessEvent event) {
        // process event
    }
}

Но есть одно неудобное обстоятельство. Мне нужно в этом обработчике выполнять запросы к БД, причем с учетом прав текущего пользователя. Т.е. нужно обращаться к dataManager. Но в этот момент в SecurityContext ещё не положен новый объект Authentication

А что такое “вычисляемые атрибуты сущности User”?
Вы расширили сущность User и добавили неперсистентные (transient) атрибуты?
Или эти вычисляемые атрибуты персистентные и хранятся в БД?
Если они неперсистентные, то заполнять их надо в обработчике события EntityLoadingEvent.
Только так вы гарантируете себе то, что где бы в приложении не загружался из БД экземпляр User - эти неперсистентные атрибуты будут заполнены, причем до применения Row-level Policy.

@andrey_vb, спасибо за ответ!
Всё верно, вычисляемыми атрибутами я назвал неперсистентные (transient) атрибуты объекта User. Но по смыслу мои атрибуты не являются атрибутами объекта User. Я размещаю их там только для того, чтобы использовать в JPQL и Groovy предикатах при описании Row-Level Roles. Там я могу к ним обратиться через конструкцию :current_user_<attribute>. Эти атрибуты являются скорее атрибутами сессии пользователя. Я не знаю, можно ли обращаться из предикатов JPQL и Groovy к чему-то ещё, помимо текущей сущности и текущего пользователя.
Так, например, мне не нужно заполнять эти атрибуты при получении списка пользователей в админке. Т.е. заполнение их в EntityLoadingEvent будет лишней нагрузкой (вычисление их доволньно затратно и требует обращения к БД).
В Cuba был такой объект UserSessionSource. Там хранились атрибуты сессии, в том числе текущий пользователь. Есть ли аналог этого объекта в Jmix с возможностью обращения к нему из JPQL и Groovy навроде того, как это сделано в Cuba через конструкцию :session?

Попробуйте событие org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent

Судя по реализации
io.jmix.securityui.authentication.LoginScreenSupport#onSuccessfulAuthentication
оно публикуется с уже установленным security context.

В Jmix аналога этой возможности пока что нет.

А в планах числится замена атрибутам сессии?
Мы тоже их активно пользовали.
Очень удобно и приятно, что их можно было прокинуть в Groovy и тот же JPQL в стандартном фильтре.

@albudarov, спасибо за совет!
Но, насколько я понял событие InteractiveAuthenticationSuccessEvent будет генерироваться только в UI-модуле. Мне нужно, чтобы код работал везде, в том числе и в REST-модуле и в тестах.
Я подумал, что я вполне обойдусь событием AuthenticationSuccessEvent и UnconstrainedDataManager -ом.

Реализовал макет своей идеи. Завёл атрибут в классе User:

@Transient
protected List<String> tagsToRead = new ArrayList<>();

Заполнил его тестовыми данными:

@Component
public class UserEventListener { 

    @EventListener
    public void onAuthenticate(AuthenticationSuccessEvent event) {
        User user = (User)event.getAuthentication().getPrincipal();
        user.setTagsToRead(List.of("T1","T2"));
    }
}

Сконфигурировал Row-Level Policy:

  • Type: JPQL
  • Action: Read
  • Where clause: t.name in :current_user_tagsToRead
  • Join clause: ,IN ({E}.tags) t

Назначил пользователю нужную роль с этой Policy. Запустил тест:

    @Test
    public void testRead() {
        systemAuthenticator.withUser("user1", ()->{
            callRead();
            return null;
        });    
    }

И оно работает! Вычитывает из БД только нужные сущности! :+1:
Всем большое спасибо за помощь!

1 симпатия

Нет, InteractiveAuthenticationSuccessEvent посылается и при аутентификации в REST.

Константин, спасибо!