13 KiB
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 ?
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 :
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 ?
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.
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 ?
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 ?
@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 :
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.
@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 :
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 ?
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.
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 :
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