Skip to main content

Гости, подрядчики и члены семьи

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

В платформе не один «пользователь с галочками», а несколько классов доступа, и класс решает сразу две вещи: что человек видит в переписке и куда его пускают в сети. Разделение сделано так, чтобы приглашение в разговор нигде не превращалось в доступ к системам.

КлассКак появляетсяПрофиль в сетиКаталог
employeeиз корпоративного каталога при первом входе (OIDC/ADFS)набор правил rs-corp, группы шлюзов corp-egress и geo-poolпо политике роли
guestприглашает сотрудник с правом guests:inviteнабор правил rs-guest, только geo-poolограничен политикой роли
partnerзаводит администратортот же rs-guest, только geo-pool, но роль отдельнаяограничен политикой роли
ботадминистратор в консолине применимопо списку возможностей
Почему partner — отдельная роль, а не «гость с правами»

Расширять права нужно кому-то одному, а не всем сразу. Пока это разные роли, изменение политики подрядчиков не задевает гостей — и наоборот. Наборы правил у них сегодня совпадают, и это осознанное совпадение по умолчанию, а не одна сущность под двумя именами.

1. Кто может приглашать

Приглашение — это право роли, а не отдельная роль: guests:invite выдаётся той роли, которой оно нужно (например, employee целиком или отдельной роли «принимающий»).

Проверить, что у сотрудника оно есть, можно его же токеном:

curl -fsS "https://s2.<домен>/v1/me/capabilities" -H "Authorization: Bearer $TOKEN"

В ответе должен быть guests:invite в списке can. Если его нет — приглашение вернёт 403, и это ответ, а не сбой: право выдаётся в консоли, в свойствах роли.

2. Приглашение

Сотрудник делает это сам из приложения (Контакты → пригласить гостя): появляется карточка с QR-кодом и учётными данными, её можно отправить гостю любым способом. Тот же вызов из командной строки:

curl -fsS -X POST "https://s2.<домен>/v1/guests" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"username":"ivanov-guest","display_name":"Иван Иванов","phone":"+79001234567","ttl_hours":720}'
ПолеОбязательноеСмысл
usernameдалогин гостя; занятый вернёт 409
phoneдаканал восстановления доступа, формат E.164, должен быть уникальным
display_name, emailнеткак гость подписан в списках
passwordнетсвой пароль (не короче 8 символов) или сгенерированный сервером
expires_at / ttl_hoursнетточная дата (RFC 3339) или срок в часах

Три поведения, о которые спотыкаются чаще всего:

  1. Телефон обязателен и уникален. Он нужен не для связи, а для восстановления — им гость называет себя, когда просит вернуть доступ (§5). Второй гость с тем же номером получит 409.
  2. Срок по умолчанию — 30 дней, даже если вы про него не думали. Максимум задаёт администратор (по умолчанию год), и указанный срок молча урезается до максимума, а не отклоняется.
  3. Кто пригласил — остаётся в записи навсегда (invited_by), и приглашение попадает в аудит. Гость всегда кому-то принадлежит; «ничей» гость в системе не появляется. Это же поле решает, кто может вернуть ему доступ (§5).

Проверка: запись видна в консоли (Users → фильтр по роли guest) со сроком и именем пригласившего, а гость входит выданной парой логин/пароль.

3. Максимальный срок приглашения

Ограничение ставится один раз и действует на все самостоятельные приглашения:

curl -fsS -X POST "https://s2.<домен>/admin/api/settings/guest-max-days" \
-H "Cookie: $ADMIN_SESSION" -H 'Content-Type: application/json' \
-d '{"days":90}'

Проверка: приглашение с ttl_hours больше лимита создаётся, но в ответе expires_at приходит урезанным до лимита.

4. Что гость получает в сети — и чего не получает

Гостевой профиль выпускает в интернет через узел из группы geo-pool и не содержит корпоративных маршрутов. Разница между «запрещено» и «не описано» здесь принципиальна: запрет снимается галочкой, отсутствующего маршрута снять нельзя.

Отсюда и сценарий «член семьи сотрудника»: домашнему устройству нужен защищённый интернет, и гостевого класса для этого достаточно. Заводить ему что-то большее не нужно — а по построению профиля и не получится.

Каталог организации гость не читает: область видимости каталога — свойство роли (none / groups / org), а не следствие участия в разговоре.

5. Восстановление доступа

Доступ гостю возвращает тот, кто его пригласил. Это и есть обычный путь, а не запасной: гость нажимает в приложении «Не могу войти», пригласивший видит его у себя в разделе Мои гости и диктует шестизначный код — голосом, в переписке, лично, любым каналом, которым они и так общаются. Пароль гость задаёт себе сам, пригласивший его не узнаёт.

Почему так, а не звонком: звонок доказывал владение номером, и делать его должна служба телефонии (роль mediation), которой в платформе пока нет. Коллега, который приглашал человека на прошлой неделе, узнаёт его — это другое доказательство и внутри организации более сильное. Решение записано дополнением к ADR-0007.

Что видит пригласивший в разделе «Мои гости»: своих гостей и только своих, просящие помощи — сверху, у каждого телефон и срок. Две кнопки: Помочь войти (выдаёт код) и Продлить.

Истёкшему гостю код не поможет

Просроченной учётной записи код выдан не будет — сервер откажет и скажет почему. Погасить его всё равно нельзя: подтверждение отказывает неактивной записи. Поэтому продление лежит на том же экране: сначала продлить, потом выдавать код.

Гость при этом может начать сам, из приложения:

curl -fsS -X POST "https://s2.<домен>/v1/auth/recover" \
-H 'Content-Type: application/json' -d '{"phone":"+79001234567"}'

Ответ одинаков всегда — это заявка, а не проверка номера. Она помечает гостя как «не может войти» и сообщает пригласившему. Код и новый пароль уходят во второй вызов:

curl -fsS -X POST "https://s2.<домен>/v1/auth/recover/confirm" \
-H 'Content-Type: application/json' \
-d '{"phone":"+79001234567","code":"123456","new_password":"<новый пароль>"}'

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

Сотруднику с правом guests:invite доступны три ручки — те же, что стоят за экраном «Мои гости»:

ВызовЧто делает
GET /v1/guestsмои гости, просящие помощи — первыми
POST /v1/guests/{логин}/recovery-codeвыдаёт код мне, чтобы продиктовать
POST /v1/guests/{логин}/extend {"days":30}продлевает срок

Область — только собственные гости. Чужой гость и несуществующий логин отвечают одинаково (404): иначе эта ручка стала бы способом перебирать логины. Выдача кода пишется в журнал аудита с обоими именами.

Когда пригласившего нет рядом

Гостя, заведённого в консоли, координатор записывает за тем администратором, который его завёл (invited_by), — такой гость есть в «Моих гостях» у этого администратора, если у него есть право guests:invite. Когда пригласивший недоступен — в отпуске, ушёл, права нет, — код выдаёт любой администратор: Users → строка человека → ⋯ → «Код восстановления» (право users:write, выдача пишется в аудит). Кнопка есть только у локальной записи с телефоном — телефоном человек называет себя, когда гасит код. Пароль администратор при этом не узнаёт, как и пригласивший.

Карточка Access recovery на той же вкладке — не то же самое: она показывает заявки, которые гости уже подали, и её не видно, когда таких нет. Начать восстановление можно кнопкой в строке.

Учётной записи из корпоративного каталога код не выдаётся: её пароль меняет каталог.

6. Отзыв

  • Сам по себе. Истёк срок — доступ закрылся; забытый гость исчезает без чьего-либо участия. Это и есть причина, по которой срок обязателен.
  • Раньше срока. Отключение или удаление записи в консоли; сессии и устройства там же видны по именам. Доступ к сети закрывается на следующем цикле обновления политики на устройстве, а не «когда-нибудь».

Типовые отказы

СимптомВероятная причина
403 на POST /v1/guestsу роли сотрудника нет права guests:invite
409 a user with this phone already existsномер уже привязан к другой записи; телефон уникален
400 invalid expires_at (want RFC3339)дата не в формате 2026-12-31T23:59:59Z
Гость входит, но не видит коллегобласть каталога у роли guest — так и задумано; смотрите политику роли
Гость подключился, но внутренние адреса недоступныожидаемо: в гостевом наборе правил корпоративных маршрутов нет
Восстановление «прошло», но звонка не былозвонков и не будет: код выдаёт пригласивший в разделе «Мои гости», а при его отсутствии — администратор в карточке «Access recovery»
404 you have no guest by that nameэто не ваш гость, либо такой записи нет — ответ намеренно один на оба случая
409 this guest's access has expiredсначала продлить срок, потом выдавать код