diff --git a/.gitignore b/.gitignore index 9167765..d39d5fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .DS_Store +__pycache__/ +scripts/__pycache__/ plane-src/node_modules/ plane-src/.turbo/ plane-src/.next/ diff --git a/scripts/bootstrap_nodedc_platform_plan.py b/scripts/bootstrap_nodedc_platform_plan.py index 73d9871..844ebe0 100644 --- a/scripts/bootstrap_nodedc_platform_plan.py +++ b/scripts/bootstrap_nodedc_platform_plan.py @@ -344,7 +344,7 @@ Launcher должен проходить OIDC Authorization Code Flow + PKCE ч {"text": "Загрузить JWKS и валидировать JWT.", "checked": True}, {"text": "Нормализовать sub/email/name/groups.", "checked": True}, {"text": "Добавить logout flow.", "checked": True}, - {"text": "Подтвердить browser login/logout через launcher.local.nodedc.", "checked": False}, + {"text": "Подтвердить browser login/logout через launcher.local.nodedc.", "checked": True}, ], ), text_block( @@ -367,7 +367,7 @@ Launcher показывает приложения не статическим {"text": "Добавить GET /api/apps.", "checked": True}, {"text": "Вычислять access state из runtime projection groups.", "checked": True}, {"text": "Не скрывать недоступные плитки, а показывать Нет доступа.", "checked": True}, - {"text": "Проверить direct URL behavior через proxy.", "checked": False}, + {"text": "Проверить direct URL behavior через proxy.", "checked": True}, ], ), text_block( @@ -408,10 +408,10 @@ Production flow: пользователь открывает nodedc.ru, види {"text": "Скрыть Authentik brand в базовом authentication page title/brand.", "checked": True}, {"text": "Исключить Authentik My applications dashboard из пользовательского маршрута.", "checked": True}, {"text": "Исключить Authentik application logout dashboard из пользовательского маршрута.", "checked": True}, - "Поддержать returnTo для прямых ссылок на приложения.", - "Поддержать forced login для диагностики.", + {"text": "Поддержать returnTo для прямых ссылок на приложения.", "checked": True}, + {"text": "Поддержать forced login для диагностики.", "checked": True}, "Спроектировать recovery/enrollment/MFA без раскрытия Authentik UI.", - "Проверить, что password/service tokens не попадают во frontend.", + {"text": "Проверить, что password/service tokens не попадают во frontend.", "checked": True}, ], ), text_block( @@ -426,21 +426,21 @@ OIDC flow реализован как Authorization Code + PKCE: state хран App registry возвращает полный каталог приложений из launcher storage и runtime access state: hasAccess, matchedGroups, accessReason. Frontend больше не скрывает недоступные плитки; он отключает переход и показывает "Нет доступа" через существующую механику карточек. -Frontend Launcher подключен к /api/me и /api/apps. Без session показывается нейтральный экран "Вход на платформу NODE.DC" и кнопка "Войти" без упоминания Authentik. После login пользователь нормализуется из OIDC claims, а плитки получают доступы из runtime app registry. +Frontend Launcher подключен к /api/me и /api/apps. Без session Launcher больше не показывает промежуточное окно с кнопкой "Войти": frontend делает прямой replace на /auth/login, а сам /auth/login сразу отдает 302 в Authentik authorize flow. Если пользователь пришел на непустой launcher path, frontend добавляет returnTo к login URL, чтобы после callback вернуть его на исходный маршрут. После login пользователь нормализуется из OIDC claims, а плитки получают доступы из runtime app registry. Проверки 2026-05-04: npm run build проходит; http://launcher.local.nodedc/healthz возвращает oidcConfigured=true; http://launcher.local.nodedc/api/me без session возвращает 401 и loginUrl; http://launcher.local.nodedc/auth/login возвращает 302 на Authentik authorize endpoint; discovery endpoint Authentik для launcher возвращает issuer и authorization_endpoint. Ручная проверка 2026-05-04 выявила callback error {"error":"JSON Web Key Set malformed"}. Root cause: Authentik OAuth2 providers были созданы без signing_key, поэтому JWKS endpoint отдавал {}. Bootstrap исправлен: providers получают authentik Self-signed Certificate как signing key. После повторного bootstrap JWKS отдает RSA key. -Открытый приемочный пункт: ручной browser login/logout через http://launcher.local.nodedc и проверка, что после callback видна плитка Task Manager. +Ручной browser login/logout через http://launcher.local.nodedc подтвержден пользователем: после callback видна витрина Launcher, OPERATIONAL CORE открывает Task Manager, global logout закрывает Launcher и downstream session. -Граница готовности на текущий момент: Launcher готов как базовый OIDC/BFF portal, но не является финально готовым production control plane. Остаются mock/dev элементы и следующий обязательный блок — Plane OIDC. Пока Plane OIDC не реализован, переход из Launcher в task.local.nodedc ожидаемо приводит к старой авторизации Plane, потому что Task Manager еще не доверяет Authentik session. +Граница готовности на текущий момент: Launcher готов как базовый OIDC/BFF portal и local control-plane prototype, но не является финально готовым production control plane. Остаются mock/dev элементы, JSON-backed persistence, recovery/MFA UX и production storage/profile work. 2026-05-04 добавлен явный logout в профильное меню Launcher: кнопка "Выйти" вызывает /auth/logout и чистит local BFF session без ухода в Authentik UI/admin. Это нужно, чтобы пользователь оставался в NODE.DC UX после выхода. SSO-session у identity provider может оставаться активной. Поэтому повторное нажатие "Войти" может вернуть пользователя в Launcher без ввода пароля — это ожидаемое SSO-поведение, а не ошибка. Для диагностики добавлен prompt=login на /auth/login?prompt=login и отдельный global logout через /auth/logout?global=1, но пользовательский logout по умолчанию остается локальным. -2026-05-04 повторная ручная проверка подтвердила целевой UX gap: после нажатия "Войти" пользователь все еще видит стандартное окно Authentik. Это зафиксировано как отдельный backlog-этап NODE.DC login facade; текущий hosted login не считать production-ready. +2026-05-04 login facade переведен на безопасную Authentik-native кастомизацию: Brand/CSS/template JS без proxy над password form. Окно приведено к NODE.DC/Plane визуальному канону, Authentik dashboard/logout application UI исключены из пользовательского маршрута, логотип синхронизирован с Launcher top bar по размеру и позиции. Текущий BFF/OIDC слой является переходной реализацией. Сейчас app access читается из OIDC groups как runtime projection, но целевая source-of-truth модель — Launcher backend: клиенты, членства, группы клиента, user grants, deny exceptions, профиль платформы и audit. Authentik должен получать из Launcher синхронизированную техническую проекцию для SSO/enforcement, а не быть ручной бизнес-админкой. """, @@ -473,7 +473,7 @@ Launcher Admin API должен владеть бизнес-администри {"text": "Добавить admin_audit_log запись для backend mutations.", "checked": True}, {"text": "Подключить frontend admin overlay к новым admin endpoints.", "checked": True}, "Заменить JSON-backed store на production persistence.", - "Реализовать фактический server-side sync в Authentik.", + {"text": "Реализовать фактический server-side sync в Authentik.", "checked": True}, ], ), text_block( @@ -701,7 +701,7 @@ Plane должен принимать Authentik OIDC callback, валидиро {"text": "Проверять nodedc:taskmanager:access.", "checked": True}, {"text": "Искать link по authentik_sub.", "checked": True}, {"text": "Логинить существующего plane_user_id.", "checked": True}, - "Закрыть путь без mapping или app access.", + {"text": "Закрыть путь без mapping или app access.", "checked": True}, ], ), text_block( @@ -739,7 +739,7 @@ Access check завязан на группы Authentik: nodedc:superadmin, node {"text": "Проверять конфликтующий mapping.", "checked": True}, {"text": "Не менять задачи/workspace/memberships.", "checked": True}, {"text": "Отключить публичный signup.", "checked": True}, - "Закрыть лишние OAuth/magic-link обходы, если они нарушают invite/manual модель.", + {"text": "Закрыть лишние OAuth/magic-link обходы, если они нарушают invite/manual модель.", "checked": True}, {"text": "Проверить старый admin после OIDC login.", "checked": True}, ], ), @@ -813,7 +813,7 @@ Plane должен не только пускать пользователя п {"text": "Добавить Task Manager front-channel logout endpoint /logout.", "checked": True}, {"text": "Закрывать app sessions из Launcher global logout перед IdP logout.", "checked": True}, {"text": "Проверить Plane API -> Launcher check из контейнера.", "checked": True}, - "Провести ручной browser acceptance: снять доступ и увидеть отзыв уже открытой Plane-сессии.", + {"text": "Провести ручной browser acceptance: снять доступ и увидеть отзыв уже открытой Plane-сессии.", "checked": True}, ], ), text_block( @@ -945,7 +945,7 @@ Plane должен оставаться самостоятельным прод "security", "Текущая архитектура", """ -Security checklist создан в platform/docs/SECURITY_CHECKLIST.md. Реальных acceptance tests по новой auth architecture пока нет, потому что Authentik/proxy/Launcher OIDC/Plane OIDC еще не реализованы. +Security checklist создан в platform/docs/SECURITY_CHECKLIST.md. Базовый local acceptance по happy path и части negative path уже пройден: Launcher без session уводит в OIDC login, недоступные плитки отображаются как disabled/Нет доступа, снятие доступа отзывает открытую Plane-сессию, старый Plane admin проходит через OIDC migration, frontend bundle не содержит service-token маркеров. Остаются destructive/edge checks: direct deny без group access, deactivated user, audit log и staging-hardening. """, ), text_block( @@ -961,15 +961,15 @@ Security checklist создан в platform/docs/SECURITY_CHECKLIST.md. Реал "security1", "Чекер этапа 1. Security acceptance tests", [ - "Проверить redirect Launcher без логина.", - "Проверить скрытие Task Manager без group access.", + {"text": "Проверить redirect Launcher без логина.", "checked": True}, + {"text": "Проверить скрытие Task Manager без group access.", "checked": True}, "Проверить deny на прямой task.local.nodedc без group access.", - "Проверить отзыв уже открытой downstream-сессии после снятия доступа.", - "Проверить успешный вход пользователя с access.", - "Проверить старого Plane admin после OIDC migration.", + {"text": "Проверить отзыв уже открытой downstream-сессии после снятия доступа.", "checked": True}, + {"text": "Проверить успешный вход пользователя с access.", "checked": True}, + {"text": "Проверить старого Plane admin после OIDC migration.", "checked": True}, "Проверить deactivate user.", "Проверить audit log admin actions.", - "Проверить отсутствие service tokens во frontend bundle.", + {"text": "Проверить отсутствие service tokens во frontend bundle.", "checked": True}, ], ), text_block(