Add audit logging, rate limiting, secret redactor, and backlinks

Implement several security and feature improvements across the backend
and frontend:
- New IP-based rate limiter for authentication endpoints
- New audit logging system for sensitive operations
- New secret redactor to mask sensitive patterns in rendered content
- Configurable token TTL and IGNORED_DIRS via environment variables
- Add backlink index and API endpoint
- Add preview tab support with single/double-click behavior in tree
- Add file backup before write/delete operations
This commit is contained in:
2026-05-26 10:27:00 -04:00
parent 5280dc7a50
commit 482937fb30
13 changed files with 1177 additions and 30 deletions
+60
View File
@@ -0,0 +1,60 @@
# ObsiGate — Plan d'implémentation Roadmap
> Généré le 2026-05-26 — Implémentation des items du ROADMAP.md
## Ordre d'implémentation
### Phase 1 — Backend Sécurité & Robustesse (P0–P1) ✅
| # | Item | Fichiers | Statut |
|---|------|----------|--------|
| 1 | Rate limiting login | `backend/ratelimit.py` (nouveau), `backend/auth/router.py` | ✅ |
| 2 | Secret redactor | `backend/secret_redactor.py` (nouveau), `backend/main.py` | ✅ |
| 3 | Log d'audit | `backend/audit.py` (nouveau), `backend/main.py` | ✅ |
| 4 | Backup avant écriture | `backend/main.py` (PUT/DELETE endpoints) | ✅ |
### Phase 2 — Configuration & Bug fixes (P1–P2) ✅
| # | Item | Fichiers | Statut |
|---|------|----------|--------|
| 5 | TOC scroll fix (slugify unifié) | `backend/main.py` (`unicodedata.category`) | ✅ |
| 6 | IGNORED_DIRS configurable | `backend/indexer.py`, `backend/watcher.py`, env var `OBSIGATE_IGNORED_DIRS` | ✅ |
| 7 | Timeout session configurable | `backend/auth/jwt_handler.py`, env vars `OBSIGATE_ACCESS_TOKEN_TTL` / `OBSIGATE_REFRESH_TOKEN_TTL` | ✅ |
### Phase 3 — UX & Fonctionnel (P2–P3) ✅
| # | Item | Fichiers | Statut |
|---|------|----------|--------|
| 8 | Clic simple/double clic arborescence | `frontend/app.js` (TabManager.openPreview/openPersistent), `frontend/style.css` | ✅ |
| 9 | Backlinks panel | `backend/indexer.py` (_backlink_index), `backend/main.py` (GET /backlinks), `frontend/app.js` (renderBacklinksPanel), `frontend/style.css` | ✅ |
| 10 | Gestion fichiers non-supportés | `backend/main.py` (FileContentResponse + unsupported), `frontend/app.js` (renderFile) | ✅ |
## Fichiers créés
- `backend/ratelimit.py` — IP-based rate limiter
- `backend/secret_redactor.py` — Regex-based secret masking
- `backend/audit.py` — JSON-lines audit logging
## Fichiers modifiés
- `backend/main.py` — +audit, +backup, +redaction, +backlinks endpoint, +unsupported files, +slugify fix
- `backend/auth/router.py` — +IP rate limiting on login
- `backend/auth/jwt_handler.py` — +configurable TTL via env vars
- `backend/indexer.py` — +IGNORED_DIRS, +backlink index
- `backend/watcher.py` — +configurable IGNORED_DIRS
- `frontend/app.js` — +TabManager preview/persistent, +backlinks panel, +unsupported file UI, +tree dblclick
- `frontend/style.css` — +preview tab style, +backlinks panel style, +unsupported file style
## Nouvelles variables d'environnement
| Variable | Défaut | Description |
|----------|--------|-------------|
| `OBSIGATE_LOGIN_MAX_ATTEMPTS` | 10 | Échecs max par IP avant blocage |
| `OBSIGATE_LOGIN_WINDOW_SECONDS` | 900 | Fenêtre de blocage IP (secondes) |
| `OBSIGATE_IGNORED_DIRS` | `.obsidian,.trash,.git,__pycache__,node_modules,.obsigate-backup` | Dossiers ignorés |
| `OBSIGATE_ACCESS_TOKEN_TTL` | 3600 | Durée access token (secondes) |
| `OBSIGATE_REFRESH_TOKEN_TTL` | 604800 | Durée refresh token (secondes) |
| `OBSIGATE_BACKUP_DIR` | `.obsigate-backup` | Répertoire de backups |
| `OBSIGATE_AUDIT_MAX_SIZE` | 10485760 | Taille max du fichier d'audit avant rotation |
## Reste à faire (non implémenté dans cette session)
- 🟢 Publication publique de documents
- 🟢 Dashboard statistiques
- 🟢 Webhooks
- 🟢 Documentation OpenAPI enrichie
- 🟡 Gestion des conflits Syncthing
- ⬜ Tests, CI/CD, i18n, CHANGELOG, MFA
+11 -9
View File
@@ -27,7 +27,7 @@
## Corrections (historique TODO.md)
- ✅ **Login popout avec redirection** — Quand un fichier est ouvert via l'URL popout et que l'authentification est requise, un formulaire de login est proposé. Après connexion réussie, la page recharge automatiquement.
- 🟡 **Correction TOC — scroll avec caractères accentués** — Le clic sur un titre dans la table des matières ne fonctionne pas toujours pour les titres contenant des lettres accentuées. Les fonctions slugify (frontend) et _heading_slugify (backend) utilisent des normalisations Unicode qui peuvent diverger pour certains caractères (ex: œ, æ). **À corriger :** unifier l'algorithme + ajouter un fallback `scrollIntoView`.
- ✅ **Correction TOC — scroll avec caractères accentués** — Les fonctions slugify (frontend et backend) utilisent maintenant `unicodedata.category()` pour une classification Unicode-aware identique (catégories L* et N* au lieu de `.isalpha()` ASCII-only).
---
@@ -35,27 +35,29 @@
### 🔴 Sécurité — P0
- 🔴 **Masquage automatique des secrets** — Implémenter un `SecretRedactor` pour masquer les clés API, tokens, et mots de passe dans les aperçus de contenu (preview RÉCENT, dashboard, vue rendu).
- 🔴 **Rate limiting sur le login** — 5 tentatives max par IP sur 15 minutes, blocage après 10 échecs.
- ✅ **Rate limiting sur le login** — IP-based rate limiting (10 tentatives/15 min par IP) + lockout par compte (5 tentatives). Implémenté dans `backend/ratelimit.py` + `backend/auth/router.py`.
- ✅ **Masquage automatique des secrets** — `SecretRedactor` dans `backend/secret_redactor.py` masque JWT, clés API, tokens GitHub, clés privées et connection strings dans les aperçus de contenu.
### 🟠 Robustesse — P1
- 🟠 **Log d'audit** — Tracer les écritures, suppressions, et changements de config dans `data/audit.log`.
- 🟠 **Backup automatique avant écriture** — Sauvegarder le contenu original dans `.obsigate-backup/` avant chaque PUT ou DELETE.
- ✅ **Log d'audit** — `backend/audit.py` trace les écritures, suppressions et changements de config dans `data/audit.log` (format JSON lines).
- ✅ **Backup automatique avant écriture** — Sauvegarde du contenu original dans `.obsigate-backup/` avant chaque PUT ou DELETE, avec timestamp.
### 🟡 UX — P2
- 🟡 **Backlinks panel** — Afficher les fichiers contenant des wikilinks pointant vers le fichier courant. Index inversé des wikilinks.
- ✅ **Clic simple / double clic dans l'arborescence** — Simple clic = onglet preview (italique, temporaire). Double clic = onglet persistant (normal, cumulable). Implémenté dans `TabManager.openPreview()` / `TabManager.openPersistent()`.
- ✅ **Backlinks panel** — Panneau affichant les fichiers avec wikilinks pointant vers le fichier courant. Backend : `GET /api/file/{vault}/backlinks` + index inversé dans `indexer.py`. Frontend : panneau `renderBacklinksPanel()`.
- 🟡 **Gestion des conflits Syncthing** — Dashboard « Conflits » avec diff et résolution (garder local, garder conflit).
- 🟡 **Liste IGNORED_DIRS configurable** — Rendre la liste des dossiers ignorés par le watcher configurable (env var ou Configurations).
- 🟡 **Timeout de session configurable** — Exposer le TTL JWT dans les Configurations.
- ✅ **Liste IGNORED_DIRS configurable** — Configurable via `OBSIGATE_IGNORED_DIRS` (liste séparée par virgules). Appliqué au watcher et à l'indexer.
- ✅ **Timeout de session configurable** — JWT TTL configurable via `OBSIGATE_ACCESS_TOKEN_TTL` et `OBSIGATE_REFRESH_TOKEN_TTL`.
### 🟢 Fonctionnel — P3/P4
- 🟢 **Publication publique de documents** — Générer un lien partageable pour un document, accessible à des utilisateurs non authentifiés (token unique, expiration configurable, lecture seule). L'utilisateur peut créer/révoquer des liens de partage depuis l'interface.
- 🟢 **Dashboard statistiques** — Métriques par vault : fichiers totaux, taille, top tags, orphelins.
- 🟢 **Webhooks** — Notifier des systèmes externes lors de changements (création, modif, suppression).
- 🟢 **Documentation OpenAPI enrichie** — Enrichir les modèles Pydantic pour la doc auto-générée /docs et /redoc.
- 🟢 **Gestion fichiers non-supportés** — Message explicite + bouton download pour les fichiers binaires non gérés.
- ✅ **Gestion fichiers non-supportés** — Message explicite avec nom du fichier, taille et bouton de téléchargement pour les fichiers binaires. Backend : réponse structurée avec `unsupported: true`. Frontend : interface `unsupported-file`.
### ⬜ Qualité & Polish — P5+