Files
team-tryouts/README.md
T
GGThed 7a1dab21cd docs: les deux documents d exploitation qui manquaient
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.
2026-08-11 15:40:30 -04:00

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é.