feat: Introduce optional JWT and Argon2id authentication with user management and access control, updating documentation and backend dependencies.

This commit is contained in:
Bruno Charest 2026-03-23 15:56:35 -04:00
parent 190f47f134
commit b6ffb45ad7
2 changed files with 170 additions and 35 deletions

204
README.md
View File

@ -2,7 +2,7 @@
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
[![Version](https://img.shields.io/badge/Version-1.2.0-blue.svg)]()
[![Version](https://img.shields.io/badge/Version-1.4.0-blue.svg)]()
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Docker](https://img.shields.io/badge/Docker-Ready-blue.svg)](https://www.docker.com/)
[![Python](https://img.shields.io/badge/Python-3.11+-green.svg)](https://www.python.org/)
@ -31,6 +31,7 @@
- [Installation rapide](#-installation-rapide)
- [Configuration détaillée](#-configuration-détaillée)
- [Variables d'environnement](#-variables-denvironnement)
- [🔒 Authentification](#-authentification)
- [Ajouter une nouvelle vault](#-ajouter-une-nouvelle-vault)
- [Build multi-platform](#-build-multi-platform)
- [Utilisation](#-utilisation)
@ -55,11 +56,12 @@
- **🖼️ Images Obsidian** : Support complet des syntaxes d'images Obsidian avec résolution intelligente
- **🎨 Syntax highlight** : Coloration syntaxique des blocs de code
- **🌓 Thème clair/sombre** : Toggle persisté en localStorage
- **<EFBFBD> Synchronisation temps réel** : Surveillance automatique des fichiers via watchdog avec mise à jour incrémentale de l'index
- **📡 Synchronisation temps réel** : Surveillance automatique des fichiers via watchdog avec mise à jour incrémentale de l'index
- **📡 Server-Sent Events** : Notifications SSE pour les changements d'index avec reconnexion automatique
- ** Gestion dynamique des vaults** : Ajout/suppression de vaults via API sans redémarrage
- **<EFBFBD>🐳 Docker multi-platform** : linux/amd64, linux/arm64, linux/arm/v7, linux/386
- **🔒 Sécurité** : Protection contre le path traversal, utilisateur non-root dans Docker
- **🐳 Docker multi-platform** : linux/amd64, linux/arm64, linux/arm/v7, linux/386
- **🔒 Authentification** : JWT + Argon2id, sessions persistantes, contrôle d'accès par vault
- **🛡️ Sécurité** : Headers de sécurité (CSP, X-Frame-Options…), protection path traversal, utilisateur non-root
- **❤️ Healthcheck** : Endpoint `/api/health` intégré pour Docker et monitoring
---
@ -192,6 +194,96 @@ Les vaults sont configurées par paires de variables `VAULT_N_NAME` / `VAULT_N_P
---
## 🔒 Authentification
> **Désactivée par défaut** — Compatible avec toutes les installations existantes.
ObsiGate supporte un système d'authentification optionnel basé sur **JWT + Argon2id** avec contrôle d'accès par vault.
### Activer l'authentification
Dans `docker-compose.yml`, décommentez les variables d'auth :
```yaml
environment:
# ... vaults ...
- OBSIGATE_AUTH_ENABLED=true
# - OBSIGATE_ADMIN_USER=admin # Défaut: admin
# - OBSIGATE_ADMIN_PASSWORD= # Vide = mot de passe auto-généré (voir logs)
# - OBSIGATE_SECURE_COOKIES=false # true si derrière un reverse-proxy HTTPS
```
### Premier démarrage
Si aucun utilisateur n'existe, ObsiGate crée automatiquement un compte admin et **affiche le mot de passe dans les logs** :
```bash
docker-compose logs obsigate | grep -A4 "PREMIER"
```
```
============================================================
PREMIER DÉMARRAGE — Compte admin créé automatiquement
Utilisateur : admin
Mot de passe : xK9mQ3pLr7wN2jT5
CHANGEZ CE MOT DE PASSE dès la première connexion !
============================================================
```
### Gestion des utilisateurs via CLI
```bash
# Créer un utilisateur
docker exec obsigate python backend/create_admin.py create alice MonMotDePasse --role user --vaults Recettes IT
# Créer un admin avec accès total
docker exec obsigate python backend/create_admin.py create bob SecretPass --role admin --vaults "*"
# Lister les utilisateurs
docker exec obsigate python backend/create_admin.py list
# Supprimer un utilisateur
docker exec obsigate python backend/create_admin.py delete alice
```
### Interface d'administration
Lorsqu'un compte **admin** est connecté, une icône 🛡️ apparaît dans le header. Cliquer dessus ouvre le panneau d'administration permettant de :
- Lister tous les utilisateurs
- Créer / modifier / supprimer des utilisateurs
- Assigner les vaults accessibles par utilisateur
- Activer/désactiver des comptes
### Contrôle d'accès par vault
| Valeur vaults | Accès |
|---------------|-------|
| `["*"]` | Toutes les vaults (y compris futures) — défaut admin |
| `["Recettes", "IT"]` | Uniquement ces vaults |
| `[]` | Aucun accès |
### Variables d'environnement d'auth
| Variable | Description | Défaut |
|----------|-------------|--------|
| `OBSIGATE_AUTH_ENABLED` | Activer l'authentification | `false` |
| `OBSIGATE_ADMIN_USER` | Nom de l'admin auto-créé | `admin` |
| `OBSIGATE_ADMIN_PASSWORD` | Mot de passe admin (vide = auto-généré) | *(auto)* |
| `OBSIGATE_SECURE_COOKIES` | Cookie `Secure` (HTTPS uniquement) | `false` |
### Volume pour la persistance
Les données d'auth (`users.json`, `secret.key`) sont stockées dans `/app/data`. Ajoutez un volume pour les persister :
```yaml
volumes:
# vaults...
- ./data:/app/data # Persistance des utilisateurs et clé JWT
```
---
## Ajouter une nouvelle vault
### Méthode 1 : Édition directe
@ -357,32 +449,35 @@ curl -X POST http://localhost:2020/api/attachments/rescan/MonVault
ObsiGate expose une API REST complète :
| Endpoint | Description | Méthode |
|----------|-------------|---------|
| `/api/health` | Health check (status, version, stats) | GET |
| `/api/vaults` | Liste des vaults configurées | GET |
| `/api/browse/{vault}?path=` | Navigation dans les dossiers | GET |
| `/api/file/{vault}?path=` | Contenu rendu d'un fichier | GET |
| `/api/file/{vault}/raw?path=` | Contenu brut d'un fichier | GET |
| `/api/file/{vault}/download?path=` | Téléchargement d'un fichier | GET |
| `/api/file/{vault}/save?path=` | Sauvegarder un fichier | PUT |
| `/api/file/{vault}?path=` | Supprimer un fichier | DELETE |
| `/api/search?q=&vault=&tag=` | Recherche fulltext (legacy) | GET |
| `/api/search/advanced?q=&vault=&tag=&limit=&offset=&sort=` | Recherche avancée TF-IDF avec facettes et pagination | GET |
| `/api/suggest?q=&vault=&limit=` | Suggestions de titres de fichiers (autocomplétion) | GET |
| `/api/tags/suggest?q=&vault=&limit=` | Suggestions de tags (autocomplétion) | GET |
| `/api/tags?vault=` | Tags uniques avec compteurs | GET |
| `/api/index/reload` | Force un re-scan des vaults | GET |
| `/api/events` | Flux SSE temps réel (index updates, vault changes) | GET |
| `/api/vaults/add` | Ajouter une vault dynamiquement | POST |
| `/api/vaults/{name}` | Supprimer une vault | DELETE |
| `/api/vaults/status` | Statut détaillé des vaults (fichiers, watching) | GET |
| `/api/image/{vault}?path=` | Servir une image avec MIME type approprié | GET |
| `/api/attachments/rescan/{vault}` | Rescanner les images d'un vault | POST |
| `/api/attachments/stats?vault=` | Statistiques d'images indexées | GET |
| `/api/config` | Lire la configuration | GET |
| `/api/config` | Mettre à jour la configuration | POST |
| `/api/diagnostics` | Statistiques index, mémoire, moteur de recherche | GET |
| Endpoint | Description | Méthode | Auth |
|----------|-------------|---------|------|
| `/api/health` | Health check (status, version, stats) | GET | Non |
| `/api/auth/status` | Statut auth (activé, utilisateurs présents) | GET | Non |
| `/api/auth/login` | Connexion (retourne access token + cookie refresh) | POST | Non |
| `/api/auth/refresh` | Renouveler l'access token via cookie refresh | POST | Cookie |
| `/api/auth/logout` | Déconnexion + révocation refresh token | POST | Oui |
| `/api/auth/me` | Infos utilisateur courant | GET | Oui |
| `/api/auth/change-password` | Changer son mot de passe | POST | Oui |
| `/api/auth/admin/users` | Lister / créer des utilisateurs | GET/POST | Admin |
| `/api/auth/admin/users/{u}` | Modifier / supprimer un utilisateur | PATCH/DELETE | Admin |
| `/api/vaults` | Liste des vaults (filtrée par permissions) | GET | Oui |
| `/api/browse/{vault}?path=` | Navigation dans les dossiers | GET | Oui |
| `/api/file/{vault}?path=` | Contenu rendu d'un fichier | GET | Oui |
| `/api/file/{vault}/raw?path=` | Contenu brut d'un fichier | GET | Oui |
| `/api/file/{vault}/download?path=` | Téléchargement d'un fichier | GET | Oui |
| `/api/file/{vault}/save?path=` | Sauvegarder un fichier | PUT | Oui |
| `/api/file/{vault}?path=` | Supprimer un fichier | DELETE | Oui |
| `/api/search/advanced` | Recherche avancée TF-IDF | GET | Oui |
| `/api/suggest` / `/api/tags/suggest` | Autocomplétion | GET | Oui |
| `/api/tags?vault=` | Tags uniques avec compteurs | GET | Oui |
| `/api/index/reload` | Force un re-scan des vaults | GET | Admin |
| `/api/events` | Flux SSE temps réel | GET | Oui |
| `/api/vaults/add` / `/api/vaults/{name}` | Gestion dynamique des vaults | POST/DELETE | Admin |
| `/api/image/{vault}?path=` | Servir une image | GET | Oui |
| `/api/config` | Lire / écrire la configuration | GET/POST | Oui/Admin |
| `/api/diagnostics` | Statistiques index et mémoire | GET | Admin |
> Quand `OBSIGATE_AUTH_ENABLED=false`, tous les endpoints sont accessibles sans token.
> Tous les endpoints exposent des schémas Pydantic documentés. La doc interactive est disponible sur `/docs` (Swagger UI).
@ -541,11 +636,12 @@ Ces paramètres sont configurables via l'interface (Settings) ou l'API `/api/con
## 🏗️ Stack technique
- **Backend** : Python 3.11 + FastAPI 0.110 + Uvicorn
- **Auth** : python-jose (JWT HS256) + argon2-cffi (Argon2id)
- **File Watcher** : watchdog 4.x (inotify natif + fallback polling)
- **Frontend** : Vanilla JS + HTML + CSS (zéro framework, zéro build)
- **Rendu Markdown** : mistune 3.x
- **Image Docker** : python:3.11-slim (multi-stage)
- **Base de données** : Aucune (index en mémoire uniquement)
- **Stockage utilisateurs** : JSON local (`data/users.json`) — aucune base de données
- **Architecture** : SPA + API REST + SSE
---
@ -598,13 +694,23 @@ ObsiGate/
│ ├── indexer.py # Scan des vaults, index en mémoire, lookup table
│ ├── search.py # Moteur de recherche fulltext avec scoring
│ ├── watcher.py # Surveillance fichiers (watchdog + debounce)
│ ├── auth/ # Module d'authentification
│ │ ├── password.py # Argon2id hashing
│ │ ├── jwt_handler.py # Tokens JWT + révocation
│ │ ├── user_store.py # CRUD users.json (atomique)
│ │ ├── middleware.py # Dépendances FastAPI auth
│ │ └── router.py # Endpoints /api/auth/*
│ ├── create_admin.py # CLI gestion utilisateurs
│ └── requirements.txt
├── frontend/ # Interface web (Vanilla JS, zéro framework)
│ ├── index.html # Page SPA + modales (aide, config, éditeur)
│ ├── app.js # Logique SPA, gestion d'état, API client
│ ├── index.html # Page SPA + écran de login
│ ├── app.js # Logique SPA, AuthManager, AdminPanel
│ └── style.css # Styles (CSS variables, thèmes, responsive)
├── data/ # Données persistantes (créé au démarrage)
│ ├── users.json # Utilisateurs (hashés Argon2id)
│ └── secret.key # Clé secrète JWT (512 bits)
├── Dockerfile # Multi-stage, healthcheck, non-root
├── docker-compose.yml # Déploiement avec healthcheck
├── docker-compose.yml # Déploiement avec healthcheck et auth env vars
├── build.sh # Build multi-platform (amd64/arm64/arm/v7/i386)
└── CONTRIBUTING.md # Guide de contribution
```
@ -631,6 +737,34 @@ Ce projet est sous licence **MIT** - voir le fichier [LICENSE](LICENSE) pour les
## 📝 Changelog
### v1.4.0 (2026)
**Authentification & Contrôle d'accès**
- Système d'authentification JWT (HS256) optionnel — désactivé par défaut, activation via `OBSIGATE_AUTH_ENABLED=true`
- Hachage des mots de passe **Argon2id** (paramètres OWASP 2024 : time_cost=2, mem=64MB)
- Sessions avec **access token (1h)** + **refresh token (7j, HttpOnly cookie)** et révocation
- Contrôle d'accès **par vault** : chaque utilisateur accède uniquement à ses vaults assignées
- Bootstrap automatique du compte admin au premier démarrage (mot de passe généré et affiché dans les logs)
- CLI de gestion des utilisateurs : `python backend/create_admin.py create|list|delete`
- Interface d'administration dans la SPA : création/modification/suppression d'utilisateurs avec sélection de vaults
- Protection brute-force : verrouillage 15 min après 5 tentatives échouées
- Réponses en temps constant sur le login pour éviter l'énumération d'utilisateurs
- Token de rafraîchissement révocable (persisté dans `data/revoked_tokens.json`)
- Clé secrète JWT auto-générée (512 bits) et persistée dans `data/secret.key`
**Sécurité (headers)**
- `Content-Security-Policy` sur toutes les réponses
- `X-Frame-Options: SAMEORIGIN`
- `X-Content-Type-Options: nosniff`
- `X-XSS-Protection: 1; mode=block`
- `Referrer-Policy: strict-origin-when-cross-origin`
**Frontend**
- Écran de connexion avec glassmorphism (avant chargement de l'app si auth activée)
- Menu utilisateur dans le header (nom affiché, bouton admin, déconnexion)
- Rafraîchissement silencieux de l'access token (via cookie refresh)
- Compatibilité totale : `OBSIGATE_AUTH_ENABLED=false` → comportement identique à v1.3.x
### v1.3.0 (2025)
**Synchronisation temps réel**
@ -717,4 +851,4 @@ Ce projet est sous licence **MIT** - voir le fichier [LICENSE](LICENSE) pour les
---
*Projet : ObsiGate | Version : 1.2.0 | Dernière mise à jour : 2025*
*Projet : ObsiGate | Version : 1.4.0 | Dernière mise à jour : 2026*

View File

@ -7,3 +7,4 @@ aiofiles==23.2.1
watchdog>=4.0.0
argon2-cffi>=23.1.0
python-jose[cryptography]>=3.3.0
cryptography>=3.4.0,<42.0.0