АРХ - NODEDC PLATFORM: каркас платформенного репозитория
This commit is contained in:
commit
0f89c4d126
|
|
@ -0,0 +1,24 @@
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# local env and secrets
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
!.env.*.example
|
||||||
|
|
||||||
|
# dependencies and build output
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.turbo/
|
||||||
|
.next/
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# local runtime data
|
||||||
|
tmp/
|
||||||
|
data/
|
||||||
|
volumes/
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
# NODE.DC Platform
|
||||||
|
|
||||||
|
Платформенный слой для multi-app authentication architecture NODE.DC.
|
||||||
|
|
||||||
|
Эта папка не является монорепозиторием всех приложений. Здесь живут общая архитектура, Authentik/reverse-proxy инфраструктура, правила локального разворачивания, auth model, migration plan для Plane и будущий общий auth-sdk.
|
||||||
|
|
||||||
|
Git repo:
|
||||||
|
|
||||||
|
- `origin`: `https://git.dcserve.ru/SILVER/NODEDC_PLATFORM.git`
|
||||||
|
- local branch: `main`
|
||||||
|
|
||||||
|
Текущие приложения остаются отдельными репозиториями:
|
||||||
|
|
||||||
|
- Launcher: `/Users/dcconstructions/Downloads/mnt/data/nodedc_launcher`
|
||||||
|
- Task Manager / Plane fork: `/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER`
|
||||||
|
- Plane runtime: `/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-app`
|
||||||
|
- Plane source fork: `/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-src`
|
||||||
|
|
||||||
|
## Нулевой этап
|
||||||
|
|
||||||
|
Нулевой этап фиксирует discovery и каркас платформы без изменения бизнес-логики Launcher и Plane.
|
||||||
|
|
||||||
|
Артефакты:
|
||||||
|
|
||||||
|
- `docs/DISCOVERY_REPORT.md`
|
||||||
|
- `docs/ARCHITECTURE.md`
|
||||||
|
- `docs/AUTH_MODEL.md`
|
||||||
|
- `docs/DEPLOYMENT_LOCAL.md`
|
||||||
|
- `docs/SECURITY_CHECKLIST.md`
|
||||||
|
- `docs/MIGRATION_PLANE_USER.md`
|
||||||
|
- `infra/README.md`
|
||||||
|
- `packages/auth-sdk/README.md`
|
||||||
|
- `tasks/CODEX_PLATFORM_AUTH_TASK.md`
|
||||||
|
|
||||||
|
## Базовое правило
|
||||||
|
|
||||||
|
Plane не переносится внутрь Launcher. Launcher не становится владельцем таблиц Plane. Authentik становится единым Identity Provider, а приложения сохраняют собственные доменные БД и роли.
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
# Architecture: NODE.DC Multi-App Platform
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Собрать единую платформенную основу для Launcher, Task Manager / Plane и будущих приложений NODE.DC.
|
||||||
|
|
||||||
|
Целевой пользовательский сценарий:
|
||||||
|
|
||||||
|
1. Пользователь открывает Launcher.
|
||||||
|
2. Если сессии нет, его отправляет в Authentik.
|
||||||
|
3. После логина Launcher получает identity и app access.
|
||||||
|
4. Launcher показывает только доступные приложения.
|
||||||
|
5. Переход в Task Manager не требует повторного логина.
|
||||||
|
6. Прямой URL Task Manager тоже защищен.
|
||||||
|
|
||||||
|
## Роли компонентов
|
||||||
|
|
||||||
|
`Authentik` является единым Identity Provider:
|
||||||
|
|
||||||
|
- логин;
|
||||||
|
- пароль;
|
||||||
|
- активность пользователя;
|
||||||
|
- группы;
|
||||||
|
- app access;
|
||||||
|
- OIDC claims;
|
||||||
|
- будущие invite/enrollment/MFA flows.
|
||||||
|
|
||||||
|
`Launcher` является control plane:
|
||||||
|
|
||||||
|
- входная точка пользователя;
|
||||||
|
- список доступных приложений;
|
||||||
|
- admin UI управления пользователями и доступами;
|
||||||
|
- backend-интеграция с Authentik API;
|
||||||
|
- audit log админских действий.
|
||||||
|
|
||||||
|
`Task Manager / Plane` остается отдельным приложением:
|
||||||
|
|
||||||
|
- собственная БД;
|
||||||
|
- собственные workspace/project/task/comment модели;
|
||||||
|
- собственные роли workspace/project;
|
||||||
|
- OIDC login через Authentik;
|
||||||
|
- mapping внешней identity на существующего Plane user.
|
||||||
|
|
||||||
|
`Reverse proxy` является внешним защитным и маршрутизирующим слоем:
|
||||||
|
|
||||||
|
- единая точка входа;
|
||||||
|
- HTTPS в staging/production;
|
||||||
|
- routing по app domain;
|
||||||
|
- forward headers для Authentik;
|
||||||
|
- deny для пользователей без app access.
|
||||||
|
|
||||||
|
## Repository layout
|
||||||
|
|
||||||
|
Рабочая схема:
|
||||||
|
|
||||||
|
```text
|
||||||
|
NODEDC/
|
||||||
|
DOC/
|
||||||
|
platform/
|
||||||
|
docs/
|
||||||
|
infra/
|
||||||
|
packages/
|
||||||
|
tasks/
|
||||||
|
|
||||||
|
/Users/dcconstructions/Downloads/mnt/data/nodedc_launcher
|
||||||
|
/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER
|
||||||
|
```
|
||||||
|
|
||||||
|
`platform/` не забирает исходники Launcher и Plane внутрь себя. Он хранит только платформенный слой и договоренности между приложениями.
|
||||||
|
|
||||||
|
## Data ownership
|
||||||
|
|
||||||
|
Не создается единая общая таблица `users` для всех приложений.
|
||||||
|
|
||||||
|
Логическая схема:
|
||||||
|
|
||||||
|
```text
|
||||||
|
authentik_db -> identity, groups, app access
|
||||||
|
launcher_db -> app registry, local profiles, audit
|
||||||
|
plane_db -> workspace, projects, tasks, comments, app roles
|
||||||
|
future_app_db -> доменная логика будущих приложений
|
||||||
|
```
|
||||||
|
|
||||||
|
Связь между identity и локальными пользователями выполняется через explicit mapping, а не через прямое чтение чужих таблиц.
|
||||||
|
|
||||||
|
## Auth flow
|
||||||
|
|
||||||
|
Для приложений, которые контролируются кодом:
|
||||||
|
|
||||||
|
- OIDC Authorization Code Flow + PKCE;
|
||||||
|
- backend/session или BFF layer;
|
||||||
|
- JWT/JWKS validation server-side;
|
||||||
|
- проверка `issuer`, `audience`, `exp`, `sub`;
|
||||||
|
- проверка app access group;
|
||||||
|
- локальный user profile или external identity link.
|
||||||
|
|
||||||
|
Для legacy/временных приложений допускается reverse proxy forward-auth, но это временный внешний слой, а не единственная долгосрочная модель.
|
||||||
|
|
||||||
|
## Plane migration rule
|
||||||
|
|
||||||
|
Существующий Plane user нельзя пересоздавать.
|
||||||
|
|
||||||
|
Нужен mapping:
|
||||||
|
|
||||||
|
```text
|
||||||
|
authentik_user.sub -> existing plane_user.id
|
||||||
|
```
|
||||||
|
|
||||||
|
Plane должен логинить существующего пользователя по link, сохраняя все старые workspace, assignee, owner, created_by, comments и attachments.
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
# Auth Model
|
||||||
|
|
||||||
|
## Identity source
|
||||||
|
|
||||||
|
Единственный источник identity: Authentik.
|
||||||
|
|
||||||
|
Launcher, Plane и будущие приложения не хранят пароли пользователей и не становятся главным источником truth по логину.
|
||||||
|
|
||||||
|
## Required claims
|
||||||
|
|
||||||
|
Минимальный normalized user object:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type AuthUser = {
|
||||||
|
sub: string;
|
||||||
|
email: string;
|
||||||
|
name?: string;
|
||||||
|
groups: string[];
|
||||||
|
entitlements?: string[];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Обязательные проверки:
|
||||||
|
|
||||||
|
- `iss` совпадает с настроенным issuer;
|
||||||
|
- `aud` совпадает с client/application audience;
|
||||||
|
- `exp` не истек;
|
||||||
|
- `sub` непустой и стабилен;
|
||||||
|
- `email` присутствует для user-facing приложений;
|
||||||
|
- `groups` или `entitlements` содержат требуемый app access.
|
||||||
|
|
||||||
|
## Groups
|
||||||
|
|
||||||
|
Минимальная группа верхнего уровня:
|
||||||
|
|
||||||
|
```text
|
||||||
|
nodedc:superadmin
|
||||||
|
```
|
||||||
|
|
||||||
|
Launcher:
|
||||||
|
|
||||||
|
```text
|
||||||
|
nodedc:launcher:access
|
||||||
|
nodedc:launcher:admin
|
||||||
|
nodedc:launcher:user-manager
|
||||||
|
```
|
||||||
|
|
||||||
|
Task Manager:
|
||||||
|
|
||||||
|
```text
|
||||||
|
nodedc:taskmanager:access
|
||||||
|
nodedc:taskmanager:admin
|
||||||
|
```
|
||||||
|
|
||||||
|
Future apps:
|
||||||
|
|
||||||
|
```text
|
||||||
|
nodedc:agents:access
|
||||||
|
nodedc:tender:access
|
||||||
|
nodedc:onec:access
|
||||||
|
nodedc:dm:access
|
||||||
|
```
|
||||||
|
|
||||||
|
## Access levels
|
||||||
|
|
||||||
|
Уровень 1: Authentik app access.
|
||||||
|
|
||||||
|
Отвечает на вопрос, можно ли открыть приложение.
|
||||||
|
|
||||||
|
Уровень 2: Launcher admin roles.
|
||||||
|
|
||||||
|
Отвечает на вопрос, можно ли управлять пользователями, группами, app registry и audit.
|
||||||
|
|
||||||
|
Уровень 3: Application roles.
|
||||||
|
|
||||||
|
Например, Plane workspace owner/admin/member/viewer. Эти роли остаются внутри Plane.
|
||||||
|
|
||||||
|
## Launcher backend requirements
|
||||||
|
|
||||||
|
Backend должен:
|
||||||
|
|
||||||
|
- хранить Authentik service token только server-side;
|
||||||
|
- выполнять admin calls к Authentik API;
|
||||||
|
- хранить audit log;
|
||||||
|
- возвращать frontend только нормализованные данные пользователя и разрешенные действия;
|
||||||
|
- не отдавать access/refresh/service tokens в browser bundle.
|
||||||
|
|
||||||
|
## Plane identity link
|
||||||
|
|
||||||
|
Минимальная таблица или эквивалентная модель в Plane:
|
||||||
|
|
||||||
|
```text
|
||||||
|
external_identity_link
|
||||||
|
provider = authentik
|
||||||
|
authentik_sub
|
||||||
|
email
|
||||||
|
plane_user_id
|
||||||
|
created_at
|
||||||
|
last_login_at
|
||||||
|
status
|
||||||
|
```
|
||||||
|
|
||||||
|
Правило миграции:
|
||||||
|
|
||||||
|
- сначала искать link по `authentik_sub`;
|
||||||
|
- если link найден, логинить связанный `plane_user_id`;
|
||||||
|
- если link не найден, но email совпадает и включен migration auto-link, создать link;
|
||||||
|
- если доступа нет, вернуть deny.
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
# Local Deployment Plan
|
||||||
|
|
||||||
|
## Domains
|
||||||
|
|
||||||
|
Для локальной разработки целевая схема:
|
||||||
|
|
||||||
|
```text
|
||||||
|
127.0.0.1 auth.local.nodedc
|
||||||
|
127.0.0.1 launcher.local.nodedc
|
||||||
|
127.0.0.1 task.local.nodedc
|
||||||
|
127.0.0.1 agents.local.nodedc
|
||||||
|
127.0.0.1 tender.local.nodedc
|
||||||
|
127.0.0.1 onec.local.nodedc
|
||||||
|
127.0.0.1 dm.local.nodedc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Current local apps
|
||||||
|
|
||||||
|
Launcher сейчас запускается как Vite app из:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/Users/dcconstructions/Downloads/mnt/data/nodedc_launcher
|
||||||
|
```
|
||||||
|
|
||||||
|
Task Manager сейчас доступен как Plane self-host runtime:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8090
|
||||||
|
```
|
||||||
|
|
||||||
|
Runtime:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-app
|
||||||
|
```
|
||||||
|
|
||||||
|
Source fork:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-src
|
||||||
|
```
|
||||||
|
|
||||||
|
## Target local flow
|
||||||
|
|
||||||
|
Первый local infra milestone:
|
||||||
|
|
||||||
|
```text
|
||||||
|
auth.local.nodedc -> Authentik
|
||||||
|
launcher.local.nodedc -> Launcher web/backend
|
||||||
|
task.local.nodedc -> Plane proxy/runtime
|
||||||
|
```
|
||||||
|
|
||||||
|
Все внешние запросы идут через reverse proxy.
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
Базовые переменные должны жить в:
|
||||||
|
|
||||||
|
```text
|
||||||
|
platform/infra/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
Пример значений хранится в:
|
||||||
|
|
||||||
|
```text
|
||||||
|
platform/infra/.env.example
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation order
|
||||||
|
|
||||||
|
1. Согласовать локальные domain/port bindings.
|
||||||
|
2. Добавить reverse proxy config.
|
||||||
|
3. Поднять Authentik server, worker, Postgres и Redis.
|
||||||
|
4. Прокинуть Authentik за proxy с корректными headers.
|
||||||
|
5. Подключить текущий Launcher как внешний сервис или через compose service.
|
||||||
|
6. Подключить текущий Plane runtime как внешний service target.
|
||||||
|
7. После стабилизации собрать воспроизводимый compose.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Текущий Plane runtime не переносится на нулевом этапе. Любое изменение `plane-app/plane.env`, volumes или startup scripts делается только после backup и отдельной задачи.
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
# Discovery Report: NODE.DC Platform
|
||||||
|
|
||||||
|
Дата: 2026-05-04
|
||||||
|
|
||||||
|
## Источники
|
||||||
|
|
||||||
|
- Базовое ТЗ: `/Users/dcconstructions/Downloads/mnt/NODEDC/DOC/BASE/tz_codex_platform_authentik_launcher_plane.md`
|
||||||
|
- Краткое ТЗ: `/Users/dcconstructions/Downloads/mnt/NODEDC/DOC/BASE/nodedc_auth.md`
|
||||||
|
- Launcher repo: `/Users/dcconstructions/Downloads/mnt/data/nodedc_launcher`
|
||||||
|
- Task Manager repo: `/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER`
|
||||||
|
|
||||||
|
## Workspace
|
||||||
|
|
||||||
|
`/Users/dcconstructions/Downloads/mnt/NODEDC` используется как внешний workspace-корень для платформенной работы.
|
||||||
|
|
||||||
|
Текущая папка `NODEDC` до нулевого этапа содержала только базовые документы в `DOC/BASE`. Отдельного git-репозитория в этой папке нет.
|
||||||
|
|
||||||
|
## Launcher
|
||||||
|
|
||||||
|
Путь: `/Users/dcconstructions/Downloads/mnt/data/nodedc_launcher`
|
||||||
|
|
||||||
|
Факты discovery:
|
||||||
|
|
||||||
|
- отдельный git-репозиторий;
|
||||||
|
- Vite/React приложение;
|
||||||
|
- основной entrypoint: `src/main.tsx`;
|
||||||
|
- команды из `package.json`: `npm run dev`, `npm run build`, `npm run preview`, `npm run test`;
|
||||||
|
- backend слоя в текущем Launcher не найдено;
|
||||||
|
- значит OIDC/session handling, Authentik service token, app registry и audit log нельзя хранить только во frontend.
|
||||||
|
|
||||||
|
Минимальный вывод: для production-like auth нужен Launcher backend или BFF слой.
|
||||||
|
|
||||||
|
## Task Manager / Plane
|
||||||
|
|
||||||
|
Путь: `/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER`
|
||||||
|
|
||||||
|
Факты discovery:
|
||||||
|
|
||||||
|
- отдельный git-репозиторий;
|
||||||
|
- self-host runtime лежит в `plane-app`;
|
||||||
|
- локальный fork исходников лежит в `plane-src`;
|
||||||
|
- Plane CE self-host запущен через Docker;
|
||||||
|
- локальный URL стенда: `http://localhost:8090`;
|
||||||
|
- текущий compose/env: `plane-app/docker-compose.yaml` и `plane-app/plane.env`;
|
||||||
|
- текущий fork Plane: `plane-src`, версия `1.3.0`;
|
||||||
|
- основной runtime-контейнер API: `plane-app-api-1`;
|
||||||
|
- активные контейнеры Plane: web, proxy, api, worker, beat-worker, db, redis, minio, mq, admin, space, live.
|
||||||
|
|
||||||
|
Текущий workspace в Plane:
|
||||||
|
|
||||||
|
- slug: `nodedc`;
|
||||||
|
- name: `NODE DC`;
|
||||||
|
- owner: `dcctouch@gmail.com`;
|
||||||
|
- codex user: `codex@nodedc.local`.
|
||||||
|
|
||||||
|
Существующие проекты в workspace `nodedc` на момент discovery:
|
||||||
|
|
||||||
|
- `NODEDCLAUN` / `NODEDC LAUNCHER`
|
||||||
|
- `CODEX` / `DCTM-WT-CODEX`
|
||||||
|
- `NODEDCTASK` / `NODEDC TASKMANAGER`
|
||||||
|
- `MGR` / `Менеджмент`
|
||||||
|
- `BUH` / `Бухгалтерия`
|
||||||
|
|
||||||
|
## NDC details в карточках
|
||||||
|
|
||||||
|
Канон карточек описан в `/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/AGENTS.md`.
|
||||||
|
|
||||||
|
Структурированные блоки карточки хранятся в `Issue.detail_layout` под ключом:
|
||||||
|
|
||||||
|
```text
|
||||||
|
nodedc_structured_blocks
|
||||||
|
```
|
||||||
|
|
||||||
|
Типы блоков:
|
||||||
|
|
||||||
|
- `text`: текстовый блок с `title` и `body`;
|
||||||
|
- `checker`: чекер с `title` и пунктами `items`.
|
||||||
|
|
||||||
|
Frontend отображает эти блоки через компоненты:
|
||||||
|
|
||||||
|
- `plane-src/apps/web/core/components/issues/issue-detail-widgets/structured-content.helpers.ts`
|
||||||
|
- `plane-src/apps/web/core/components/issues/issue-detail-widgets/structured-content-blocks.tsx`
|
||||||
|
|
||||||
|
## Решение по переносам
|
||||||
|
|
||||||
|
Физически переносить Launcher и Task Manager внутрь `NODEDC` на нулевом этапе не нужно.
|
||||||
|
|
||||||
|
Причины:
|
||||||
|
|
||||||
|
- оба приложения уже являются отдельными git-репозиториями;
|
||||||
|
- Plane runtime завязан на текущие `plane-app`, `plane.env`, volumes и backup;
|
||||||
|
- перенос может сломать локальный self-host стенд;
|
||||||
|
- платформенный слой должен управлять интеграцией, а не становиться монорепозиторием всех исходников.
|
||||||
|
|
||||||
|
Допустимый вариант позже: добавить symlink или workspace manifest из `NODEDC` на внешние репозитории, если это потребуется для удобства навигации.
|
||||||
|
|
||||||
|
## Риски
|
||||||
|
|
||||||
|
- Нельзя пересоздавать существующего Plane admin/user, иначе потеряются связи workspace, задач, комментариев и assignee.
|
||||||
|
- Нельзя делать Authentik admin token доступным frontend-коду Launcher.
|
||||||
|
- Нельзя полагаться только на reverse proxy как единственный долгосрочный auth layer для приложений, которые мы контролируем кодом.
|
||||||
|
- Нельзя публиковать наружу Postgres, Redis, MinIO и внутренние API в staging/production.
|
||||||
|
|
||||||
|
## Следующий технический шаг
|
||||||
|
|
||||||
|
Создать локальный `platform/infra` слой с Authentik и reverse proxy, но сначала согласовать конкретную схему доменов, ports и secrets.
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
# Plane User Migration Plan
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Связать существующего пользователя Plane с Authentik identity без потери workspace, projects, tasks, comments, attachments и истории.
|
||||||
|
|
||||||
|
## Current baseline
|
||||||
|
|
||||||
|
Текущий рабочий Plane workspace:
|
||||||
|
|
||||||
|
```text
|
||||||
|
workspace slug: nodedc
|
||||||
|
workspace name: NODE DC
|
||||||
|
workspace owner: dcctouch@gmail.com
|
||||||
|
codex user: codex@nodedc.local
|
||||||
|
```
|
||||||
|
|
||||||
|
Перед реализацией OIDC нужно дополнительно снять точный snapshot:
|
||||||
|
|
||||||
|
- users;
|
||||||
|
- profiles;
|
||||||
|
- workspace memberships;
|
||||||
|
- project memberships;
|
||||||
|
- issue ownership;
|
||||||
|
- issue assignees;
|
||||||
|
- comments;
|
||||||
|
- attachments;
|
||||||
|
- uploaded files/storage.
|
||||||
|
|
||||||
|
## Backup before changes
|
||||||
|
|
||||||
|
Перед любыми изменениями Plane auth:
|
||||||
|
|
||||||
|
```text
|
||||||
|
plane_db dump
|
||||||
|
plane-app/plane.env
|
||||||
|
uploads / MinIO volumes
|
||||||
|
redis, если в нем есть важные очереди/сессии
|
||||||
|
rabbitmq, если есть невыполненные jobs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mapping model
|
||||||
|
|
||||||
|
Минимальная модель:
|
||||||
|
|
||||||
|
```text
|
||||||
|
external_identity_link
|
||||||
|
id
|
||||||
|
provider = authentik
|
||||||
|
authentik_sub
|
||||||
|
email
|
||||||
|
plane_user_id
|
||||||
|
created_at
|
||||||
|
last_login_at
|
||||||
|
status
|
||||||
|
```
|
||||||
|
|
||||||
|
Уникальность:
|
||||||
|
|
||||||
|
- `provider + authentik_sub`;
|
||||||
|
- `provider + plane_user_id`;
|
||||||
|
- желательно `provider + email`, если email используется для migration auto-link.
|
||||||
|
|
||||||
|
## Login flow
|
||||||
|
|
||||||
|
```text
|
||||||
|
Authentik login
|
||||||
|
-> Plane OIDC callback
|
||||||
|
-> validate state/nonce/token
|
||||||
|
-> validate issuer/audience/exp/sub
|
||||||
|
-> check nodedc:taskmanager:access
|
||||||
|
-> find external_identity_link by authentik_sub
|
||||||
|
-> login existing plane_user_id
|
||||||
|
```
|
||||||
|
|
||||||
|
Migration auto-link допускается только контролируемо:
|
||||||
|
|
||||||
|
```text
|
||||||
|
if no link and email matches existing Plane user and migration mode enabled:
|
||||||
|
create external_identity_link
|
||||||
|
else:
|
||||||
|
deny or provisioning flow
|
||||||
|
```
|
||||||
|
|
||||||
|
## Forbidden during migration
|
||||||
|
|
||||||
|
- Не пересоздавать существующего Plane user.
|
||||||
|
- Не менять `created_by`.
|
||||||
|
- Не менять `owner_id`.
|
||||||
|
- Не менять `assignee_id`.
|
||||||
|
- Не менять `member_id`.
|
||||||
|
- Не делать bulk update связей задач без отдельной проверки.
|
||||||
|
|
||||||
|
## Management command target
|
||||||
|
|
||||||
|
Целевая команда:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python manage.py link_authentik_user --email admin@example.ru --sub <authentik-sub>
|
||||||
|
```
|
||||||
|
|
||||||
|
Требования:
|
||||||
|
|
||||||
|
- idempotent;
|
||||||
|
- проверяет конфликтующий mapping;
|
||||||
|
- ничего не меняет в задачах/workspace;
|
||||||
|
- выводит summary;
|
||||||
|
- поддерживает dry-run.
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Security Checklist
|
||||||
|
|
||||||
|
## Network
|
||||||
|
|
||||||
|
- [ ] Наружу опубликованы только reverse proxy ports.
|
||||||
|
- [ ] Postgres не опубликован наружу в staging/production.
|
||||||
|
- [ ] Redis не опубликован наружу в staging/production.
|
||||||
|
- [ ] MinIO/storage не опубликован наружу в staging/production.
|
||||||
|
- [ ] Внутренние API не доступны напрямую извне.
|
||||||
|
- [ ] Authentik получает корректные `X-Forwarded-Proto`, `X-Forwarded-For`, `Host`.
|
||||||
|
- [ ] WebSocket headers настроены для Authentik/Plane/live.
|
||||||
|
|
||||||
|
## Authentik
|
||||||
|
|
||||||
|
- [ ] Каждое приложение имеет отдельный Authentik Application.
|
||||||
|
- [ ] Каждое приложение имеет отдельный Provider.
|
||||||
|
- [ ] Для каждого приложения задана отдельная access policy.
|
||||||
|
- [ ] Группы app access заведены отдельно от app-local ролей.
|
||||||
|
- [ ] MFA/enrollment policy вынесены в отдельный этап.
|
||||||
|
|
||||||
|
## Launcher
|
||||||
|
|
||||||
|
- [ ] Authentik service token хранится только server-side.
|
||||||
|
- [ ] Frontend не получает service token.
|
||||||
|
- [ ] Admin endpoints требуют `nodedc:superadmin` или `nodedc:launcher:admin`.
|
||||||
|
- [ ] Все admin actions пишутся в audit log.
|
||||||
|
- [ ] Удаление пользователя реализовано как deactivate/disable, не hard delete.
|
||||||
|
|
||||||
|
## Plane
|
||||||
|
|
||||||
|
- [ ] Перед изменениями сделан backup DB/env/uploads/storage.
|
||||||
|
- [ ] Существующий Plane user не пересоздается.
|
||||||
|
- [ ] `owner_id`, `created_by`, `assignee_id`, `member_id` не меняются без отдельной миграции.
|
||||||
|
- [ ] Публичный signup отключен.
|
||||||
|
- [ ] Лишние обходные auth сценарии закрыты или явно оставлены как временные.
|
||||||
|
- [ ] OIDC login проверяет state/nonce/token.
|
||||||
|
- [ ] `authentik_sub` связан с существующим `plane_user_id`.
|
||||||
|
|
||||||
|
## Tokens and secrets
|
||||||
|
|
||||||
|
- [ ] Secrets не попадают в git.
|
||||||
|
- [ ] Access/refresh tokens не логируются.
|
||||||
|
- [ ] Session cookies имеют `secure=true` в staging/production.
|
||||||
|
- [ ] В production включены HTTPS и HSTS.
|
||||||
|
|
||||||
|
## Acceptance scenarios
|
||||||
|
|
||||||
|
- [ ] Без логина Launcher отправляет в Authentik.
|
||||||
|
- [ ] Пользователь без `nodedc:taskmanager:access` не видит Task Manager в Launcher.
|
||||||
|
- [ ] Пользователь без `nodedc:taskmanager:access` получает deny на прямой `task.local.nodedc`.
|
||||||
|
- [ ] Пользователь с доступом открывает Task Manager.
|
||||||
|
- [ ] Старый Plane admin после OIDC видит старые workspace/tasks/comments.
|
||||||
|
- [ ] Деактивированный пользователь теряет доступ.
|
||||||
|
- [ ] Admin action появляется в audit log.
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
# domains
|
||||||
|
AUTH_DOMAIN=auth.local.nodedc
|
||||||
|
LAUNCHER_DOMAIN=launcher.local.nodedc
|
||||||
|
TASK_DOMAIN=task.local.nodedc
|
||||||
|
|
||||||
|
# authentik
|
||||||
|
AUTHENTIK_SECRET_KEY=
|
||||||
|
AUTHENTIK_POSTGRES_PASSWORD=
|
||||||
|
AUTHENTIK_REDIS_HOST=redis-authentik
|
||||||
|
|
||||||
|
# launcher oidc
|
||||||
|
LAUNCHER_OIDC_ISSUER=http://auth.local.nodedc/application/o/launcher/
|
||||||
|
LAUNCHER_OIDC_CLIENT_ID=
|
||||||
|
LAUNCHER_OIDC_CLIENT_SECRET=
|
||||||
|
LAUNCHER_OIDC_REDIRECT_URI=http://launcher.local.nodedc/auth/callback
|
||||||
|
|
||||||
|
# plane oidc
|
||||||
|
PLANE_OIDC_ISSUER=http://auth.local.nodedc/application/o/task-manager/
|
||||||
|
PLANE_OIDC_CLIENT_ID=
|
||||||
|
PLANE_OIDC_CLIENT_SECRET=
|
||||||
|
PLANE_OIDC_REDIRECT_URI=http://task.local.nodedc/auth/oidc/callback
|
||||||
|
|
||||||
|
# security
|
||||||
|
SESSION_SECRET=
|
||||||
|
COOKIE_DOMAIN=.local.nodedc
|
||||||
|
COOKIE_SECURE=false
|
||||||
|
|
||||||
|
# current external local apps
|
||||||
|
LOCAL_LAUNCHER_URL=http://localhost:5173
|
||||||
|
LOCAL_TASK_MANAGER_URL=http://localhost:8090
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
# NODE.DC Platform Infra
|
||||||
|
|
||||||
|
Эта папка предназначена для локального и staging infra слоя:
|
||||||
|
|
||||||
|
- Authentik;
|
||||||
|
- reverse proxy;
|
||||||
|
- локальные домены;
|
||||||
|
- shared env examples;
|
||||||
|
- будущие docker compose файлы.
|
||||||
|
|
||||||
|
На нулевом этапе здесь фиксируется только каркас. Рабочий `docker-compose.dev.yml` создается отдельным этапом после согласования ports/domains и стратегии подключения текущих Launcher/Plane runtime.
|
||||||
|
|
||||||
|
## Expected files
|
||||||
|
|
||||||
|
```text
|
||||||
|
infra/
|
||||||
|
.env.example
|
||||||
|
docker-compose.dev.yml
|
||||||
|
docker-compose.staging.yml
|
||||||
|
reverse-proxy/
|
||||||
|
authentik/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Current decision
|
||||||
|
|
||||||
|
Текущий Plane runtime не переносится в compose платформы до backup и отдельного шага миграции.
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
# NODE.DC Auth SDK
|
||||||
|
|
||||||
|
Будущий общий пакет для приложений, которые контролируются кодом NODE.DC.
|
||||||
|
|
||||||
|
## Target responsibilities
|
||||||
|
|
||||||
|
- загрузка JWKS;
|
||||||
|
- валидация JWT;
|
||||||
|
- проверка issuer/audience/exp;
|
||||||
|
- нормализация claims;
|
||||||
|
- helper `requireAppAccess(groupName)`;
|
||||||
|
- helper `getCurrentUser()`;
|
||||||
|
- typed `AuthUser`.
|
||||||
|
|
||||||
|
## Type contract
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export type AuthUser = {
|
||||||
|
sub: string;
|
||||||
|
email: string;
|
||||||
|
name?: string;
|
||||||
|
groups: string[];
|
||||||
|
entitlements?: string[];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
Первый SDK рассчитан на Launcher backend и будущие Node.js/Next.js сервисы.
|
||||||
|
|
||||||
|
Для Plane fork на Python/Django нужна отдельная реализация middleware по тем же правилам, а этот пакет остается спецификацией для TypeScript приложений.
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
# CODEX Platform Auth Task
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Построить NODE.DC platform auth architecture:
|
||||||
|
|
||||||
|
- Authentik как единый Identity Provider;
|
||||||
|
- Launcher как access/admin control plane;
|
||||||
|
- Plane fork как отдельное приложение;
|
||||||
|
- reverse proxy как внешний защитный слой;
|
||||||
|
- сохранение существующего Plane user и всех связанных данных;
|
||||||
|
- поддержка будущих приложений.
|
||||||
|
|
||||||
|
## Hard constraints
|
||||||
|
|
||||||
|
- Не класть Plane внутрь Launcher.
|
||||||
|
- Не делать единую `users` таблицу для всех приложений.
|
||||||
|
- Не ломать существующего Plane user.
|
||||||
|
- Не менять `owner`, `assignee`, `created_by`, `member` связи Plane без отдельной миграции.
|
||||||
|
- Не хранить service tokens во frontend.
|
||||||
|
- Не использовать reverse-proxy forward-auth как единственный долгосрочный auth mechanism для собственных приложений.
|
||||||
|
|
||||||
|
## Phase 0 result
|
||||||
|
|
||||||
|
Выполнены:
|
||||||
|
|
||||||
|
- discovery текущих путей Launcher и Task Manager;
|
||||||
|
- фиксация решения не переносить репозитории физически;
|
||||||
|
- platform skeleton;
|
||||||
|
- базовые документы архитектуры, auth model, local deployment, security и migration plan.
|
||||||
|
|
||||||
|
## Next phases
|
||||||
|
|
||||||
|
1. Local domains + reverse proxy.
|
||||||
|
2. Authentik bootstrap.
|
||||||
|
3. Launcher backend/BFF.
|
||||||
|
4. Launcher OIDC login and app filtering.
|
||||||
|
5. Launcher admin API and audit.
|
||||||
|
6. Plane OIDC integration.
|
||||||
|
7. Plane user migration command.
|
||||||
|
8. Security acceptance and staging path.
|
||||||
Loading…
Reference in New Issue