Аудит Leadera — полный отчёт

📁 ~/Projects/docs-a2v/content/yasha/leadera/leadera-audit.md

Аудит проекта 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:

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.