Нужен внешний (без входа в ACORE) read-only доступ к документам аптеки —
для сторон вроде поставщиков/проверяющих. Для сотрудников внутренний доступ
уже был (access.can_access_pharmacy_docs через виджет портала), это отдельный,
внешний путь.
- NextcloudSharingService — клиент OCS Files Sharing API (публичные ссылки,
идемпотентно: сначала проверяет существующую шару на путь, не плодит дубли).
- webdav_service.ensure_dir() — идемпотентный MKCOL (в отличие от mkdir(),
который должен падать на дубликате — так и оставлен для UI).
- PharmacyPublicLink — аптека ↔ URL + share_id (для последующего отзыва/
обновления).
- sync_service.sync_pharmacy(): больше не использует Group Folders (аптечные
сотрудники учёток Nextcloud не имеют — ACL по AD-группам тут не нужен,
это была подготовка под LDAP, которая пока не горит). Теперь — обычная
WebDAV-папка под сервисным аккаунтом + публичная RO-ссылка. Попутно чинит
сегодняшний блокер: --pharmacies-only больше не зависит от сломанного (404)
Group Folders API — клиенты в NextcloudSyncService стали ленивыми.
- Отделы (sync_department) не тронуты — Group Folders/LDAP остаётся
архитектурным заделом на будущее, не нужно прямо сейчас.
Тесты: SharingServiceTests, SyncPharmacyPublicLinkTests (идемпотентность,
обновление URL, обработка ошибок WebDAV, dry-run). 326 тестов в затронутых
приложениях — зелёные.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MattermostBot не имеет поля .role (есть галки can_* и метод caps()).
При успешном коннекте (users/me → 200) команда падала с AttributeError.
Теперь показывает включённые возможности бота. Тест на OK-ветку добавлен.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Пример окружения больше не раскрывает топологию сети:
- ALLOWED_HOSTS: убран 10.10.31.3 (оставлены localhost/127.0.0.1)
- DATABASE_URL: 10.10.31.2 → 127.0.0.1 (+ комментарий про <db-host>)
- комментарий Traefik: 10.10.31.3 → <portal-lan-ip>
Секретов не было (SECRET_KEY/пароли = CHANGE_ME/пусто) — только IP.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ContentSecurityPolicyMiddleware ставит заголовок CSP при CSP_ENABLED=True.
По умолчанию выключен; CSP_REPORT_ONLY=True — только отчёты, ничего не
блокирует (для сбора нарушений перед боевым режимом).
Политика (дефолт под текущий стек): default-src 'self'; object-src 'none';
base-uri 'self'; frame-ancestors 'none'; form-action 'self';
script/style-src с 'unsafe-inline' (пока есть инлайн onclick/<script> —
Фаза 2: вынести и перейти на nonce) + CDN (jsdelivr/unpkg/cdnjs) + тайлы карт.
CSP_EXTRA_HOSTS — внешние хосты (напр. домен OnlyOffice Document Server).
htmx CSP-чистый (allowEval=false, нет hx-on) — 'unsafe-eval' не нужен.
Тесты: выкл по умолчанию, Report-Only/enforce заголовки, extra-hosts. 795 ок.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Аудит по OWASP: найдены и исправлены два Stored XSS класса «json.dumps|safe
в <script>» — вырыв из тега через </script> в пользовательских данных.
- OnlyOffice-редактор (documents-editor): имя файла (title) выводилось как
var cfg = {{ editor_config|safe }} → перешли на |json_script
- РРО (rro.html): поля реестра (notes/model_name/...) в <script
application/json> → |json_script; из view убран ручной json.dumps
- safe_filename: отсекает < > : " | ? * и управляющие символы (defense-in-depth)
- SECURE_CONTENT_TYPE_NOSNIFF = True
Тесты: XSS-пэйлоад </script> в имени файла и в поле РРО экранируется;
safe_filename отклоняет HTML-опасные символы. Прогон: 791 тест, 0 провалов.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- EmployeeAdminPasswordTests: форма требует internal_line_type (поле
телефонии, у модели default=ANALOG) — добавлено в данные формы
- EmployeeDetailTests: в чужой карточке телефон отображается
форматированным (format_ua_phone_display) — проверка обновлена
Полный прогон (.venv): 788 тестов, 0 провалов.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>