UC-PRF-005. Запрет редактирования профиля для менеджера застройщика (TalentSync)
Назначение
Исключительный сценарий: менеджер застройщика (UserType = DeveloperManager,
ContactsManagedBy = TalentSync) не может управлять профилем, контактами, ФИО и любыми персональными
данными в PIN — источник знаний Talent Identity (требование INV-IDN-001). Фиксирует поведение проверок
доступа profile/identity на каждом уровне (UI-флаги, API-отказы) и то, что остаётся доступным. Связан
с контекстами profile и identity.
| Поле | Значение |
|---|
| ID | UC-PRF-005 |
| Контекст | profile (см. docs/06-services/profile/); смежно — identity |
| Тип | исключительный |
| Статус | draft |
Актор
| Поле | Значение |
|---|
| Роль | Девелопер (менеджер застройщика) — пользователь компании-застройщика, реплицируемый из Talent Identity |
| Permissions / scope | profile.personal.view-self (просмотр реплики); manage-permissions над персональными данными не действуют — блокируются проверками доступа независимо от наличия permission |
| Tenant | TenantId = CompanyId застройщика в данных identity/crm; данные profile — record-level по Id = UserId (ADR-0052); Profile.CompanyId — информационная реплика |
| Record-level | только собственный профиль (read-only реплика) |
Предусловие
- Пользователь создан синком Talent (
identity, UC-IDN-007): UserType = DeveloperManager,
ContactsManagedBy = TalentSync, Status = Active.
- Consumer
identity-users-sync создал ProfileEntity-реплику (ContactsManagedBy = TalentSync);
персона Self для не-инвестора не создаётся.
Триггер
| Поле | Значение |
|---|
| Источник | UI button / API call |
| Триггерящий элемент | ЛК /profile/personal менеджера застройщика; либо прямой API-вызов (обход UI) |
Основной Поток
- Менеджер застройщика открывает
/profile/personal. Фронт вызывает getMyProfile.
getMyProfile возвращает реплику (Name, ContactPhone, ContactEmail,
UserType = DeveloperManager, ContactsManagedBy = TalentSync) и вычисленные UI-флаги:
canEditContacts = false, canEditName = false, canManagePersons = false,
canManageTemplates = false, canSubmitVerification = false, canEditSettings = true.
- Фронт скрывает формы редактирования ФИО/контактов и блоки «Персоны», «Документы», «Шаблоны
реквизитов», «Верификация»; показывает плашку «Данные управляются системой застройщика (Talent);
для изменения обратитесь к администратору вашей компании».
- Менеджер (или злоумышленник с его токеном) вызывает API напрямую, минуя UI, — например
createPerson (POST /profile/persons). Проверка доступа по профилю: ContactsManagedBy = TalentSync
→ отказ до любых мутаций — PROFILE_MANAGED_EXTERNALLY (INV-PRF-2/INV-PRS-3, HTTP 403). Даже при
SelfService следующая проверка отклонила бы не-инвестора PROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE
(INV-PRF-4) — двойная защита. Целевые агрегаты не загружаются (fail-fast); состояние БД не меняется;
отказ логируется (UserId, операция, код — без PII) и учитывается метрикой
profile_managed_externally_rejections_total (alert на всплеск — README profile).
- Тот же порядок проверок (шаг 4) отклоняет все self-операции контекста над персональными
данными:
updatePerson, archivePerson, addPersonDocument, updatePersonDocument,
attachDocumentScan, createRequisiteTemplate, updateRequisiteTemplate, setDefaultTemplate,
createVerificationRequest, submitVerification — единая проверка режима самообслуживания во всех
операциях над агрегатами (INV-PRF-2).
- Попытка сменить контакты/ФИО через identity (
requestPhoneChange/requestEmailChange/
updateInvestor) отклоняется на стороне identity проверкой INV-4 ManagedContacts →
IDENTITY_CONTACTS_MANAGED_EXTERNALLY (403) — см.
UC-IDN-004, поток A3.
- Актуализация данных менеджера происходит только синком Talent: Talent Identity →
identity (UC-IDN-007) → событие UserUpsertedKafkaEvent (pin.identity.users.v1) →
consumer identity-users-sync обновляет реплику ProfileEntity (INV-PRF-1 MonotonicReplica).
- Что остаётся доступным менеджеру: просмотр профиля (read-only),
updateMySettings
(Locale/Timezone — не персональные данные, матрица в ../domain/profile.md),
2FA/пароль/пин в identity (секреты входа — PIN-локальные, Talent их не ведёт:
UC-IDN-002,
UC-IDN-003).
Альтернативные Потоки
A1. Настройки ЛК (разрешённая операция)
- Менеджер меняет язык/часовой пояс:
updateMySettings — проверяется только Status = Active
(INV-PRF-3); ContactsManagedBy не блокирует (настройки — собственность profile, не реплика Talent).
Успех: публикуется ProfileSettingsChangedEvent.
A2. Агент/сотрудник ПИН (не TalentSync, но не инвестор)
- Для
UserType ∈ {Agent, PartnerManager, PinEmployee} (ContactsManagedBy = SelfService)
проверка режима самообслуживания (INV-PRF-2) проходит, но проверка типа пользователя (INV-PRF-4)
отклоняет персоны/документы/шаблоны/верификацию → PROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE (403).
- Смена собственных ФИО/контактов через identity им разрешена (матрица в
../domain/profile.md). Сценарий завершается.
A3. Смена типа управления (пользователь перестал быть TalentSync)
- Talent/identity меняет
ContactsManagedBy → SelfService (административная миграция учётки);
UserUpsertedKafkaEvent обновляет реплику (INV-PRF-1: только при event.Version > ReplicaVersion).
- Между сменой в identity и догоном consumer-а (окно eventual-consistency — секунды) guard решает по
прежней реплике: self-операции ещё отклоняются по старому
ContactsManagedBy (fail-closed, A4).
- После применения события проверка пропускает self-операции по новой реплике; фронт получает новые
флаги из
getMyProfile без ручных действий. Возврат к обычным сценариям UC-PRF-001..004 (для инвестора).
A4. Устаревшая реплика (событие ещё не доехало)
- Реплика в profile консервативнее фактического состояния identity: проверка решает по реплике —
до догона consumer-а операции могут отклоняться по старому
ContactsManagedBy.
- Поведение принято осознанно (fail-closed для персональных данных); операция повторяется после
догона consumer-а (синка реплики).
Постусловие
- Состояние БД
profile не изменено ни одной отклонённой операцией (проверка срабатывает до мутаций
и до сохранения).
- Отказы зафиксированы в логах/метрике
profile_managed_externally_rejections_total (без PII).
- Данные менеджера в ЛК соответствуют последнему применённому событию Talent-синка.
Возможные Ошибки
| Код | HTTP | Условие | Поведение клиента |
|---|
PROFILE_MANAGED_EXTERNALLY | 403 | Любая self-операция над персональными данными при ContactsManagedBy = TalentSync (INV-PRF-2) | Скрыть формы, плашка «управляется Talent» |
IDENTITY_CONTACTS_MANAGED_EXTERNALLY | 403 | Смена ФИО/email/phone на стороне identity (INV-4) | То же — UC-IDN-004 A3 |
PROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE | 403 | Не-инвестор (A2, INV-PRF-4) | Скрыть блоки персон/шаблонов/верификации |
PROFILE_USER_BLOCKED | 403 | Status ∈ {Blocked, Archived} (INV-PRF-3) | Экран блокировки |
401 UNAUTHORIZED | 401 | Нет JWT | Auth-модалка |
Доменные События
| Событие | Когда поднимается | Subscribers |
|---|
| — | Отклонённые операции доменных событий не поднимают (мутаций нет); отказ — только лог + метрика | — |
ProfileSettingsChangedEvent | A1 (разрешённая смена настроек) | in-proc |
Kafka Интеграционные События
| Событие | Topic | Когда публикуется | Consumers |
|---|
| — | — | Отклонённые операции события не публикуют | — |
Consumed: UserUpsertedKafkaEvent.v1 | pin.identity.users.v1 | Единственный канал актуализации данных менеджера (шаг 7) | profile (consumer identity-users-sync) |
Acceptance Criteria
| # | Критерий | Проверка |
|---|
| AC1 | Все 10+ self-операций над персональными данными (персоны/документы/сканы/шаблоны/верификация) возвращают 403 PROFILE_MANAGED_EXTERNALLY для TalentSync-пользователя, БД не меняется | параметризованный тест по всем операциям + снапшот БД |
| AC2 | getMyProfile для DeveloperManager отдаёт все canEdit*-флаги = false (кроме настроек) | тест флагов |
| AC3 | updateMySettings доступен DeveloperManager (Locale/Timezone меняются) | тест A1 |
| AC4 | Guard срабатывает по реплике: смена ContactsManagedBy в identity вступает в силу в profile только после события (INV-PRF-1) | тест A3/A4 с задержкой consumer-а |
| AC5 | Агент получает PROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE, а не PROFILE_MANAGED_EXTERNALLY (различимые причины) | тест A2 |
| AC6 | Отказы инкрементируют метрику и логируются без PII | проверка лога/метрик |
| AC7 | Смена контактов менеджера в Talent доезжает в ЛК PIN без ручных действий | сквозной тест синка (UC-IDN-007 → consumer) |
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные Документы