Внешняя авторизация изнутри - сайт и чужой каталог учётных записей
Разбираем, что означает отметка о внешнем источнике у учётной записи и на чём держится контракт сайта с чужим каталогом. Дальше смотрим три режима этого контракта, первый вход сотрудника и обратную операцию - отвязку записи.
Механика
Внешний источник учётной записи - это её поле EXTERNAL_AUTH_ID. Пустое
значение означает обычную запись сайта, заполненное - что за проверку пароля
отвечает не сайт. Состав самой локальной записи, её поля и пароль разбираются в
подтеме про импорт.
Контракт с каталогом распадается на три независимых механизма: проверку пароля, импорт записей и единый вход. Они включаются по отдельности, и путаница начинается там, где включённым считают сразу весь набор.
Проверка пароля устроена просто: сайт отдаёт каталогу логин и пароль из формы входа и получает ответ. Локальный пароль записи в этой проверке не участвует вовсе, даже когда он у записи есть.
Список доступных источников платформа собирает событием OnExternalAuthList.
Оттуда же берётся выпадающий список в форме правки пользователя, и свой источник
появляется там только после ответа обработчика.
Саму проверку учётных данных выполняет обработчик события OnUserLoginExternal.
Он получает логин с паролем и решает, принадлежит ли сотрудник этому источнику. В
нём же задаётся признак store_password, и значение N запрещает платформе
сохранять пароль на стороне сайта.
При включённом хранении платформа обновляет хеш пароля записи после каждого успешного входа. В журнале событий это выглядит как смена пароля пользователем при каждом обращении сотрудника к сайту.
Первый вход без настроенного импорта проходит по своему пути. Записи на сайте ещё нет, каталог пароль подтвердил, и платформа заводит пользователя сама. Он получает логин из каталога, отметку внешнего источника и группы по умолчанию, а не группы своего отдела.
После успешного входа срабатывает событие OnAfterUserAuthorize. На него вешают
дополнение записи данными каталога: должность, телефон, подразделение, состав
групп. Именно здесь чаще всего затираются поля, которые правили руками в админке
сайта.
Удаление сотрудника из каталога сайт сам по себе не замечает. Запись остаётся на месте с правами и группами, а вход прекращается лишь при отказе каталога в проверке пароля. Отдельная опасность - запись, у которой отметку источника когда-то сняли, а пароль оставили рабочим.
Недоступность каталога делит пользователей сайта ровно на две части. Записи с заполненной отметкой источника войти не могут: проверять их пароль стало некому. Записи с пустой отметкой продолжают входить обычным паролем сайта.
Шаги
- Посмотреть отметку внешнего источника у спорной записи и понять, какое подключение за неё отвечает.
- Проверить по настройкам модуля, какие из трёх режимов контракта включены на этом сайте сейчас.
- Пройти путь первого входа сотрудника и убедиться, что запись создаётся с нужными группами.
- Проверить поведение сайта при недоступном каталоге и заранее оставить себе локальный вход администратора.
- Для разрыва связи снять отметку источника и задать пароль, а не удалять учётную запись.
Код
Смотрим отметку у учётной записи:
$res = CUser::GetList('ID', 'ASC', ['LOGIN_EQUAL' => 'i.ivanov'], ['FIELDS' => ['ID', 'LOGIN', 'ACTIVE', 'EXTERNAL_AUTH_ID']]);$row = $res->Fetch();// пустое поле - обычная запись сайта, заполненное - запись чужого источникаprintf("%s внешний=%s\n", $row['LOGIN'], $row['EXTERNAL_AUTH_ID'] ?: '(нет)');// логин записи и логин в каталоге совпадать не обязаны: связь держит это полеПоле отвечает на главный вопрос разбора: кто проверяет пароль этого сотрудника. Группы, права и активность записи к выбору источника отношения не имеют.
Считаем записи по источникам:
$res = CUser::GetList('ID', 'ASC', [], ['FIELDS' => ['ID', 'EXTERNAL_AUTH_ID']]);$bySource = [];while ($row = $res->Fetch()) { $key = $row['EXTERNAL_AUTH_ID'] ?: 'локальные'; // LDAP#7 - подключение с номером 7 $bySource[$key] = ($bySource[$key] ?? 0) + 1;}print_r($bySource); // __controller - служебные записи контроллера, а не люди// [локальные] => 3 [LDAP#1] => 412 - на сайте три записи с локальным паролемСводка показывает, сколько человек на сайте зависит от чужого каталога и сколько записей входит собственным паролем сайта.
Проверяем, какие режимы включены:
use Bitrix\Main\Loader;
Loader::includeModule('ldap');echo COption::GetOptionString('ldap', 'use_ntlm', 'N'), "\n"; // единый вход по доменуecho COption::GetOptionString('ldap', 'ntlm_varname', 'REMOTE_USER'), "\n";// имя переменной веб-сервера, в которой приходит распознанный доменный логин// проверка пароля и периодический импорт задаются в самом подключении к каталогуТри режима задаются в разных местах настроек. Выключенный импорт не мешает входу сотрудника, а выключенный единый вход не мешает проверке пароля в каталоге.
Объявляем свой источник авторизации:
AddEventHandler('main', 'OnExternalAuthList', 'addOwnAuthSource');function addOwnAuthSource() { // ID попадёт в поле записи, NAME - в список источников формы пользователя return ['ID' => 'hr_directory', 'NAME' => 'Каталог отдела кадров'];}// источников бывает несколько: каждый обработчик добавляет в список свойБез этого обработчика источник не появится ни в форме пользователя, ни в фильтре списка. Идентификатор источника потом менять нельзя: он записан в каждой привязанной к нему учётной записи.
Проверяем пароль на стороне источника:
AddEventHandler('main', 'OnUserLoginExternal', 'checkOwnAuthSource');function checkOwnAuthSource(&$arArgs) { $arArgs['store_password'] = 'N'; // хеш пароля на сайте не сохраняем if (ownDirectoryAccepts($arArgs['LOGIN'], $arArgs['PASSWORD'])) { $arArgs['EXTERNAL_AUTH_ID'] = 'hr_directory'; // сотрудник признан своим }}Обработчик получает данные формы по ссылке и дописывает в них признак источника. Непроставленный признак означает, что сотрудник этому каталогу не принадлежит, и вход продолжается обычным путём.
Смотрим, что приходит после авторизации:
AddEventHandler('main', 'OnAfterUserAuthorize', 'syncOnAuthorize');function syncOnAuthorize($arUser) { // состав массива смотрят такой записью один раз, потом читают нужные ключи file_put_contents(__DIR__ . '/auth.log', print_r($arUser, true), FILE_APPEND); // сюда вешают дополнение записи данными каталога: должность, телефон, отдел}Событие срабатывает после каждого успешного входа, а не только после первого. Тяжёлый запрос к каталогу в этом обработчике добавляется ко времени входа каждого сотрудника.
Отвязываем запись от внешнего источника:
$user = new CUser();$user->Update(265, [ 'EXTERNAL_AUTH_ID' => '', // запись становится обычной записью сайта 'PASSWORD' => 'Nc7-xK2p-qW', // без пароля входить будет нечем 'CONFIRM_PASSWORD' => 'Nc7-xK2p-qW',]);echo $user->LAST_ERROR; // пустая строка означает успешное сохранение// групп, прав и авторства записи правка отметки источника не затрагиваетОтвязка сохраняет номер пользователя, его права и историю, а удаление с повторным импортом рвёт авторство сообщений, задач и загруженных файлов.
Снимаем привязку при потерянном доступе:
-- крайняя мера: каталог недоступен, а локального администратора на сайте нетSELECT ID, LOGIN, EXTERNAL_AUTH_ID FROM b_user WHERE LOGIN = 'admin';UPDATE b_user SET EXTERNAL_AUTH_ID = NULL WHERE LOGIN = 'admin';-- пароля у отвязанной записи нет: следом идёт восстановление пароля по почтеПрямая правка базы - осознанное исключение из правил, и применяют его только там, где штатных путей не осталось вовсе.
Ограничения
Отметка источника хранится у записи, а не у подключения к каталогу. Пересоздание подключения меняет его номер, старые записи остаются привязанными к прежнему, и следующий вход заводит сотрудникам новые записи.
Один контроллер домена на подключение - штатное ограничение модуля каталога. Отказоустойчивую пару из двух контроллеров настройками сайта не собрать.
Записи из каталога ничем не отличаются от обычных по правам доступа. Отметка источника не даёт им ни привилегий, ни ограничений: права по-прежнему считаются по группам сайта.
Отдельного журнала синхронизации с каталогом платформа не ведёт. Расхождения разбирают по журналу событий сайта, по сообщению в конце ручного импорта и по консольным запросам к самому каталогу.
Единый вход по домену устроен поверх этого контракта и настраивается отдельной страницей. Он не заменяет проверку пароля, а только избавляет сотрудника от ввода пароля в форме.
Типичные проблемы
В журнале событий при каждом входе появляется смена пароля.
Источник авторизации сохраняет пароль на сайте, и хеш переписывается после каждой успешной проверки. В обработчике внешнего входа признак хранения пароля выставляют в значение отказа.
Сотрудник заведён до подключения каталога и входит только старым паролем.
У его записи пустая отметка внешнего источника, и каталог для неё не спрашивается вовсе. Отметку проставляют правкой самой записи, а не удалением сотрудника с повторным импортом.
Каталог недоступен, и в админку не может войти вообще никто.
Все записи администраторов помечены внешним источником, и проверять их пароль стало некому. Одну локальную запись администратора держат нетронутой именно на такой случай.
После переезда на новый сервер каталога сотрудники приезжают новыми записями.
Отметка старых записей указывает на прежний номер подключения, и связь по ней уже не находится. Номер в отметке существующих записей меняют на новый до первого входа сотрудников.
Поля сотрудника, поправленные в админке, возвращаются к прежним значениям.
Запись дополняется данными каталога при входе, а не только при плановом импорте. Свои поля сайта держат отдельно от полей, которые приходят из каталога организации.
Модуль каталога удалён, а вход всё равно отдаёт ошибку подключения.
Внешний источник объявлен обработчиком события в файле начальной загрузки, а не самим модулем. Обработчики внешней авторизации ищут именно в этом файле, а не в списке установленных модулей.
Частые вопросы
Как отключить AD/LDAP, если каталог недоступен и войти нельзя?
Снять отметку внешнего источника у своей записи и задать ей пароль. При полной потере доступа поле правят прямо в таблице пользователей, а пароль восстанавливают штатно по почте.
Что означает значение LDAP#1 в поле внешней авторизации?
Это ссылка на подключение к каталогу с номером один, а не на сам домен. При заведении второго подключения у новых записей окажется другое значение.
Как включить вход по домену для пользователей, заведённых до подключения каталога?
Проставить им отметку внешнего источника при логине, совпадающем с логином в каталоге. Удалять запись и импортировать заново не нужно: авторство и история привязаны к её номеру.
Можно ли указать два контроллера домена для отказоустойчивости?
Нет, подключение принимает один сервер каталога. Отказоустойчивость собирают на стороне сети, а не настройками сайта.
Почему после авторизации данные пользователя обновляются сами?
Внешний вход дополняет запись данными каталога, а не только плановый импорт. Если правки затираются, их переносят в поля сайта, которые из каталога не приходят.
Смежное
- LDAP и Active Directory - оглавление подтемы
- Пользователи, группы и права доступа - устройство учётных записей
- Синхронизация с Active Directory: подключение, отбор, группы - настройка импорта и отбора сотрудников
- Единый вход по доменной учётной записи: настройка NTLM - режим входа без ввода пароля
- LDAP не подключается: причины по убыванию частоты - если каталог перестал отвечать
- Вход через внешний сервис: регистрация приложения, привязка, первый вход - другой механизм со своей таблицей привязок
- Импорт пользователей в 1С-Битрикс - состав локальной учётной записи
- Забыт пароль и потерян доступ в админку: что делать - если локального администратора не осталось
- Обработчик события: регистрация, аргументы, отмена действия - как подключается обработчик внешнего входа
- Журнал событий: чтение, очистка, свои записи - где видно записи о смене пароля