fix(config): valider l'alias CalDAV legacy et retirer file:// de fetch_ical
Supprime le contournement model_construct : l'alias CALDAV_URL est désormais validé comme le champ canonique (userinfo, host et port rejetés). Retire le support file:// de fetch_ical et corrige l'exemple résiduel du guide. Ajoute les tests négatifs de l'alias legacy et adapte le test de sécurité CalDAV au contrat durci. Refs #63
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""Récupération et parsing du flux iCal Pronote.
|
||||
|
||||
Ce module fournit le téléchargement du flux iCal Pronote (via HTTP ou
|
||||
``file://`` pour les tests) ainsi que son parsing en modèles : cours
|
||||
Ce module fournit le téléchargement réseau du flux iCal Pronote ainsi que
|
||||
son parsing en modèles : cours
|
||||
(:class:`~pronote_sync.models.agenda.Lesson`), événements scolaires
|
||||
(:class:`~pronote_sync.models.agenda.SchoolEvent`) et devoirs
|
||||
(:class:`~pronote_sync.models.homework.Homework`).
|
||||
@@ -15,10 +15,8 @@ from __future__ import annotations
|
||||
import hashlib
|
||||
import re
|
||||
import unicodedata
|
||||
import urllib.parse
|
||||
from datetime import date, datetime
|
||||
from html import unescape
|
||||
from pathlib import Path
|
||||
from typing import NamedTuple, TypedDict
|
||||
|
||||
import requests
|
||||
@@ -70,35 +68,21 @@ class ParsedHomeworkBlock(NamedTuple):
|
||||
|
||||
|
||||
def fetch_ical(url: str, timeout: int = 20) -> str:
|
||||
"""Récupère le contenu brut d'un flux iCal Pronote.
|
||||
"""Récupère le contenu brut d'un flux iCal Pronote par HTTPS.
|
||||
|
||||
Gère les URLs ``file://`` pour les tests locaux (le chemin est
|
||||
décodé de l'échappement URI, ex. ``%20`` → espace) et valide que
|
||||
le flux commence bien par ``BEGIN:VCALENDAR``. Toutes les erreurs
|
||||
sont relancées avec un message dont les secrets (token
|
||||
``icalsecurise``) sont masqués.
|
||||
Le schéma ``file://`` n'est pas supporté : les fixtures locales doivent
|
||||
être injectées via un mock HTTP (``responses``) dans les tests. Valide que
|
||||
le flux commence bien par ``BEGIN:VCALENDAR``. Toutes les erreurs sont
|
||||
relancées avec un message dont les secrets (token ``icalsecurise``) sont
|
||||
masqués.
|
||||
|
||||
:param url: URL du flux iCal (avec token ``icalsecurise``) ou chemin ``file://``.
|
||||
:param url: URL HTTPS du flux iCal (avec token ``icalsecurise``).
|
||||
:param timeout: Timeout HTTP en secondes (défaut : 20).
|
||||
:return: Contenu brut du flux iCal.
|
||||
:rtype: str
|
||||
:raises OSError: Si le fichier local ``file://`` est illisible.
|
||||
:raises requests.RequestException: Si la récupération HTTP échoue.
|
||||
:raises ValueError: Si le flux ne commence pas par ``BEGIN:VCALENDAR``.
|
||||
"""
|
||||
if url.startswith("file://"):
|
||||
parsed_url = urllib.parse.urlparse(url)
|
||||
path = Path(urllib.parse.unquote(parsed_url.path))
|
||||
try:
|
||||
content = path.read_text(encoding="utf-8")
|
||||
except OSError as exc:
|
||||
raise OSError(
|
||||
f"Impossible de lire le fichier iCal {redact_url(url)} : {redact_exception(exc)}"
|
||||
) from exc
|
||||
if not content.lstrip().startswith("BEGIN:VCALENDAR"):
|
||||
raise ValueError(f"Fichier iCal invalide (pas de BEGIN:VCALENDAR) : {redact_url(url)}")
|
||||
return content
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=_HEADERS, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
|
||||
Reference in New Issue
Block a user