Files
docs.a2v.space/content/yasha/leadera/leadera-audit.md
T
Агальцов Антон dd67d2c2ec Реструктуризация: два глобальных раздела — Яша и Семен
Весь контент перенесён в content/yasha/ (7 подразделов).
Создан content/semen/ с подразделами: changelog, kb, diary.
Навигация — dropdown в навбаре, двухуровневое меню в hugo.toml.
Шаблоны, сайдбар, поиск обновлены под вложенные секции.
2026-04-26 19:44:52 +03:00

15 KiB
Raw Blame History

title, date, lastmod, tags, weight
title date lastmod tags weight
Аудит Leadera — полный отчёт 2026-03-30 2026-03-30
audit
leadera
security
architecture
100

Аудит проекта Leadera

Дата: 30.03.2026
Версия: pre-production, миграции 15


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.