15 KiB
title, date, lastmod, tags, weight
| title | date | lastmod | tags | weight | ||||
|---|---|---|---|---|---|---|---|---|
| Аудит Leadera — полный отчёт | 2026-03-30 | 2026-03-30 |
|
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:
- Таблица
space_members— связка user ↔ space с role (admin/editor/viewer) SpaceMiddleware.RequireSpace()— проверяет членство и записываетContextSpaceRoleв contextRoleMiddleware.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}/membersganttService.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. Рекомендации по правам доступа
- Удалить модалку участников диаграммы на фронтенде — она мертва
- Или добавить раздел «Участники Space» с возможностью управления ролями через
/spaces/:space_id/members(эти endpoints работают) - Удалить fallback на
ContextRoleвrole_middleware.go - Добавить проверку: 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. Резюме
Ключевые выводы
- Права доступа — архитектурно верно: роль назначается через Space, не через диаграмму. Но фронтенд ещё не обновлён после удаления
gantt_chart_members. - Безопасность — критические уязвимости в конфигурации (JWT secret, CORS, bcrypt). Требуют немедленного исправления до любого публичного деплоя.
- Функциональность — CRUD работает, но основная ценность (визуализация Ганта) не реализована.
- Код — чистый, хорошая архитектура, но есть мёртвый код и незарегистрированные маршруты.
Общая оценка: 🔴 Pre-alpha
Проект готов к внутренней демонстрации CRUD, но не готов к продакшену. Первоочередное — безопасность и исправление мёртвых endpoints.