security(users): SafeUser — passwordHash/токены не уходят клиенту

Шаг 0.8 плана production-готовности:
- shared/schema.ts: тип SafeUser + safeUserColumns
- getUsersByOrganization, listUsers, searchUsers, getUsersByRole,
  getUsersByOrgRoleId, documents getUser — без чувствительных колонок
- sync (initial/delta), автоматизации ctx.users.list, MCP list_users — sanitized
This commit is contained in:
2026-09-07 21:03:00 +03:00
parent 26419f395f
commit cf6f937108
12 changed files with 79 additions and 51 deletions

View File

@@ -60,3 +60,19 @@
- Как проверялось: повторный grep `status(5xx)` + `err.message` по `server/` — 0 совпадений; `npm run check` чисто; `npx vitest run` 45/45.
- Влияние на поиск/UX: нет (только тексты 5xx-ответов).
- Подводные камни: остались ответы `200 + success:false` с `err?.message` (llm-providers, rag, finance-di2) и 4xx с `err.message` (documents, data-tables) — сознательно вне скоупа шага, кандидаты для фазы 2.
---
## [0.8] Убрать passwordHash из выдачи пользователей
- Статус: ✅ done
- Зачем: хэши паролей и токены не должны уходить клиенту (в т.ч. через offline-sync).
- Что изменено:
- `shared/schema.ts` — тип `SafeUser` (User без passwordHash/verificationToken/resetPasswordToken/resetPasswordExpires) + набор колонок `safeUserColumns` (единая точка правды).
- `server/storage/users.storage.ts` — `getUsersByOrganization` и `listUsers` → `SafeUser[]`.
- `server/storage/social.storage.ts` (`searchUsers`), `server/storage/task-meta.storage.ts` (`getUsersByRole`, `getUsersByOrgRoleId`), `server/documents/data-resolution.service.ts` (getUser для шаблонов) — тоже на `safeUserColumns`.
- Сигнатуры в `server/storage.ts`; типовые правки у caller'ов (логика не менялась). Методы аутентификации (где passwordHash нужен) не тронуты.
- Побочный эффект: `ctx.users.list()` в автоматизациях и MCP `list_users` тоже sanitized.
- Как проверялось: ни один из 43 caller'ов не использовал исключённые поля (tsc); sync (initial + delta) теперь отдаёт пользователей без хэшей; `npm run check` чисто; `npx vitest run` 45/45; grep `passwordHash` по server/ — только auth-флоу.
- Влияние на поиск/UX: нет.
- Подводные камни: при явном списке колонок drizzle возвращает плоские строки даже с join — мёртвый маппинг в listUsers убран, на это опираться нельзя в будущих правках.