Перейти к основному содержимому

Группы и схема атрибутов

Актуально для: SimpleTwo 0.9.x, directory 0.2 · Проверено: 24.09.2026

Группы, адресные книги, почтовые алиасы и схема атрибутов живут в отдельной серверной роли identity — своя база в Postgres, свой хост. Всё это настраивается в консоли координатора: люди, гости, группы и боты — в разделе «Пользователи», вход, приложения и синхронизация — в разделе «Безопасность». В консоли платформы карточка Directory (Настройки → Directory) показывает только состояние роли: хост, версию, публичное имя, сверку учёток и ссылку на портал /me (с 26.09.2026, версии 0.9.286 и новее; раньше те же блоки были и в карточке).

Когда карточка пустая​

«no directory host yet» — хост роли не зачислен. Зачисление как у календаря: Servers → роль identity, обязателен приватный адрес (по нему координатор и службы обращаются к роли). Публичное имя нужно для входа через directory, портала /me и консоли /admin. С ним сервис сам получает сертификат на 443. Поле «Способ доступа»: direct — имя указывает на собственный публичный адрес хоста; nat — на адрес организации, чей NAT пробрасывает 443 на этот хост. DNS-запись для имени заводите сами. Клиентам имя объявляется, только когда координатор до него достучался; строка под заголовком карточки показывает последнюю проверку: куда указывает имя, объявлено ли оно и почему нет. Хранилище роли — файлы встроенного SQLite на её же хосте (/var/lib/simpletwo/directory, с directory 0.76.0): отдельная база на роли postgres ей не нужна. Установка, где данные directory уже лежали в PostgreSQL, переносит их сама при первом старте 0.76.0 и больше к той базе не обращается.

Виды групп​

kindкто владеет членствомзачем
syncedпоставщик идентификации (ADFS/AD) — пишется при каждом входе, только чтениероли, шлюзы, политика адресной книги
unitправило по атрибутам: компания, отдел, площадка@dept, календарь отдела, аудитория правил
managedсписок из консоли или правилодежурные, ревьюеры, участники проекта
resourceправило entity: resourceпереговорки в пикере бронирования
publishedсписок; видимость federated заложена, включается фазой федерации«наши контакты в X»
personalсам человек; видит только онизбранное, «моя команда»
systemплатформа; не удаляетсяролевые группы

Kind нельзя сменить после создания: группа другого вида — это другая группа.

Правило динамической группы​

Состав динамической группы вычисляет directory сама: координатор присылает ей каждое событие каталога вместе со всеми атрибутами профиля, правило переоценивается для этого человека, и строки состава дописываются или снимаются. Смена правила пересчитывает группу по всем, кого directory уже знает; правило, сохранённое до первого события каталога, пустое до ночной сверки — об этом пишется в журнал. Руками такой состав не правится.

rule — словарь «ключ: значение», все пары должны совпасть. Ключи — то, что учётная запись действительно несёт: атрибуты из схемы плюс user, display_name, is_guest, entity. entity принимает person, service, resource, список через запятую, !resource («все, кроме переговорок») или any.

Ключ, которого никто не несёт, отклоняется при сохранении с именем ключа и списком существующих. Это не придирка: 08.09.2026 правило приветствия сузили на is_room: false — такой переменной не было, правило перестало совпадать с кем бы то ни было и десять дней молча никого не приветствовало, оставаясь включённым в консоли.

Кто может обращаться к группе​

writers — кто вправе упомянуть группу, написать на её адрес, позвонить в неё: user:<имя>, role:<имя>, group:<id>. Пусто — все, кто группу видит. Потолка на охват нет: @sales на четыре тысячи человек — это четыре тысячи уведомлений, и контроль здесь — кто может, а не сколько задело. owners могут править состав, writers и join без прав администратора. address делает группу списком рассылки — любого вида, включая динамические.

Проверяет это messaging в момент отправки: сообщение с @handle группы, к которой отправитель обращаться не вправе, отклоняется целиком с кодом group_not_addressable — оно не уходит «как текст» с тихо потерянным упоминанием. Тем, к кому обращаться можно, @handle считается упоминанием каждого участника: уведомление на уровне «только упоминания», пробой «не беспокоить», автоподписка на ветку. Копию группы messaging получает от directory после каждой правки; между правкой и копией — секунды, а при недоступном messaging изменение доедет со следующей правкой этой группы.

В правилах автоматизации группа — ещё одно условие when: group: <id или @handle> рядом с entity сужает правило до участников группы на момент события.

Владельцы и самообслуживание​

owners — люди (user:<имя>) или роль (role:<имя>), которые ведут группу без прав администратора: меняют название, writers, join и default_convs, добавляют и удаляют участников, ставят срок членства (expires_at — подрядчик до пятницы, подмена на дежурстве). Вид группы, правило, источник, адрес и сам список владельцев остаются за администратором — они меняют то, чем группа является, а не кто в ней.

join решает, как в группу попадают: closed (по умолчанию) — только владелец добавляет; approval — человек подаёт заявку, владельцы, названные по имени, получают уведомление и решают в очереди заявок; open — любой, кто видит группу, входит сам. Выйти из группы может любой её участник. Истёкшее членство снимается само в пределах десяти минут и попадает в аудит как group.member.expire.

default_convs — беседы, куда новый участник добавляется при появлении в группе: любым путём — владельцем, заявкой, синхронизацией из AD. Несуществующая беседа пропускается с записью в журнал, группа при этом обновляется.

Схема атрибутов​

Каждый атрибут — одно определение: kind (text, number, date, birthday, phone, address, url, choice, multichoice, user, bool), source — ровно один писатель (self, admin, claim, ldap, hrms, scim), visibility (everyone, unit, managers, admins, self), поиск, обязательность, список значений для choice, pattern. Имена для LDAP и SCIM подставляются сами с нашим префиксом (x-simpletwo-…, …:extension:simpletwo:2.0:User:…), если вы не указали стандартные.

Что схема отказывает, чтобы остаться схемой:

  • смену kind — расширение text до choice молча обесценило бы все сохранённые значения; отзовите определение и заведите новый ключ;
  • удаление choice, которое может быть в значениях;
  • удаление определения — только Retire: значения остаются читаемыми администраторам, ключ зарезервирован, Restore возвращает;
  • вычисляемые атрибуты («ФИО из двух полей») — хранимая производная протухает без предупреждения.

Определение, добавленное здесь, тут же годится ключом в правилах групп и автоматизаций.

Базовый состав. Схема поставляется с базой по SCIM 2.0 (RFC 7643): ядро (никнейм, фото, имя по частям — given_name, family_name, middle_name, телефоны, должность, предпочитаемый язык), расширение Enterprise (department, company — в SCIM organization, division, employee_number, cost_center, manager) и расширение платформы (office, status, hire_date, birthday, два руководителя из HRMS, nativefullname — ФИО на родном языке). LDAP-имена стандартные, где они есть; остальное — под префиксом x-simpletwo-…. Базу каталог сеет при старте и никогда не перезаписывает ваши правки. Фото, когда у определения photo источник claim, каталог берёт из утверждения ID-токена (у ADFS — thumbnailPhoto) при каждом входе через поставщика и хранит, как фото из LDAP; проход LDAP по фото при этом отступает.

Группы из Active Directory целиком​

Вход через ADFS приносит только имена групп входящего человека. Чтобы каталог был полным до первого входа — с владельцем у каждой группы и полным составом, — включите источник LDAP: в консоли, в разделе «Безопасность → Синхронизация», карточка «LDAP-источник — задачи справочника» → «Изменить источник» → group_base_dn (например OU=Groups,DC=corp,DC=example; фильтр по умолчанию (objectClass=group)). Раз в шесть часов и по кнопке «Синхронизировать группы» directory читает объекты групп и людей постраничными поисками, сопоставляет member учётным записям — по userPrincipalName, затем mail, затем sAMAccountName; несопоставленные считаются, а не угадываются, — назначает managedBy владельцем и пишет группу под тем же id, что и вход: группа AD, узнанная дважды, остаётся одной группой. Две группы AD, имена которых сводятся к одному id (регистр, знаки, обрезка до 48 символов), различаются по objectGUID: id остаётся у группы, которая его уже носит, вторая получает постоянный суффикс из своего GUID. Группа, в которой ничего не изменилось, не перезаписывается. Строка последнего прохода на карточке показывает: сколько групп, записанных, членств, несопоставленных, владельцев — или ошибку.

Где работают задачи справочника. Проходы по группам, людям и фото и модуль SimpleOne HRMS принадлежат directory: directory — справочник и эмитент. Раньше их выполнял координатор, и его настройки переезжают сами: как только в установке развёрнут хост directory, координатор один раз передаёт ему подключение LDAP с паролем привязки, отметки инкрементального чтения, последние результаты, подключение SimpleOne с его секретом и настройки HRMS, а после ответа directory удаляет свои копии — у каждого секрета один владелец. Секреты в directory хранятся зашифрованными, как остальные секреты интеграций. После переезда карточки «Directory photos (AD / LDAP)» и «SimpleOne (HRMS)» в платформенной консоли пишут «Управляется в directory» и ведут в раздел «Безопасность → Синхронизация»; переключатель «разрешить всем загружать своё фото» остаётся на платформе, и directory получает его значение. Без directory (небольшая установка) задачи остаются на координаторе и настраиваются на его карточках, как прежде; то же — пока directory слишком старая и переезд не принимает, карточка говорит об этом.

Люди из каталога. На той же карточке — Sync people now и проход раз в шесть часов: каждая учётка здесь получает из каталога те атрибуты, которые в схеме directory объявлены с source: ldap и ldap_name (и только их — атрибут, которого в схеме нет, под именем AD не сохраняется), и руководителя — manager из DN превращается в имя учётки здесь. Состояние учётки (бит «отключена», accountExpires) на каждом проходе считается, а применяется только при включённом переключателе «применять состояние учётки»: это единственная часть прохода, которая может отключить человека, поэтому сначала посмотрите число расхождений. Изменения уходят обычными событиями каталога — в messaging и в проекцию людей directory, так что динамическая группа по department сдвигается тем же часом, что и AD. directory пишет изменения прямо в учётку-источник правды, а координатор получает их по ленте изменений учёток, как записи SCIM.

Уволенные. LDAP не сообщает об удалении, поэтому проход людей сам находит учётки, пришедшие из каталога (вход через ADFS), которых в каталоге больше нет, и показывает их на карточке. С включённым переключателем «применять состояние учётки» они блокируются. Если таких больше десятой части (или больше половины в маленькой организации) либо каталог не вернул никого, проход не блокирует никого и пишет причину — так выглядит сломанный base DN или фильтр, а не массовое увольнение. Локальные учётки и гости не затрагиваются.

Только изменения. С Active Directory проход людей читает только то, что изменилось с прошлого раза (по uSNChanged того же контроллера домена); целиком каталог читается при первом проходе, при смене контроллера, при изменении профиля или сопоставленных атрибутов и раз в сутки. Удалённых из AD проход видит сразу, если служебной учётке разрешено читать CN=Deleted Objects; иначе — на суточном полном чтении. Карточка пишет, каким было последнее чтение. Проход групп читает так же: только изменившиеся группы (изменение состава — это изменение группы), удалённую в AD группу убирает из directory по её следу в CN=Deleted Objects или на суточном полном чтении, переименованную — узнаёт по objectGUID. Удаляются только группы, которые записал сам этот проход, и не больше десятой части за раз.

Пароли на время сосуществования. Каталог никогда не отдаёт хеш пароля, поэтому на той же карточке есть переключатель «сквозная проверка пароля»: пока у учётки нет локальных учётных данных, directory проверяет пароль, привязываясь к каталогу от имени человека — только по ldaps://, профиль с ldap:// directory отклонит. Ничего не сохраняется: локальные учётные данные накапливаются по мере того, как люди их заводят, а карточка Directory показывает, у скольких их ещё нет — это число, по которому планируют день отключения каталога. Неверный пароль считается в блокировку так же, как локальный; недоступный каталог — не отказ, а сообщение «каталог не ответил», и в счётчик он не идёт. Сам переключатель — на карточке «LDAP-источник» раздела «Безопасность → Синхронизация»; профиль подключения хранится в одном месте — в directory, на карточке «LDAP-источник — задачи справочника» (до переезда задач — на карточке платформенной консоли, откуда он копируется в directory).

Фото из каталога. Фото сотрудника (thumbnailPhoto или атрибут, указанный в источнике) directory читает при его входе, для людей, изменения которых увидел инкрементальный проход, и по кнопке «Синхронизировать все фото», хранит у себя — не больше 512 КБ и только JPEG или PNG, проверенные по содержимому — и передаёт в messaging, который раздаёт аватары. Неизменившееся фото повторно не отправляется; фото, удалённое из каталога, удаляется и в directory, и в messaging. Фото есть только у учёток из каталога (вход через корпоративный провайдер); остальные загружают своё сами. Пока на платформе включено «разрешить всем загружать своё фото» или фото приходит в утверждении ID-токена, каталог фото не трогает.

SimpleOne HRMS. На той же вкладке — подключение к экземпляру SimpleOne (только https://, Basic или Bearer; пароль или токен хранится зашифрованным, и при смене адреса экземпляра его нужно ввести заново — секрет не уходит на новый хост; то же с паролем привязки LDAP) и модуль HRMS: путь к таблице сотрудников, таблица соответствия полей (attr_map), таблица графиков (schedule_map: имя графика → дни и часы), интервал и пробный режим. «Предпросмотр» ничего не пишет и показывает, сколько записей сопоставлено, что изменилось бы и какие имена графиков отвечает экземпляр; «Синхронизировать» работает только при включённом модуле и выключенном пробном режиме. Атрибуты, которые питают утверждения входа платформы, HRMS не пишет — они показаны в отчёте. Рабочие часы, в которые таблица графиков переводит график человека, записываются на учётку, и платформа передаёт их календарю.

Адресные книги​

Личные книги хранятся на сервере ради синхронизации между устройствами, но в консоли не показываются и через координатор не читаются — маршрута к чужой личной книге нет ни у кого. Книга организации (owner = org) — общая: внешние партнёры, поставщики, горячие линии; её правит администратор.

Импорт и экспорт. Книга принимает vCard (3.0 и 4.0) и CSV и отдаёт то же самое: POST …/books/{book}/import с телом файла, GET …/books/{book}/export?format=vcf|csv. Контакт здесь — указатель (адрес, номер, пользователь) плюс подпись и заметка, поэтому из визитки берутся EMAIL, TEL и X-SIMPLETWO-USER, FN становится подписью, NOTE — заметкой; всё остальное (ORG, ADR, фото) в отчёте считается как dropped_properties, а визитка без единого указателя попадает в refused с номером строки. Дубли по паре «вид + адрес» с тем, что уже в книге, считаются, но не пишутся; повторный импорт — в том числе собственного экспорта — ничего не создаёт. Идентификатор импортированного контакта — ключевой MAC от книги и указателя под секретом службы, так что по дампу базы нельзя проверять догадки об адресах и номерах; контакты, импортированные раньше, сохраняют прежние идентификаторы. CSV: заголовок kind,target,label,note,pinned в любом порядке, разделитель , или ;. В аудит попадают только счётчики.

Глобальная адресная книга: кто кого видит​

Кого человек находит в поиске, видит в списке контактов и кому может начать писать, решает роль directory по политикам адресной книги — так же, как это устроено в Exchange. Консоль координатора, вкладка «Пользователи», карточка «Адресная книга».

  • Список адресов — фильтр по каталогу. Условия складываются через «и»: участники перечисленных групп; вид учётки (member, guest, partner, bot, system, resource); значения атрибутов; «делит группу с читателем». Список без условий — вся организация.
  • Политика адресной книги — набор списков и группы, которым она назначена. Назначается только группам, не ролям.
  • Человек видит объединение списков всех политик своих групп. Если ни одна его группа политики не даёт, действует политика по умолчанию — встроенная «По умолчанию: вся организация».
  • Гость и пригласивший его сотрудник видят друг друга всегда, какие бы политики ни стояли.
  • Люди, с которыми человек уже в одной беседе, но которых нет в его списках, находятся только по имени: без должности, телефонов и остальной карточки.
  • Учётка со снятым флажком «Показывать в адресной книге» находится только по точному адресу.

Начать беседу можно с тем, кого адресная книга показывает полностью, или с тем, с кем человек уже в одной беседе; messaging спрашивает об этом directory и отвечает not_reachable. Кнопка «Что видит этот человек» показывает адресную книгу выбранного логина — проверяйте ею политику до того, как назначить её группе.

После обновления до версии с политиками сотрудники видят то же, что и раньше. Гости и партнёры, которым раньше роль ограничивала каталог, видят всю организацию, пока их группе не назначена политика: назначьте политику группе гостей (например, встроенную «Свои группы») сразу после обновления.

Ящики​

Ящики directory не ведёт. Любой ящик — личный, общий, ресурсный — и доступы к нему принадлежат почтовой роли. Она берёт людей и группы из directory и сама решает, у кого есть доступ к какому ящику. Календари и адресные книги принадлежат учётке человека.

Политика учётных данных и служба поддержки​

В консоли, в разделе «Безопасность → Требования ко входу», блок «Политика учётных данных»: минимальная длина, запрет повтора последних N, срок действия в днях (по умолчанию 0 — бессрочно: истечение без признаков компрометации только вредит, но аудитор может потребовать), блокировка после N промахов на M минут и проверка по списку утёкших паролей. Проверка локальная: встроенный список самых ходовых паролей есть всегда, а на хосте directory можно указать DIR_BREACH_LIST — выгрузку Pwned Passwords (SHA-1, по порядку хешей); файл читается двоичным поиском и в память не грузится, наружу ничего не уходит. Пароль, переезжающий с координатора при первом входе, политикой не проверяется — он у человека уже есть.

Там же, блок «Требования ко входу» (всё выключено по умолчанию). «Требовать второй фактор» (mfa_required): выключено, для ролей (mfa_roles; пусто — роли, открывающие консоль) или для всех. Человек в охвате без второго фактора при входе по паролю попадает на шаг настройки прямо на странице входа: секрет для приложения-аутентификатора и код либо новый ключ доступа, затем коды восстановления. Льготный срок (mfa_grace_days, 0 — при следующем входе) считается от дня, когда требование впервые коснулось человека; пока он идёт, шаг можно отложить. Если единственный фактор — ключ доступа, после пароля спрашивают его. Вход по паролю в мессенджере то же требование выполняет отказом: после льготного срока токен не выдаётся, ответ {"mfa_enrolment_required": true, "portal": "<identity>/me"} отправляет в портал. Вход через корпоративный провайдер под требование не попадает: факторы там ведёт провайдер. Для отдельного приложения — options.require_mfa: true в его параметрах: однофакторный вход (пароль без кода, ключ без проверки на устройстве, такая же живая сессия) перед выдачей кода подтверждается вторым фактором. Как человек вошёл, приложение видит в ID-токене: amr (pwd, otp, hwk/swk, mfa) и acr (urn:simpletwo:mfa или urn:simpletwo:1fa). «Вход только по ключам доступа» (passwordless) — политика поставщика входа, а не каталога: каталог её не принимает.

Там же — «Забыли пароль?» на странице входа (self_reset: off по умолчанию или on). Код из восьми цифр приходит в приложение SimpleTwo на устройствах, где человек уже вошёл, — канал, которого не видит тот, кто знает только имя пользователя; на адрес, введённый на странице, ничего не отправляется, и почтой код не ходит. Страница никогда не говорит, есть ли такая учётная запись. Код действует 10 минут, один раз, пять попыток; с включённым вторым фактором после кода спрашивают и его (или код восстановления). Учётным записям корпоративного каталога (LDAP, домен корпоративного провайдера) сброс не делается — пароль меняют в каталоге; учётной записи с ключами доступа и без кода — тоже: она входит ключом и меняет пароль в портале. Не больше трёх кодов в час на учётную запись и десяти запросов в час с одного адреса. После сброса завершаются все сессии и refresh-токены, в журнал пишется password.reset.self, человеку приходит уведомление.

В меню строки пользователя (⋯), когда роль directory развёрнута: Разблокировать, Временный пароль (двенадцать символов без похожих букв, показывается один раз; при следующем входе человек обязан задать свой), Сбросить 2FA. «Log out» теперь завершает и сессии на странице входа directory, и refresh-токены приложений. При включении второго фактора выдаются восемь кодов восстановления — один раз; каждый принимается на шаге второго фактора вместо кода из приложения и сгорает. Страница входа directory после пароля и фактора требует сменить временный или просроченный пароль, прежде чем выдать приложению код.

Сеансы: сколько живёт вход​

В консоли, в разделе «Безопасность → Требования ко входу», блок «Сеансы». По умолчанию: сеанс на странице входа — 12 часов, простоя нет, токен приложения (refresh) живёт 90 дней с последнего использования и без общего предела, консоль заново входить не просит. Сеанс в браузере, часов (web_max_hours, до 720) — абсолютный срок, сколько бы человек ни работал. Простой, минут (web_idle_minutes, 0 — выключено, иначе от 5 до 10080) — сеанс, которым столько не пользовались, заканчивается. Токен приложения без использования, дней (refresh_idle_days, до 365) — каждое обновление токена отсчитывает заново. Токен приложения не дольше, дней (refresh_max_days, 0 — без предела) — считается от входа, с которого цепочка началась; уменьшенный предел действует и на уже выданные токены. Потерянный ответ обновления: повтор в течение, секунд (refresh_replay_seconds, по умолчанию 120, до 600, −1 — никогда). При каждом обновлении каталог выдаёт новый токен и гасит старый, а повтор погашенного токена считает кражей и завершает сеанс. Но если ответ на обновление потерялся (приложение ушло в фон, сменилась сеть), приложение повторяет запрос со старым токеном. В течение этого окна, пока новым токеном никто не пользовался, такой повтор получает новую пару токенов и сеанс продолжается (в аудите — oidc.refresh_replay). Токены, привязанные к ключу устройства, повторяются с подписью этим ключом без ограничения по времени. Повтор после того, как новым токеном уже воспользовались, по-прежнему завершает сеанс (oidc.refresh_reuse). Консоль: заново войти через, минут (reauth_for_console_minutes, 0 — выключено) — консоль просит свежий вход для разделов каталога, если последний был раньше. У отдельного приложения в его параметрах могут быть свои options.access_ttl_minutes (время жизни access-токена, 5…1440, по умолчанию час) и options.refresh_max_days (заменяет предел политики). Кнопка «Завершить все сеансы» после подтверждения завершает все сеансы в браузере и токены приложений организации, приложениям с адресом выхода (backchannel_logout_uri) сообщает о каждом человеке, действие записывается в аудит (sessions.revoke_all). Уже выданный access-токен доживает свой срок.

Приложения: права, согласие, службы и устройства​

В разделе «Безопасность → Приложения» у каждого приложения есть Параметры — один JSON: scopes — что приложение вправе запросить (запрос за пределами регистрации отклоняется, а не урезается молча); options.claims — какие claims оно получает из тех, что даёт scope; options.attrs — ключи схемы, отдаваемые как claims; options.groups_as — группы по id, handle или имени; options.client_credentials — грант для службы (только confidential; токен с sub: client:<id>, без ID-токена); options.device_flow — вход с устройства без клавиатуры: устройство получает код на POST /oauth/device, человек вводит его на identity_url/oauth/device, входит как обычно и подтверждает, устройство забирает токены при опросе. Приложение, не отмеченное как first-party, один раз спрашивает согласие человека на странице в его языке; портал показывает выданные разрешения и отзывает их (вместе с refresh-токенами приложения).

Проверки доступа​

В разделе «Безопасность → Проверки доступа»: «проверять гостей старше N дней» и «проверять держателей ролей старше N дней» — раз в сутки directory открывает проверку: гостя — тому, кто его пригласил, держателя ролей — его руководителю (атрибут manager); проверяющий получает уведомление в мессенджере и отвечает в портале identity_url/me (раздел «Группы»): «ещё нужен» или «отозвать». Без ответа к сроку доступ отзывается: гость отключается, у держателя ролей снимаются все роли — это запись в источник правды, и платформенная консоль подхватывает её через ленту изменений. Оставленный доступ проверяется снова через тот же период. Там же: «блокировать адрес источника после N промахов» — защита от перебора по адресу, независимо от учёток. Промахами считаются и неверный пароль, и неверный код второго фактора — на странице входа directory и во входе мессенджера (координатор передаёт адрес клиента вместе с кодом); с заблокированного адреса не принимается и верный код.

SCIM 2.0: HRMS и Entra пишут людей и группы​

Раздел «Безопасность → Синхронизация», карточка «SCIM 2.0»: выдайте токен (показывается один раз), укажите провизионеру адрес identity_url/scim/v2 и bearer-токен. Поддержаны Users и Groups (создание, чтение, фильтры userName/externalId/emails.value/ displayName/active, PATCH — active, имя, почта, атрибуты расширений, участники групп), enterprise-расширение (department, manager, organization, division, employeeNumber) ложится под ключи схемы с теми же именами, а расширение urn:ietf:params:scim:schemas:extension: simpletwo:2.0:User — под ключи по их scim_path; атрибут без ключа в схеме отклоняется по имени. Записанный человек — учётка в источнике правды с source: scim; платформенная консоль забирает его в течение минуты через ленту изменений directory, и с ним происходит всё, что с учёткой из консоли: событие каталога, welcome-триггер, ассистент, календарь.

Первую загрузку удобно отправить одним запросом Bulk (POST /scim/v2/Bulk, до 100 операций и 1 МБ): группа может ссылаться на людей из того же запроса через bulkId:<id>, даже если они перечислены ниже; операция, чья ссылка ведёт на неудавшееся создание, сама отклоняется (409), а failOnErrors останавливает запрос после заданного числа ошибок. Списки сортируются (sortBy — userName, displayName, meta.lastModified; sortOrder — ascending/descending). У каждого ресурса есть версия — слабый ETag W/"<ревизия>" в meta.version и заголовке ETag: If-Match на PUT/PATCH/DELETE вернёт 412, если ресурс успели изменить, If-None-Match на GET — 304, если не изменился.

SCIM-клиент: directory наполняет приложения​

Обратное направление — консоль → «Безопасность → Синхронизация» → SCIM-получатели: приложение, которое хочет получать людей и группы, заводится с base_url (корень SCIM 2.0, только https), bearer- токеном, который выдало приложение (не показывается обратно), и тем, что отправлять: users, groups, filter_group (только участники этой группы). Новый человек уходит POST /Users, правка — PUT /Users/{id} с тем id, который вернуло приложение; ушедший (удалён или вышел из filter_group) отключается PATCH active=false, а не удаляется. Группа создаётся POST /Groups, состав заменяется PATCH /Groups/{id}, удалённая группа удаляется. Отправка идёт по тем же лентам изменений учёток и групп, что читают платформенная консоль и почта, в пределах минуты; у каждого получателя свой курсор. При сбое курсор остаётся на строке, которая не прошла, следующая попытка — через паузу с нарастанием (от 30 секунд до 30 минут); в таблице видно последний успех, последнюю ошибку и сколько изменений в очереди, кнопка «Отправить сейчас» пробует сразу. Новый base_url — новое приложение: отправка начинается заново. Каждая отправка и сбой — строка аудита scim.push.

LDAP-сервер для приложений​

Принтер, Wi-Fi-контроллер, NAS, GitLab без OIDC — раздел «Безопасность → Синхронизация», блок LDAP-сервера. Дерево только для чтения: ou=people (uid=<имя>, inetOrgPerson: cn, sn, givenName, displayName, mail, telephoneNumber, memberOf и атрибуты схемы под их LDAP-именами), ou=groups (cn=<handle>, groupOfNames с member), ou=services — учётки привязки для приложений: у каждой своя область (люди, группы, какие атрибуты), секрет показывается один раз, вне области приложение получает noSuchObject. Человек привязывается своим паролем с той же блокировкой, что везде; учётка со вторым фактором привязаться не может, пока это не разрешено переключателем. Любая запись отклоняется. Слушатели включаются в настройках хоста directory: Серверы → строка directory → Settings → поле LDAP server: off, ldaps (порт 636), starttls (порт 389, для приложений, которые умеют только ldap:// со StartTLS; привязка до StartTLS отклоняется) или both. Хост применяет это на следующем опросе и сам открывает порты. Сертификат — тот же, что хост уже получил для своего публичного имени, поэтому поле требует публичного имени, а раскладывать PEM-файлы и править env-файл вручную больше не нужно. Выключение (off) так же сам закрывает 636 и 389 — только те порты, которые хост открыл для directory сам. Приложение подключайте по публичному имени (ldaps://id.example.com). Строка LDAP на карточке Directory показывает, что реально слушает и с каким сертификатом, а если настройка ещё не применена — говорит и это.

Переключатель posixAccount и posixGroup в блоке LDAP-сервера добавляет атрибуты для Linux и NAS: у людей uidNumber, gidNumber (равен собственному uid), homeDirectory /home/<имя>, loginShell /bin/bash; у групп gidNumber и memberUid. Номера выдаются один раз из счётчика, начиная с 10000, и не меняются и не переиспользуются — файлы на NAS остаются за тем же человеком.

Вебхуки и экспорт аудита​

Раздел «Безопасность → Синхронизация», карточка «Вебхуки»: приёмник подписывается на события жизненного цикла (учётка создана / изменена / удалена, группа изменена / удалена, согласие выдано / отозвано, или *) и получает каждое как JSON, подписанный X-S2-Signature, если задан секрет — тот же контракт, что у ботов messaging. Три попытки; каждая доставка — строка в аудите; кнопка «Проверить» шлёт пробное событие. Export audit скачивает весь журнал событий безопасности в JSON Lines для SIEM; фильтры в запросе /control/audit/export: action= (префикс, например login.), actor=, subject=, from=/to= (RFC 3339), since=/until= (номер строки).

Отправка аудита в SIEM и срок хранения​

Консоль → «Логи и аудит → Аудит» → Отправка в SIEM: журнал можно не только забирать, но и отправлять. kind: syslog-tls — RFC 5424 поверх TLS в кадрах RFC 5425 на address (host:port), по желанию ca_pem (УЦ коллектора) и клиентский сертификат client_cert_pem/client_key_pem; syslog-tcp — то же без TLS; https — POST пачками JSON-строк на url с bearer token. В каждом сообщении — вся строка журнала с её хешем (facility authpriv, важность по исходу, MSGID — действие). Курсор помнит последнюю принятую строку: при сбое пачка уходит повторно после паузы, ничего не пропускается; строка состояния показывает курсор, последний успех или ошибку и очередь, «Отправить сейчас» пробует сразу. Хранить журнал, дней (retain_days, 0 — всё, по умолчанию; иначе не меньше 30): строки старше удаляются раз в минуту, но никогда раньше, чем их принял настроенный получатель SIEM. Последняя удалённая строка становится якорем цепочки — проверка начинается с него, и цепочка остаётся проверяемой; удаление мимо этого механизма база по-прежнему не пропускает.

Страница входа: оформление, условия использования, выход из приложений​

Раздел «Безопасность → Страница входа», карточка «Оформление»: название организации, логотип, один цвет и фраза на двух языках — их берут все страницы directory (вход, согласие, устройство, условия, портал). Условия использования: версия больше 0 показывается один раз при входе после пароля и второго фактора — краткое содержание на странице и ссылка на полный документ; отказ = отказ во входе; новая версия спрашивает всех заново. Приложение с options.backchannel_logout_uri получает подписанный logout-токен, когда человек выходит на странице входа или служба поддержки завершает его сессии.

Каталог в консоли​

Своей консоли у каталога нет: его разделы — части консоли координатора, и каждый стоит там, где его ищут.

ГдеЧто
Пользователи → Люди, Гости, Группы, Ботыучётки, гости, группы (состав, заявки), боты
Пользователи → Роли«Кто может открыть эту консоль» — рядом с «Группы → роли»
Пользователи → Адресная книгаадресные списки, политики адресной книги, книги организации
Безопасность → Требования ко входувторой фактор, политика учётных данных, сеансы, пароли приложений, доступ к SimpleTwo
Безопасность → Страница входадомены входа, корпоративный провайдер (OIDC, SAML), условия использования, оформление, поля людей от провайдера
Безопасность → Приложенияприложения OIDC и ключи подписи
Безопасность → СинхронизацияLDAP-источник и его задачи, SimpleOne HRMS, SCIM (входящий и получатели), LDAP-сервер, вебхуки
Безопасность → Проверки доступаполитика и открытые проверки
Безопасность → Аварийный доступаварийная учётка координатора, доступ поддержки вендора
Настройки → Каталогподключение к каталогу, перевод служб, схема атрибутов
Логи и аудит → Входы, Аудитжурнал входов, аудит каталога и отправка в SIEM
Обзорчто требует внимания, входы за 14 дней, настройка каталога

Право — по учётной записи в каталоге: платформенные роли owner/admin (или группа из «Кто может открыть эту консоль») дают роль identity-admin — всё; helpdesk/support — роль help-desk: только «Люди» с четырьмя действиями (разблокировать, временный пароль, сбросить 2FA, завершить сессии) и чтением карточки человека. Каждое действие пишется в аудит под именем человека.

С 27.09.2026:

  • Обзор консоли показывает, что требует внимания в каталоге: администраторы и поддержка без второго фактора, заблокированные сейчас, заявки в группы, просроченные проверки доступа, сбои SCIM-получателей, отправки в SIEM и вебхуков (с последней ошибкой), недоступный LDAP-каталог или отказывающий корпоративный провайдер, цепочка аудита и порог его хранения. Карточка ведёт в раздел, где это исправляется (для людей — сразу с фильтром). Ниже — входы за 14 дней (выполнено и отказано по дням) и список настройки: публичный адрес, ключи подписи, администратор со вторым фактором, корпоративный провайдер или LDAP-источник, оформление, SIEM, политика проверок.
  • Люди — таблица с именем и логином, e-mail, источником, состоянием (активна, отключена, истекла, заблокирована, гость), вторым фактором и последним входом; фильтры-кнопки (все, заблокированы, отключены, гости, без второго фактора, без локальных учётных данных, администраторы, администраторы без 2FA), поиск по логину, имени и e-mail, сортировка по заголовку, Экспорт CSV отфильтрованных строк. Строка открывает карточку человека: четыре действия поддержки в шапке и вкладки «Профиль» (атрибуты с подписями из схемы, JSON свёрнут), «Учётные данные» (пароль и его возраст, TOTP, коды восстановления, ключи доступа и пароли приложений — каждый можно удалить), «Группы» (убрать, добавить), «Сессии» (завершить одну или все), «Входы», «Разрешения» (сторонние приложения, отзыв) и «Действия» (всё из аудита, где человек действовал или над ним действовали).
  • Логи и аудит → Входы — журнал входов всей организации за 30 дней: время, человек, способ, приложение, адрес, итог с причиной; фильтры по человеку, итогу, приложению, способу и дате. Строка открывает панель со всеми полями, номером и хешем строки аудита.

Служба поддержки видит только «Люди»: вкладки чтения карточки (кроме «Разрешений» и «Действий»), четыре действия и завершение отдельной сессии; удалить ключ доступа или пароль приложения, отозвать разрешение может только identity-admin.

Портал человека — identity_url/me​

Каждый входит на identity_url/me через страницу входа directory (клиент me, роль регистрирует его сама при старте, когда задан DIR_PUBLIC_URL) и видит: профиль (что о нём знает каталог, атрибуты по схеме с подписью на его языке и с пометкой, кому они видны); безопасность (сменить пароль — с текущим; включить второй фактор — секрет и ссылка otpauth:// для приложения-аутентификатора, восемь кодов восстановления показываются один раз; сессии на странице входа и в приложениях, «Выйти везде»); группы (свои, выйти, вступить по id — открытая принимает сразу, с одобрением — заявка владельцам); адресные книги (создать, импорт vCard/CSV, экспорт, удалить); приложения — лаунчер: всё, куда можно войти, по одному нажатию. Адрес приложения для лаунчера задаётся в разделе «Безопасность → Приложения» (⋯ → «Адрес в лаунчере»). Портал работает и когда координатор недоступен: он входит через провайдер directory.

Вход через identity (OpenID Connect)​

Directory — провайдер OpenID Connect: /.well-known/openid-configuration на его публичном адресе, code flow с PKCE, ключи EdDSA с ротацией, страница входа на русском и английском (язык — из браузера или ui_locales). Приложение регистрируется в консоли → «Безопасность → Приложения»: public (телефон, браузерное приложение — без секрета, PKCE обязателен) или confidential (GitLab, vCenter — секрет показывается один раз, его можно перевыпустить); адреса возврата только https. Вход проверяет пароль и второй фактор из хранилища directory, десять промахов — блокировка на пятнадцать минут; каждое решение — строка аудита. Консоль и клиенты SimpleTwo могут указать directory своим провайдером по одному адресу discovery.

Корпоративные провайдеры за directory. Если у организации остался ADFS, Entra или Keycloak, он подключается как upstream: в консоли → «Безопасность → Страница входа» → «Корпоративный провайдер» — issuer, client_id, секрет, список почтовых доменов. Провайдеров может быть несколько (консоль → «Безопасность → Страница входа» → «Корпоративный провайдер», список): у каждого id, подпись кнопки на русском и английском (label), свои домены и provision. На странице входа у каждого своя кнопка; домен провайдера входит только через него — пароль здесь отказывается, и другой провайдер за этот домен поручиться не может. После входа directory сверяет ID-токен и находит учётку по claim (upn, preferred_username, email, или заданный username_claim). Если учётки нет: provision: off (по умолчанию) — «учётной записи нет»; provision: create — directory заводит учётку в источнике правды (source: oidc, имя и почта из стандартных claim'ов, только в доменах этого провайдера), платформенная консоль получает её через ленту изменений, вход записан в аудит (oidc.upstream.provision). Адрес возврата — один для всех провайдеров, он показан в карточке. Прежняя настройка с одним провайдером читается как список из одного (id: default). Второй фактор самого провайдера (mfa_via_upstream: true): когда вход, сделанный через этого провайдера, нужно повысить — приложение с require_mfa, SAML-приложение, запросившее MFA, — directory не спрашивает свой код или ключ доступа, а отправляет человека обратно к провайдеру с acr_values (mfa_acr_values; по умолчанию https://refeds.org/profile/mfa и http://schemas.microsoft.com/claims/multipleauthn, который понимает ADFS; Entra сообщает MFA в amr независимо от запроса), prompt=login, max_age=0 и login_hint. Ответ считается двумя факторами, только если это видно в ID-токене: amr с mfa (или второй вид фактора рядом с первым входом), multipleauthn от ADFS в amr или acr из mfa_acr_values; кроме того, токен должен называть того же человека и не быть прежним входом (auth_time). Иначе приложение получает access_denied, а сессия остаётся однофакторной. В журнале входов такой вход — «вход через компанию + второй фактор компании» (oidc.upstream.stepup). Для провайдера по SAML то же самое делается запросом AuthnRequest с ForceAuthn="true" и RequestedAuthnContext из классов mfa_acr_values (по умолчанию те же два); сравнение — mfa_comparison: minimum (по умолчанию) или exact. Двумя факторами ответ считается, только если AuthnContextClassRef (или атрибут ADFS authnmethodsreferences) — REFEDS MFA, multipleauthn ADFS или класс из mfa_acr_values, NameID тот же, что в утверждении, которым была открыта сессия (или тот же человек по claim'ам), а AuthnInstant — позже начала повышения.

Корпоративный провайдер по SAML (ADFS по SAML). Провайдер в том же списке может быть kind: saml — тогда directory для него поставщик услуг (SP), а не клиент OIDC. Проще всего завести его импортом: консоль → «Безопасность → Страница входа» → «Корпоративный провайдер» → «Импорт метаданных IdP (SAML)» — вставьте XML или укажите адрес (для ADFS https://<хост>/FederationMetadata/2007-06/FederationMetadata.xml). В список добавится выключенный черновик с idp_entity_id, sso_url и sso_binding (HTTP-Redirect, а если IdP предлагает только HTTP-POST — post), slo_url (единый выход IdP по HTTP-Redirect) и сертификатами подписи idp_certs; проверьте, задайте домены, provision, подпись кнопки и сохраните. Под карточкой появятся адрес метаданных SP вида https://<identity>/oauth/upstream/saml/<id>/metadata (он же entity ID), ACS …/oauth/upstream/saml/<id>/acs и адрес единого выхода …/oauth/upstream/saml/<id>/slo — по этому адресу метаданных в ADFS заводится Relying Party Trust («Import data about the relying party published online»). sso_binding: post отправляет AuthnRequest формой, которая отправляется сама (подпись — внутри XML); redirect (по умолчанию) — подписанным запросом в адресе. Остальные поля: nameid_format (пусто — не запрашивать формат, ADFS выдаст тот, что задан в правилах), attribute_map — из какого SAML-атрибута брать claim (upn, email, name, given_name, family_name, …); не перечисленные читаются из обычных имён ADFS (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn, …/emailaddress, …/givenname, …/surname), Entra и eduPerson, а NameID доступен как claim nameid (например, username_claim: nameid). want_assertions_signed (по умолчанию включено) требует подписи на самом утверждении — так ADFS и подписывает по умолчанию; выключенное принимает и подпись только на ответе, но неподписанный ответ не принимается никогда. sign_requests (по умолчанию включено) подписывает AuthnRequest ключом RSA directory. encrypt_assertions публикует в метаданных сертификат шифрования и принимает зашифрованные утверждения (ADFS шифрует AES-256-CBC — это принимается; AES-GCM тоже), а также зашифрованный NameID (EncryptedID) — в утверждении и в запросе на выход от IdP; расшифровывается он теми же ключами и алгоритмами и только после того, как сошлась подпись. Без него зашифрованное утверждение или NameID отклоняется. Дальше всё как у провайдера OIDC: домены, поиск и создание учётки (source: saml), сессия. Способ входа берётся из AuthnContextClassRef: multipleauthn ADFS или REFEDS MFA — вход с двумя факторами (mfa). Отказы — в журнале входов с причиной «вход через компанию отклонён»: неверная подпись, чужая аудитория или ACS, истёкшее утверждение (допуск по часам — две минуты), повтор уже принятого утверждения, вход, начатый на стороне IdP.

Адрес почты из SAML. У SAML нет признака «адрес подтверждён», поэтому по умолчанию адрес из утверждения учётку не находит. trust_email: true — заявление администратора, что IdP выдаёт только проверенные адреса: тогда вход, чей логин не нашёлся, связывается с учёткой, у которой этот адрес (ровно одной), как у провайдера OIDC с email_verified (oidc.upstream.link). Не включайте его для IdP, где человек может сам сменить себе адрес.

Смена сертификата подписи. Если задан metadata_url (только https), directory перечитывает метаданные IdP каждые шесть часов (с разбросом, после сбоя — раньше, с нарастающей паузой) и по кнопке «Обновить метаданные» под карточкой. Новый сертификат, который IdP начал публиковать, добавляется сразу; сертификат, пропавший из метаданных, остаётся ещё сутки и только потом удаляется — так ADFS переживает смену ключа без простоя. Неудачное чтение (адрес не ответил, документ не разобран, в нём другой entityID) ничего не меняет, последний сертификат не удаляется никогда; время последнего успеха, ошибка и следующее обновление видны под карточкой. Каждое изменение — строка аудита oidc.upstream.metadata с отпечатками SHA-256 добавленных и удалённых сертификатов. Вставленный вручную XML не обновляется.

Единый выход. С slo_url выход из directory (/oauth/logout или выход, начатый SAML-приложением) сначала выводит из приложений directory, а затем отправляет браузер в IdP с подписанным LogoutRequest (NameID и SessionIndex из утверждения, по которому открыта сессия); ответ IdP (подписанный его сертификатом, иначе отказ) возвращает человека туда, где выход начался. Выход, начатый в IdP (подписанный LogoutRequest на …/oauth/upstream/saml/<id>/slo, по Redirect или POST, не старше пяти минут, один раз), завершает сессии этого человека, пришедшие через этот IdP (с указанным SessionIndex — только её), выводит из приложений directory и отвечает IdP подписанным LogoutResponse. Журнал — oidc.upstream.slo.

Что показывает состояние​

v0.2.0 · http://10.40.0.7:8086 · https://id.example.com — версия роли, приватный адрес, по которому её набирает координатор, и публичное имя, если оно есть. unreachable — хост зачислен, но не отвечает: смотрите журнал роли на хосте (journalctl -u simpletwo-identity) — обычно это каталог DIR_DATA_DIR, в который служба не может писать, или при первом старте после перехода на SQLite — база из DIR_IMPORT_PG_DSN, до которой не пускает pg_hba.