Проблемы с windows-аутентификацией в rest-api

Добрый день.

Настроил windows-аутентификацию, как описано в документации
Вижу, что есть 2 проблемы:

  1. Вижу, что EqualsFilter в LdapAuthController ломает логин пользователя pk\sptest10
    image
    Кажется, что в этом методе мы вообще ожидаем “очищенный” от домена логин пользователя, что, как мне кажется, странно.
  2. Поправьте меня, если я не прав, но текущая реализация вообще не предусматривает “многодоменности”. Можно запросить это реализовать? К тому реализация этого уже есть в ldap-addon.

Добрый день.

Не очень понятно, чего вы хотите добиться и в чем проблема. Если вам нужна многодоменность, можете попробовать установить ldap add-on. Если хотите предложить новую функциональность - заведите issue в нашем GitHub.

Какую проблему вы решаете и что вам мешает? Из кода не совсем понятно.

Нужна. ldap add-on уже установлен.

Мне необходимо аутентифицировать пользователей разных доменов (одного леса) в rest-api.

Собственно метод здесь - это метод аддона rest-api в отладчике, который формирует фильтр для поиска пользователя в домене при включенной ldap-аутентификации. И проблемы здесь 2, которые я описал выше.

То есть, у вас есть REST API и вы хотите доступ к нему прикрыть LDAP аутентификацией, возможно с OAuth2? И у вас это не получается, потому что ldap-add-on отрезает домены от имени пользователя, так?

У меня установлен addon rest-api.
Пользователи ходят в систему с использованием ldap-аутентификации из ldap-addon.

Так устроена аутентификация в rest-addon. Так?

Нет.

  1. Если я указываю корневой домен в качестве контекста подключения, то поиск пользователя будет выполнен именно в нем, без учета домена, из которого пришел пользователь. Для примера: есть корневой домен root.com и есть домен леса domain.root.com. Пользователь с логином domain\user1 будет искаться в в домене root.com
  2. Более того поиск пользователя с логином domain\user1 будет выполнен (sAMAccountName=) в домене не как user1, а как domain\user1, а пройдя через метод, показанный выше, вообще будет “попорчен” - domain\5сuser1

Вопрос: зачем вы ходите через LDAP в REST API, если вы можете ходить через CUBA авторизацию? У вас же пользователи синхронизированы. По идее, вам нужно отключить cuba.rest.ldap.enabled параметр и использовать имена пользователей из CUBA.

А конкретно по вашему замечанию:

Действительно, метод buildPersonFilter в контроллере REST API строит слишком примитивный запрос к Ldap, в котором, похоже, не учитывается, ваш случай с мульти-доменами.

Я создал issue для этого. Вы можете, конечно, взять исходники REST API контроллера и пропатчить их под свои нужды. Думается, что все, что вам надо сделать, это поправить метод

    protected String buildPersonFilter(String login) {
        AndFilter filter = new AndFilter();
        filter.and(new EqualsFilter("objectclass", "person"))
                .and(new EqualsFilter(ldapUserLoginField, login));
        return filter.encode();
    }

И сделать свой собственный фильтр, который будет учитывать вашу структуру домена.

Подождите, может я чего-то совсем не понимаю. Вот есть у нас условно говоря некоторое корпоративное приложение на cuba (c vaadin интерфейсом). Пользователи ходят в него через доменные учетные записи. Опять же условно мы хотим разработать мобильное приложение, которое будет общаться с системой через rest-api. Для авторизации пользователей я по-прежнему предполагаю использовать те же доменные учетки.

Вот опять же не понимаю. Разве для логина пользователя не должен быть проверен пароль пользователя? Должен. А если учетки доменные, то каким образом мы будем их аутентифицировать штатным кубинским аутентификаторм? У меня для них пароли не заданы… Они доменом проверяются…

Попробую еще раз.
Метод в каком-то смысле не рабочий даже для одного домена. На вход он может принимать только очищенный от домена логин. Т.е. пользователь должен быть заведен без домена. Ни так domain\user1, ни так user1@domain.com пользователь в домене найден не будет. Т.е. чтобы это работало для одного домена необходимо вводить пользователя вот так: user1. Но при этом что базовая лдап-аутентификация в платформе, что ldap-аутентификация с использованием jespa, что с использование нового ldap-аддона предполагают, что пользователь будет введен в систему с доменом.

Для мультидоменов даже исправление этого метода не решит вопроса. Нужно еще и цель для запроса правильную выбрать из леса доменов.

Давайте по порядку:

  1. У вас установлен и настроен LDAP Add-on, теперь пользователи могут в стандартном окне логина вводить свое имя и пароль, на сервере это перенаправляется в LDAP, там происходит авторизация, и все хорошо.
  2. Теперь вы хотите, чтобы пользователи так же могли ходить через REST. Т.е. REST Add-on должен перенаправлять имя пользователя и пароль ТОМУ ЖЕ САМОМУ механизму, который УЖЕ настроен через LDAP Add-on и которым пользуется стандартный GUI.
  3. Следовательно, не нужно отдельно настраивать REST API Add-on для работы с LDAP, указывать ему сервер и прочие вещи, оно же уже все настроено в LDAP Add-on.

Наша задача - задействовать в REST те же механизмы, что и в стандартном GUI.

Я быстренько написал прототип, который решает эту задачу. Нужно подменить CubaUserAuthenticationProvider и для пользователей, которые указаны в cuba.web.standardAuthenticationUsers ходить через стандартный механизм CUBA, а для всех остальных - использовать LDAP.

Для этого я указал в rest-dispatcher-spring в модуле web нового провайдера - CubaLdapUserAuthenticationProvider. А сам провайдер сделан довольно просто, по мотивам LdapAddonLoginProvider, который используется в GUI. Он создает TrustedClientCredentials на основе переданного имени пользователя и пароля, вместо стандартных LoginPasswordCredentials. А первые уже проверяются LDAP механизмами.

Это всего лишь идея, так что не судите строго. Пример приложения в аттаче, быстрая проверка показывает, что все нормально работает, можно логиниться в UI и получать токен через REST API, но тщательно не проверял. Дальше там можно развивать тему, проверять, включено ли свойство ldap.addonEnabled и т.д.

По идее, если LDAP Add-on нормально по имени пользователя и паролю логинит всех ваших мультидоменных пользователей через GUI, то и в этом приложении они должны нормально логиниться ровно так же через REST.
Пробуйте получить токен сначала:
http://localhost:8080/app/rest/v2/oauth/token с передачей всех заголовков, как в документации
А потом можете позвать сервис: http://localhost:8080/app/rest/v2/services/sessionplanner_SessionService/findSessionsBySpeaker?speakerEmail=jane.doe@sessions.com с нужным токеном.

Пример сделан на базе приложения из QuickStart, база - PostgreSQL.

session-planner.zip (98.6 КБ)

1 симпатия

Это не “всего лишь”, а решение проблемы, при чем очень быстро и точно.
Я проверил, все отлично работает. Огромное спасибо.
Может быть имеет смысл добавить данное решение в документацию?

Андрей, а не подскажите, нет среди ваших (кубинских) семплов референсной реализации аутентификации для клиента rest-api? Т.е. чтобы учитывалось:

  1. первоначальное получение токена, если его нет при первоначальном запросе
  2. проверка не истек ли токен и его обновление при очередном запросе с помощью refresh_token
  3. корректно обработать ответ о невалидности токена, если предположим сервер рестартовали или истек refresh_token и запросить новый (п.1)

Пока что завели тикет, будем обсуждать. Мне кажется, нужно более правильно сделать комбинацию add-ons, чтобы LDAP аккуратнее подменял авторизацию.

По идее, это должен уметь делать наш frontend client. Можете сгенерировать его по документации и заглянуть внутрь библиотек. Явного примера именно для этой цели мы не писали.

1 симпатия

Спасибо за идею - на досуге я попробую.
Я просто сгенерировал java-клиента с помощью openapi и требуемых возможностей не обнаружил…

1 симпатия