Semaine 11

This commit is contained in:
gauvainboiche
2026-07-11 21:38:00 +02:00
parent d3d2416dea
commit 0d071d2843
281 changed files with 54412 additions and 0 deletions
+405
View File
@@ -0,0 +1,405 @@
# Examen final — Programmation web en Python (Modules 1 à 5)
**Nom / Prénom :** …………………………………………… **Groupe :** …………
## Consignes
- Épreuve **individuelle**, **notes de cours autorisées**.
- **45 questions fermées (QCM)** : une **seule** réponse correcte par question — entourez la lettre.
- **13 questions ouvertes** : répondez dans l'espace prévu ; pour les analyses de code, soyez précis (ligne, cause, correction).
- Contexte de toutes les questions : la **gestion d'un parc d'équipements réseau** (serveurs, switchs, routeurs, interfaces).
---
**Q1.** Que vaut `len(set([22, 80, 443, 22, 80]))` ?
- a) 5
- b) 3
- c) 2
- d) 4
**Q2.** Parmi ces méthodes HTTP, laquelle **n'est pas** idempotente ?
- a) GET
- b) PUT
- c) POST
- d) DELETE
**Q3.** Quel code de statut HTTP indique qu'une ressource a été **créée** ?
- a) 200
- b) 201
- c) 301
- d) 404
**Question ouverte O1 *(2 pts)*.** Expliquez en deux phrases la différence entre **Internet** et le **Web**.
> Réponse : ………………………………………………………………………………………………………………………
**Q4.** *(Analyse de code)* Que produit l'exécution de cet extrait ?
```python
adresse = ("10.0.0.1", 22)
adresse[1] = 443
```
- a) Affiche `('10.0.0.1', 443)`
- b) Lève une `TypeError`
- c) Affiche `('10.0.0.1', 22)`
- d) Lève une `IndexError`
**Q5.** Quelle structure Python **n'autorise pas les doublons** et **n'est pas ordonnée** ?
- a) `list`
- b) `tuple`
- c) `set`
- d) `dict`
**Q6.** Dans une API REST, quelle URL respecte les bonnes pratiques ?
- a) `/getEquipements`
- b) `/equipements`
- c) `/equipement/delete/5`
- d) `/list`
**Q7.** *(Analyse de code)* Qu'affiche `print(2 ** 6 - 2)` (nombre d'hôtes d'un /26) ?
- a) 64
- b) 62
- c) 126
- d) 30
**Question ouverte O2 *(4 pts)*.** Écrivez une fonction Python `hotes_disponibles(bits_hote)` qui renvoie le nombre d'adresses **hôtes utilisables** d'un sous-réseau, puis donnez le résultat pour un **/24**.
> Réponse :
> ```python
>
> ```
**Q8.** Que renvoie une fonction Python qui ne contient **aucun** `return` ?
- a) `0`
- b) `""`
- c) `None`
- d) `False`
**Q9.** À quoi sert l'en-tête HTTP `Content-Type: application/json` ?
- a) À authentifier le client
- b) À indiquer le format du corps du message
- c) À définir la méthode HTTP
- d) À fixer le code de statut
**Q10.** Quel format de données est le standard d'échange des API REST modernes ?
- a) XML
- b) CSV
- c) JSON
- d) YAML
**Question ouverte O3 *(3 pts)*.** Qu'est-ce que l'**idempotence** d'une méthode HTTP ? Donnez une méthode idempotente et une non idempotente, en justifiant.
> Réponse : ………………………………………………………………………………………………………………………
---
**Q11.** À quoi sert une **clé primaire** ?
- a) À relier deux tables
- b) À identifier de façon unique chaque ligne
- c) À trier les résultats
- d) À chiffrer les données
**Q12.** Une **clé étrangère** permet de…
- a) accélérer les requêtes
- b) créer une relation vers la clé primaire d'une autre table
- c) empêcher les doublons
- d) définir le type d'une colonne
**Q13.** *(Analyse de code)* Qu'affiche cet extrait ?
```python
resultats = [("srv-web-01", "192.168.1.10"), ("sw-access-02", "192.168.1.2")]
print(resultats[1][0])
```
- a) `srv-web-01`
- b) `192.168.1.10`
- c) `sw-access-02`
- d) `192.168.1.2`
**Question ouverte O4 *(3 pts)*.** Expliquez ce qu'est une **clé étrangère** et à quoi elle sert, à l'aide d'un exemple `equipement` / `interface`.
> Réponse : ………………………………………………………………………………………………………………………
**Q14.** En SQL, quelle commande permet de **lire** des données ?
- a) `INSERT`
- b) `SELECT`
- c) `UPDATE`
- d) `DELETE`
**Q15.** Pourquoi utiliser des `?` (requête paramétrée) avec `sqlite3` ?
- a) C'est plus rapide
- b) Pour éviter les injections SQL
- c) C'est obligatoire
- d) Pour trier les résultats
**Q16.** « Un équipement possède plusieurs interfaces » : de quel type de relation s'agit-il ?
- a) Un-à-un (1-1)
- b) Un-à-plusieurs (1-N)
- c) Plusieurs-à-plusieurs (N-N)
- d) Aucune relation
**Q17.** Dans SQLAlchemy 2.0, quel objet gère les opérations `add()` / `commit()` ?
- a) `Engine`
- b) `Session`
- c) `Model`
- d) `Cursor`
**Question ouverte O5 *(4 pts — analyse de code)*.** Identifiez la **faille de sécurité** de cet extrait et **corrigez-la**.
```python
hostname = input("hostname ? ")
cur.execute(f"SELECT * FROM equipement WHERE hostname = '{hostname}'")
```
> Réponse : ………………………………………………………………………………………………………………………
**Q18.** Que fait `cursor.fetchall()` ?
- a) Exécute la requête
- b) Valide la transaction
- c) Renvoie toutes les lignes du résultat
- d) Ferme la connexion
**Q19.** Une relation **N-N** (plusieurs-à-plusieurs) nécessite…
- a) uniquement une clé primaire composite
- b) une table de liaison (d'association)
- c) un index
- d) rien de particulier
**Q20.** Quel est un **avantage** de l'ORM par rapport au SQL brut ?
- a) Il est toujours plus rapide
- b) Il manipule des objets Python et facilite le changement de SGBD
- c) Il supprime le besoin de base de données
- d) Il empêche toute erreur
**Question ouverte O6 *(3 pts — analyse de code)*.** Après ce code, la ligne n'apparaît pas dans la base. **Pourquoi** et **comment corriger** ?
```python
conn = sqlite3.connect("inventaire.db")
cur = conn.cursor()
cur.execute("INSERT INTO equipement (hostname, ip, type) VALUES (?, ?, ?)",
("srv-dns-01", "192.168.1.20", "serveur"))
conn.close()
```
> Réponse : ………………………………………………………………………………………………………………………
---
**Q21.** Flask est un…
- a) framework full-stack
- b) micro-framework
- c) ORM
- d) serveur web de production
**Q22.** *(Analyse de code)* Que renvoie cette vue Flask ?
```python
@app.route("/api/statut")
def statut():
return {"etat": "ok"}
```
- a) Une erreur 500
- b) Du texte brut
- c) Une réponse **JSON** avec le code **200**
- d) Une réponse 204 sans contenu
**Q23.** Quel décorateur associe une URL à une fonction en Flask ?
- a) `@app.get_route`
- b) `@app.route`
- c) `@route`
- d) `@flask.url`
**Question ouverte O7 *(3 pts)*.** À quoi sert la bibliothèque `requests` ? Donnez un exemple d'appel **GET** avec un **timeout**.
> Réponse :
> ```python
>
> ```
**Q24.** Comment lit-on le corps **JSON** d'une requête entrante en Flask ?
- a) `request.body`
- b) `request.get_json()`
- c) `request.json_body`
- d) `flask.read()`
**Q25.** Avec `requests`, comment envoyer un corps **JSON** dans un POST ?
- a) `data=...`
- b) `body=...`
- c) `json=...`
- d) `payload=...`
**Q26.** Que fait `response.raise_for_status()` ?
- a) Fixe le code de statut
- b) Lève une exception si le code est ≥ 400
- c) Convertit la réponse en JSON
- d) Ferme la connexion
**Q27.** En Jinja2, quelle syntaxe **affiche** une variable ?
- a) `{% variable %}`
- b) `{{ variable }}`
- c) `${variable}`
- d) `<%= variable %>`
**Question ouverte O8 *(4 pts — analyse de code)*.** Un client envoie `POST /api/equipements` et reçoit `405 Method Not Allowed`. Voici la route. **Pourquoi** ? **Corrigez-la.**
```python
@app.route("/api/equipements")
def creer_equipement():
donnees = request.get_json()
...
```
> Réponse : ………………………………………………………………………………………………………………………
**Q28.** En Jinja2, quelle syntaxe permet d'écrire une **boucle** ?
- a) `{{ for e in liste }}`
- b) `{% for e in liste %} … {% endfor %}`
- c) `<for e in liste>`
- d) `@for(e in liste)`
**Q29.** Quel moteur de templates Flask utilise-t-il par défaut ?
- a) Mako
- b) Jinja2
- c) Django Templates
- d) Handlebars
**Q30.** Comment renvoyer un JSON **avec le code 201** en Flask ?
- a) `return jsonify(x)`
- b) `return jsonify(x), 201`
- c) `return 201, x`
- d) `return status(201)`
**Question ouverte O9 *(3 pts)*.** Écrivez une route Flask `GET /api/equipements/<int:equipement_id>` qui renvoie l'équipement en JSON, ou une erreur **404** s'il n'existe pas (liste `equipements` en mémoire). Expliquez brièvement votre code.
> Réponse :
> ```python
>
> ```
---
**Q31.** Django est un framework…
- a) micro
- b) full-stack (« batteries included »)
- c) uniquement pour les API
- d) uniquement frontend
**Q32.** Que signifie l'architecture **MTV** de Django ?
- a) Model-Template-View
- b) Model-View-Template
- c) Method-Type-Value
- d) Module-Test-Vue
**Q33.** Quelle commande **génère** les fichiers de migration ?
- a) `migrate`
- b) `makemigrations`
- c) `startapp`
- d) `runserver`
**Question ouverte O10 *(3 pts)*.** Qu'est-ce qu'une **migration** Django et pourquoi en a-t-on besoin ? (une analogie est acceptée)
> Réponse : ………………………………………………………………………………………………………………………
**Q34.** Quelle commande **applique** les migrations à la base de données ?
- a) `makemigrations`
- b) `migrate`
- c) `syncdb`
- d) `commit`
**Q35.** *(Analyse de code)* L'équipement 999 n'existe pas. Que se passe-t-il ?
```python
Equipement.objects.get(id=999)
```
- a) Renvoie `None`
- b) Renvoie `[]`
- c) Lève `Equipement.DoesNotExist`
- d) Renvoie une erreur 404
**Q36.** Dans DRF, le rôle d'un **sérialiseur** est de…
- a) router les URLs
- b) convertir les objets ↔ JSON et valider les données
- c) gérer les sessions
- d) créer la base de données
**Q37.** Quelle classe DRF fournit **automatiquement** tout le CRUD ?
- a) `APIView`
- b) `ModelViewSet`
- c) `Serializer`
- d) `Router`
**Question ouverte O11 *(4 pts — analyse de code)*.** Cette vue déclenche l'erreur « *didn't return an HttpResponse object* ». **Pourquoi** ? **Corrigez-la.**
```python
def liste_equipements(request):
return Equipement.objects.filter(actif=True)
```
> Réponse : ………………………………………………………………………………………………………………………
**Q38.** À quoi sert un `DefaultRouter` en DRF ?
- a) À valider les données
- b) À générer automatiquement les URLs d'un ViewSet
- c) À sérialiser les objets
- d) À authentifier les utilisateurs
**Q39.** La permission `IsAuthenticatedOrReadOnly` autorise…
- a) tout à tout le monde
- b) la lecture à tous, l'écriture aux utilisateurs authentifiés
- c) rien aux anonymes
- d) l'écriture à tous
**Q40.** Quel attribut booléen du modèle `User` donne accès à l'interface `/admin/` ?
- a) `is_active`
- b) `is_staff`
- c) `is_superuser`
- d) `is_admin`
**Question ouverte O12 *(4 pts)*.** Écrivez un `EquipementSerializer` (DRF `ModelSerializer`) exposant `id`, `hostname`, `ip`, `actif`, puis expliquez en une phrase la différence entre `APIView` et `ModelViewSet`.
> Réponse :
> ```python
>
> ```
**Q41.** Un utilisateur authentifié accède aux équipements d'un autre en modifiant simplement l'identifiant dans l'URL (/api/equipements/42/). De quel risque OWASP s'agit-il ?
- a) API8 — Security Misconfiguration
- b) API1 — Broken Object Level Authorization (BOLA)
- c) API7 — Server Side Request Forgery
- d) API4 — Unrestricted Resource Consumption
**Q42.** Dans un ViewSet DRF, quelle technique garantit que chaque utilisateur ne voie que ses ressources ?
- a) Mettre fields = "__all__" dans le sérialiseur
- b) Surcharger get_queryset() en filtrant sur request.user
- c) Définir permission_classes = [AllowAny]
- d) Activer la pagination
**Q43.** Pourquoi éviter fields = "__all__" dans un ModelSerializer exposé par une API ?
- a) Cela ralentit les requêtes
- b) Cela désactive l'authentification
- c) Cela peut exposer des champs sensibles et autoriser le mass assignment (API3 BOPLA)
- d) Cela empêche la pagination
**Q44.** Une API récupère une URL fournie par l'utilisateur (webhook). Quelle est la meilleure protection contre la SSRF (API7) ?
- a) Augmenter le timeout de la requête
- b) Tenir une liste noire de chaînes comme "127.0.0.1"
- c) Valider le schéma puis bloquer les IP privées/loopback/link-local (ou utiliser une allowlist)
- d) Renvoyer le corps de la réponse distante au client
**Q45.** Quel code de statut HTTP DRF renvoie-t-il lorsqu'un client dépasse la limite de débit (throttling, API4) ?
- a) 401 Unauthorized
- b) 403 Forbidden
- c) 429 Too Many Requests
- d) 500 Internal Server Error
**Question ouverte O13** - Trouver et corriger la faille (analyse de code)
Le développeur veut renvoyer « mes équipements ». Voici sa vue DRF. Identifiez la faille, nommez le risque OWASP, expliquez l'impact, puis corrigez le code.
# parc/views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Equipement
from .serializers import EquipementSerializer
@api_view(["GET"])
def mes_equipements(request):
user_id = request.query_params.get("user_id") # ex. /api/mes-equipements/?user_id=1
equipements = Equipement.objects.filter(proprietaire_id=user_id)
return Response(EquipementSerializer(equipements, many=True).data)
> Réponse : ………………………………………………………………………………………………………………………
FIN DU DOCUMENT