--- title: "Аудит Leadera — полный отчёт" date: 2026-03-30 lastmod: 2026-03-30 tags: ["audit", "leadera", "security", "architecture"] weight: 100 --- # Аудит проекта Leadera **Дата:** 30.03.2026 **Версия:** pre-production, миграции 1–5 --- ## 1. Обзор проекта Leadera — SaaS-система управления IT-ландшафтом с диаграммами Ганта. ### Стек | Компонент | Технологии | |-----------|-----------| | Backend | Go 1.21+, Gin, PostgreSQL 16, pgx/v5 + sqlx, JWT (HMAC-SHA256) | | Frontend | Angular 21.1, Bootstrap 5, NgBootstrap, RxJS, Signals | | Инфраструктура | Docker (Colima), pm2, nginx reverse proxy, VPS reg.ru | ### Доменная модель ``` User ──┬── Space (через space_members с role) │ └── GanttChart (принадлежит Space) ├── GanttSection └── GanttTask ``` **Роли:** admin, editor, viewer — назначаются **на уровне Space** через таблицу `space_members`. ### Что реализовано (endpoints) **Auth (5):** register, login, refresh, change-password, logout **Spaces (9):** CRUD + members CRUD **Gantt Charts (9):** CRUD + archive + members (добавлен, но удалён в миграции 5) **Gantt Sections (5):** CRUD + reorder **Gantt Tasks (4):** CRUD **Итого: ~32 endpoint** --- ## 2. Архитектура и качество кода ### 2.1. Backend — структура ``` cmd/server/main.go — точка входа internal/ domain/ — модели (User, Space, GanttChart, etc.) dto/ — request/response DTO (auth, space, gantt) handlers/ — HTTP handlers (auth, space, gantt) middleware/ — auth, cors, space, gantt, role, logging, recovery repository/ — слой доступа к данным service/ — бизнес-логика config/ — конфигурация (.env) utils/ — jwt, password, response, validation migrations/ — SQL миграции (1-5) ``` **Оценка:** ✅ Чистая layered архитектура. Разделение ответственности чёткое. ### 2.2. Frontend — структура ``` src/app/ core/auth/ — auth service, interceptor core/models/ — app.model, space.model core/services/ — space.service, space-storage.service pages/auth/ — sign-in, sign-up, reset-pw, etc. pages/gantt/ — компоненты, модалки, сервисы, модели pages/dashboards/ — spaces list, dashboard layouts/ — navbar, topbar, main-layout ``` **Оценка:** ✅ Стандартная Angular-структура. Signals для state management. ### 2.3. Проблемы кода | # | Проблема | Критичность | Описание | |---|----------|-------------|----------| | C1 | `\r` в service-файлах | Medium | gantt_service.go, gantt_section_service.go содержат CRLF | | C2 | `main.go:238` — мёртвый код `getUserHandler` | Low | Неиспользуемая функция | | C3 | Swagger не сгенерирован | Medium | Аннотации есть, но `api/` директории нет | | C4 | `RegisterProtectedRoutes` не вызывается в main.go | **Critical** | GET/PUT/DELETE `/spaces/:id` не зарегистрированы | --- ## 3. Права доступа — главный вопрос ### 3.1. Текущая реализация Права назначаются **исключительно через Space**: 1. Таблица `space_members` — связка user ↔ space с role (admin/editor/viewer) 2. `SpaceMiddleware.RequireSpace()` — проверяет членство и записывает `ContextSpaceRole` в context 3. `RoleMiddleware.RequireMinRole()` — проверяет иерархию ролей (admin > editor > viewer) **Диаграммы Ганта НЕ имеют собственной системы ролей.** Таблица `gantt_chart_members` была создана (миграция 4) и тут же удалена (миграция 5). Все проверки доступа к диаграммам идут через space membership. ### 3.2. Как это работает ``` Запрос → AuthMiddleware (JWT → userID) → SpaceMiddleware.RequireSpace() (проверяет space_members → ContextSpaceRole) → GanttMiddleware.RequireGanttChart() (проверяет chart.space_id == spaceID) → RoleMiddleware.RequireMinRole("editor") (проверяет ContextSpaceRole) ``` **Это правильно.** Любой член Space с ролью editor+ может создавать/редактировать диаграммы. Viewers — только читать. ### 3.3. 🔴 Критическая проблема: Frontend не соответствует Backend **Frontend `MembersModalComponent`** пытается управлять участниками **диаграммы**: - `ganttService.addMember(chartId, dto)` → `/api/v1/spaces/{spaceId}/gantt-charts/{chartId}/members` - `ganttService.updateMemberRole(chartId, userId, dto)` → `.../members/{userId}` - `ganttService.removeMember(chartId, userId)` → `.../members/{userId}` **Backend `GanttHandler.RegisterRoutes()` НЕ регистрирует маршруты для members диаграмм.** После удаления `gantt_chart_members` (миграция 5) эти endpoints не существуют. **Результат:** модальное окно «Участники диаграммы» на фронтенде **не работает** — все вызовы вернут 404. ### 3.4. 🔴 Проблема: Role fallback В `role_middleware.go:28-31`: ```go role := c.GetString(ContextSpaceRole) if role == "" { role = c.GetString(ContextRole) // fallback на глобальную роль User } ``` Если `ContextSpaceRole` не установлен, используется **глобальная роль пользователя** (из JWT). Это значит: - Пользователь с `role=admin` в таблице `users` получит admin-доступ к **любому** space, даже если он не член этого space - Но `SpaceMiddleware` проверяет членство раньше, так что в цепочке `RequireSpace() → RequireMinRole()` это не эксплуатируется - Однако если `RequireMinRole()` используется **без** `RequireSpace()` — это дыра **Рекомендация:** Удалить fallback на `ContextRole`. Если `ContextSpaceRole` пуст — отказывать. ### 3.5. Рекомендации по правам доступа 1. **Удалить модалку участников диаграммы** на фронтенде — она мертва 2. **Или** добавить раздел «Участники Space» с возможностью управления ролями через `/spaces/:space_id/members` (эти endpoints работают) 3. Удалить fallback на `ContextRole` в `role_middleware.go` 4. Добавить проверку: editor не может повысить кого-то до admin (сейчас может — `AddMember` разрешает editor+ назначать любую роль) --- ## 4. Безопасность ### 4.1. Критические | # | Проблема | Файл | Описание | |---|----------|------|----------| | S1 | JWT Secret = test | `.env` | `JWT_SECRET=test_secret_key_change_in_production` — **это прод-конфиг!** | | S2 | CORS: `*` | `.env` | `CORS_ALLOWED_ORIGINS=*` — любой домен может обращаться к API | | S3 | Пароль БД в .env | `.env` | `DB_PASSWORD=popoloka16` — creds в открытом виде | | S4 | Refresh tokens: SHA256 | `utils/password.go` | SHA256 вместо bcrypt для refresh token hash — быстро брутфорсится | ### 4.2. Высокие | # | Проблема | Описание | |---|----------|----------| | S5 | Нет rate limiting | Brute-force на `/auth/login` без ограничений | | S6 | Нет CSRF protection | API принимает POST/PUT/DELETE без CSRF-токена | | S7 | Нет CSP заголовков | X-Frame-Options, Content-Security-Policy не настроены | | S8 | JWT в localStorage | XSS может украсть access + refresh tokens | ### 4.3. Средние | # | Проблема | Описание | |---|----------|----------| | S9 | Нет password policy | Минимальные требования к паролю не enforced | | S10 | Нет audit log | Действия с spaces/charts не логируются для аудита | | S11 | Нет блокировки аккаунта | Неограниченные попытки входа | --- ## 5. Инфраструктура ### 5.1. Текущее состояние | Компонент | Статус | Примечание | |-----------|--------|------------| | Go API (pm2: leadera-api) | ⚠️ | Порт 8080, хост 0.0.0.0 | | Angular FE (pm2: leadera-fe) | ⚠️ | Порт 4200, хост 0.0.0.0 | | PostgreSQL (Docker) | ✅ | Порт 5432, контейнер app-leadera-db | | VPS reverse proxy | ⚠️ | sand.a2v.space → 8082/8083, но 502 если сервисы не запущены | | SSL | ✅ | Let's Encrypt на VPS | ### 5.2. Проблемы | # | Проблема | Описание | |---|----------|----------| | I1 | Docker для БД, нативно для API | Несогласованность — API и FE не в Docker | | I2 | Нет health-check в pm2 | API может упасть без перезапуска | | I3 | Нет CI/CD | Ручной деплой через `make run` + pm2 | | I4 | Нет nginx на Mac Studio | FE и API торчат наружу напрямую | --- ## 6. Функциональная оценка ### 6.1. Что работает | Функция | Backend | Frontend | Статус | |---------|---------|----------|--------| | Регистрация/логин | ✅ | ✅ | Работает | | JWT auth + refresh | ✅ | ✅ | Работает | | Spaces CRUD | ⚠️ | ✅ | `RegisterProtectedRoutes` не вызван (C4) | | Space members | ✅ | ✅ | Работает | | Gantt charts CRUD | ✅ | ✅ | Работает | | Gantt sections | ✅ | ✅ | Работает | | Gantt tasks | ✅ | ⚠️ | Нет UI для задач внутри диаграммы | | Архивация диаграмм | ✅ | ✅ | Работает | | Members диаграммы | ❌ | ✅ (мёртвый UI) | Backend endpoints не существуют | ### 6.2. Что НЕ работает или отсутствует | Функция | Статус | |---------|--------| | Визуализация Ганта (таймлайн) | ❌ Нет компонента отрисовки | | Drag & Drop задач | ❌ | | Real-time collaboration | ❌ | | Уведомления | ❌ | | Поиск | ❌ | | Экспорт (PDF/Excel) | ❌ | | Email verification | ❌ | | Password reset | Backend есть, UI есть — не проверено | ### 6.3. Оценка готовности **Система на стадии каркаса.** Backend API функционален для CRUD операций, но: - Визуализация Ганта — основная фича — не реализована (нет canvas/SVG рендеринга) - Управление участниками только на уровне Space - Нет CI/CD, тестов, мониторинга --- ## 7. Приоритизированный план исправлений ### Неделя 1 — Критическое (безопасность) | Приоритет | Задача | Трудоёмкость | |-----------|--------|--------------| | P0 | Заменить JWT secret на криптостойкий | 1 час | | P0 | Настроить CORS (только sand.a2v.space) | 1 час | | P0 | bcrypt для refresh tokens вместо SHA256 | 2 часа | | P0 | Удалить fallback на ContextRole в role_middleware | 30 мин | | P0 | Исправить `RegisterProtectedRoutes` — зарегистрировать маршруты | 1 час | | P1 | Rate limiting на auth endpoints | 4 часа | | P1 | Editor не может назначать admin роль | 2 часа | ### Неделя 2 — Архитектура прав | Приоритет | Задача | Трудоёмкость | |-----------|--------|--------------| | P1 | Удалить/переписать MembersModalComponent → Space members | 4 часа | | P1 | Добавить страницу управления участниками Space | 8 часов | | P2 | HttpOnly cookies для JWT (вместо localStorage) | 8 часов | | P2 | CSRF protection | 4 часа | ### Неделя 3 — Функциональность | Приоритет | Задача | Трудоёмкость | |-----------|--------|--------------| | P2 | Визуализация Ганта (canvas/d3) | 40+ часов | | P2 | Drag & Drop для задач | 16 часов | | P3 | Тесты (unit + integration) | 24 часа | | P3 | CI/CD pipeline | 8 часов | --- ## 8. Резюме ### Ключевые выводы 1. **Права доступа** — архитектурно верно: роль назначается через Space, не через диаграмму. Но фронтенд ещё не обновлён после удаления `gantt_chart_members`. 2. **Безопасность** — критические уязвимости в конфигурации (JWT secret, CORS, bcrypt). Требуют немедленного исправления до любого публичного деплоя. 3. **Функциональность** — CRUD работает, но основная ценность (визуализация Ганта) не реализована. 4. **Код** — чистый, хорошая архитектура, но есть мёртвый код и незарегистрированные маршруты. ### Общая оценка: 🔴 Pre-alpha Проект готов к внутренней демонстрации CRUD, но не готов к продакшену. Первоочередное — безопасность и исправление мёртвых endpoints.