DOC-001 demandait quatre documents courts. Deux existaient — installation (deployment.md) et restauration (database-restore.md). Les deux autres n existaient pas. docs/roles-and-permissions.md decrit le comportement **implemente**, pas celui qu on souhaiterait, et note les endroits ou les deux divergent : un gerant gere toutes les equipes mais pas toutes les selections, asymetrie presente depuis toujours et ecrite nulle part ailleurs. Le piege du double rattachement coach/equipe y est en toutes lettres, avec le motif a ne jamais reintroduire. Le document dit aussi qui a raison en cas de desaccord : les tests. Une documentation d autorisation qui se contredit avec le code est pire que pas de documentation, parce qu on la croit. docs/incident-runbook.md part du symptome, pas du composant. Le reflexe d ouverture est la reference affichee sur la page d erreur — c est ce que l identifiant de requete rend possible, et sans un endroit qui le dise, la fonctionnalite ne sert a personne. Trois affirmations ont ete verifiees contre le code avant d etre ecrites, et deux etaient fausses : le corps de /health en echec dit database="error" et non "disconnected", et les evenements de verrouillage s appellent login.failure et account.throttled. La liste complete des quatorze evenements de auth.log est maintenant dans le manuel. README : index des documents, et deux points d etat corriges — TRUSTED_PROXY est desormais une variable, et l absence de validation des inscriptions par le staff est nommee comme decision en attente.
151 lines
6.2 KiB
Markdown
151 lines
6.2 KiB
Markdown
# Plateforme centralisée de tryouts
|
|
|
|
Application interne du club e-sport de l'UdeS : inscriptions aux sélections,
|
|
évaluations, gestion des équipes, disponibilités, contrats, et notifications
|
|
Discord.
|
|
|
|
Le site est servi **en français**, l'anglais reste accessible par le sélecteur
|
|
de la barre latérale (voir `docs/translations.md`).
|
|
|
|
---
|
|
|
|
## Démarrer
|
|
|
|
```bash
|
|
python -m venv .venv
|
|
.venv/Scripts/pip install -r requirements.txt -r requirements-dev.txt
|
|
cp app/.env.example .env # puis remplir SECRET_KEY et DATABASE_URL
|
|
.venv/Scripts/python run.py # développement, http://127.0.0.1:5000
|
|
```
|
|
|
|
`SECRET_KEY` et `DATABASE_URL` sont **obligatoires** : `create_app()` refuse
|
|
de démarrer sans eux. `DATABASE_URL` doit pointer sur PostgreSQL ; le pilote
|
|
psycopg 3 est nommé automatiquement si l'URL n'en nomme pas.
|
|
|
|
Production : `python wsgi.py` (Waitress derrière nginx). Voir
|
|
`docs/deployment.md`.
|
|
|
|
Ce sont les **deux seuls** points d'entrée.
|
|
|
|
## Vérifier
|
|
|
|
```bash
|
|
.venv/Scripts/python -m pytest # suite complète
|
|
.venv/Scripts/python -m ruff check . # lint
|
|
.venv/Scripts/python -m ruff format --check .
|
|
```
|
|
|
|
Les trois tournent en CI et y sont bloquants.
|
|
|
|
---
|
|
|
|
## Ce que fait l'application
|
|
|
|
- **Comptes et rôles** — cinq rôles : président (`admin`), gérant
|
|
(`manager`), coach, joueur (`player`), recruteur (`scout`). Le président
|
|
attribue les rôles.
|
|
- **Sélections** — organisation des tryouts, trois formats de match
|
|
(équipe contre équipe, joueur contre joueur, scrim), évaluation des
|
|
joueurs sur dix critères.
|
|
- **Équipes** — effectifs de la saison, matchs et entraînements. Le
|
|
formulaire d'entraînement affiche les disponibilités des joueurs.
|
|
- **Disponibilités** — créneaux hebdomadaires des joueurs, créneaux
|
|
réservables des coachs.
|
|
- **Notes** — un coach écrit des notes d'équipe (visibles par l'équipe) et
|
|
des notes nominatives (visibles par le joueur concerné).
|
|
- **Un-à-un** — un joueur demande une séance à son coach ; le coach répond
|
|
depuis le site ou par une réaction sur le message privé Discord.
|
|
- **Contrats** — dépôt d'un contrat par le staff, signature par le joueur.
|
|
|
|
## Comment c'est construit
|
|
|
|
Backend Python 3.12 / Flask, rendu serveur en Jinja2, CSS et JavaScript
|
|
maison, sans framework front. Base PostgreSQL via SQLAlchemy. Bot Discord
|
|
(`discord.py`) dans un fil du même processus que le serveur web.
|
|
|
|
`docs/architecture.md` contient les diagrammes (classes, paquets, flux
|
|
d'une requête).
|
|
|
|
---
|
|
|
|
## Sécurité
|
|
|
|
En place et vérifié par des tests :
|
|
|
|
- **Limitation de débit** sur la connexion (10 requêtes/minute par IP).
|
|
- **Cookies de session** `HttpOnly`, `SameSite=Lax`, `Secure`, avec
|
|
expiration effective.
|
|
- **CSRF** sur tous les formulaires, y compris la déconnexion (en POST).
|
|
- **HTTPS** forcé en production, **HSTS**.
|
|
- **CSP sans `unsafe-inline`** sur `script-src` : aucun gestionnaire
|
|
d'événement en ligne, chaque bloc `<script>` porte un nonce par requête.
|
|
- **Redirections** validées (rien ne sort du site).
|
|
- **Validation** par schémas marshmallow sur les formulaires de compte, avec
|
|
politique de mot de passe.
|
|
- **Autorisation** centralisée dans `app/permissions.py`.
|
|
- **Journal d'authentification** (`logs/auth.log`) : connexions, échecs,
|
|
changements de rôle, suppressions de compte.
|
|
- **Téléversements** vérifiés par extension *et* par signature de fichier.
|
|
|
|
Ce qui **n'est pas** fait, pour que personne ne s'y fie :
|
|
|
|
- **Aucune migration de schéma.** `db.create_all()` crée les tables
|
|
manquantes et ne modifie jamais une table existante : une colonne ajoutée
|
|
à un modèle est absente de la production.
|
|
- **Les secrets de l'historique git ne sont pas révoqués** — jeton du bot,
|
|
mot de passe PostgreSQL, `SECRET_KEY`.
|
|
- **`trusted_proxy='*'`** reste le défaut : l'en-tête `X-Forwarded-For` est
|
|
accepté de n'importe quelle source, donc la limitation par IP est
|
|
contournable. C'est désormais la variable `TRUSTED_PROXY` plutôt qu'une
|
|
constante — `docs/deployment.md` donne la valeur pour chaque topologie.
|
|
- **Les comptes créés par le formulaire d'inscription sont actifs
|
|
immédiatement** : il n'y a pas d'étape de validation par le staff. Décision
|
|
de produit en attente, voir `docs/roles-and-permissions.md`.
|
|
- **L'identité Discord** transite encore par un champ caché du formulaire
|
|
d'inscription : elle n'est pas prouvée par le passage OAuth2.
|
|
|
|
`docs/security-checklist.md` détaille la liste avant mise en production.
|
|
|
|
### Documentation
|
|
|
|
| Document | Pour |
|
|
|---|---|
|
|
| `docs/deployment.md` | Installer, déployer, revenir en arrière |
|
|
| `docs/roles-and-permissions.md` | Qui peut faire quoi, et où c'est décidé |
|
|
| `docs/database-restore.md` | Sauvegarder et restaurer |
|
|
| `docs/database-schema.md` | Sortir de `create_all()` : relevé, Alembic, migrations |
|
|
| `docs/incident-runbook.md` | Quand quelque chose ne va pas |
|
|
| `docs/architecture.md` | Diagrammes |
|
|
| `docs/translations.md` | Ajouter ou corriger une traduction |
|
|
| `docs/security-checklist.md` | Avant une mise en production |
|
|
|
|
---
|
|
|
|
## Intégration Discord
|
|
|
|
Messages privés au coach lors d'une demande d'un-à-un, aux joueurs à la
|
|
création d'un match ou d'un entraînement les concernant, et rappel 24 h
|
|
avant un match. Les réponses se font par réaction sur le message ou depuis
|
|
le site.
|
|
|
|
L'état des messages en attente de réponse est dans `discord_pending.json`,
|
|
**non versionné** : c'est de l'état d'exécution, propre à chaque serveur.
|
|
|
|
### Mise en place
|
|
|
|
Le bot du club existe déjà ; ce qui suit ne concerne qu'une nouvelle
|
|
installation.
|
|
|
|
1. Créer une application sur le [portail développeur
|
|
Discord](https://discord.com/developers/applications), puis un bot.
|
|
2. Copier le jeton dans `DISCORD_BOT_TOKEN`.
|
|
3. Activer **Message Content Intent** dans les *Privileged Gateway Intents*.
|
|
C'est le seul intent privilégié demandé : il sert à lire le motif d'un
|
|
refus écrit en réponse au message.
|
|
4. Chaque personne doit partager un serveur avec le bot (ou l'avoir en ami)
|
|
pour recevoir un message privé, et renseigner son identifiant Discord
|
|
dans son profil (Discord → Paramètres → Avancés → Mode développeur, puis
|
|
clic droit sur son profil → Copier l'identifiant).
|
|
|
|
`/health` indique si le bot tourne et s'il est connecté.
|