ciel-b1/src/cours/CIEL1/01-bases-python/tp/TP_MDP04_fichiers.md

9.7 KiB

TP4 - Générateur de mots de passe - Fichiers

Objectifs du TP

Objectifs pédagogiques

  • Fichiers : Comprendre comment manipuler des fichiers en Python (lecture et écriture)
  • Dictionnaires : Manipuler des dictionnaires en Python

Objectifs techniques

  • Écrire des mots de passe générés dans un fichier au lieu de les afficher en console
  • Récupérer la liste des mots pour générer des passphrases directement depuis un fichier

Validation

Livrables

  • Le code final
  • Le compte-rendu du TP

Le tout dans un répertoire avec le nom suivant YYYY-MM-DD_TPX_NOM1_Prenom1_NOM2_Prenom2.

  • Le compte rendu peut-être livré au format Markdown ou PDF.
  • Si vous choisissez le Markdown, attention à bien livrer les éventuelles images et à la validité des liens.

Résultats attendus

  • Le programme se lance sans erreur.
  • Chaque fonction contient une docstring minimale expliquant sa fonctionnalité. Bonus : Doctsrings complètes au format Numpy.
  • Le code est propre, correctement formaté, les variables, fonctions et paramètres sont bien nommés, les conventions sont respectées.

Critères d'évaluation

  • 50% Technique (10pts)
    • Code fonctionnel
    • Réponses dans le compte-rendu
    • Questions orale et compréhension générale
    • Connaissance du cours
  • 50% Professionnalisme (10pts)
    • Posture professionnelle durant l'activité (3pts)
    • Qualité du rendu (Compte rendu - Documentation - Code) (3pts)
    • Respect des délais/horaires (2pts)
    • Autonomie dans la progression (2pts)
  • Bonus +2pts

Documents fournis

  • Cours
    • Python - 5 - Les fichiers
  • Docs
    • Listes de mots en anglais
    • Listes de mots en français

Étapes

1 - Ouvrir un fichier pour lire la liste des mots

Objectif : Créer une fonction qui ira lire les mots dans un fichier et utiliser cette liste pour générer des passphrases en français

1. Dans votre module `mdp`, créer une nouvelle fonction `get_word_list_from_file`. Cette fonction prendra un paramètre `filename` et la valeur par défaut `"wordlist_fr.txt"`.
1. Ouvrir le fichier à l'aide d'un **gestionnaire de contexte**
1. Lire toutes les lignes du fichier, et les stocker dans la variable `lines` et retourner cette variable
1. Remplacer l'appel à la fonction `get_word_list` dans votre fonction `generer_passphrase` par votre nouvelle fonction.
1. Vérifier que votre programme génère désormais des passphrases en français.
Un gestionnaire de contexte est la méthode recommandée par Python pour ouvrir et fermer un fichier en toute sécurité. La syntaxe est la suivante:
~~~python
with open(filename, mode) as file:
    # Actions sur le fichier
~~~

Cette syntaxe présente l'avantage de fermer systématiquement votre fichier à la sortie du bloc de contexte, et évite donc de nombreux risques de bugs pour votre application. Elle définit une variable `file` qui sera détruite une fois sorti du bloc de contexte.
1. Donnez le code de votre fonction
2. Si nous n'avions pas utilisé de gestionnaire de contexte (`with ... as ...:`), qu'aurait-on dû absolument faire après avoir travaillé avec notre fichier ? Quelle méthode aurions-nous dû utiliser pour ça ?

2 - Écrire dans un fichier

Objectif : Écrire un mot de passe généré dans un fichier au lieu de l'afficher en console

1. Dans votre module principal (`main.py`), créer une fonction `write_password_in_file` qui prendra en paramètres : 
    - `password`
    - `filename` avec une valeur par défaut `passwords.txt`
2. Dans cette fonction, ouvrir votre fichier en mode "append" avec un gestionnaire de contexte.
3. Écrire le contenu de la variable `password` dans le fichier
4. Créer une nouvelle entrée dans votre menu intitulée `5 - Générer un fichier avec des passphrases`. 
    - Adapter votre fonction `choix_utilisateur` pour gérer ce nouveau cas. 
    - Vous ferez appel aux fonctions `generer_passphrase` et `write_password_in_file` pour écrire votre passphrase dans un fichier.
5. Testez votre programme pour vérifier que vos passphrases s'écrivent bien dans le fichier.
1. Donnez le code de votre nouvelle fonction
1. Que constatez-vous dans le fichier si vous appelez plusieurs fois votre programme à la suite ?
2. Quelle est la différence entre le mode "write" et le mode "append" ?
3. Quel mode puis-je utiliser si je veux à la fois lire et écrire dans mon fichier ?

3 - Faire un vrai jet de dés

Dans la suite, nous allons aller au bout du processus "diceware" décrit par l'EFF dans le document du TP3. C'était déjà l'objet de l'étape 6 du TP précédent.

Les personnes ayant terminé l'étape 6 dans le TP précédent (celle qui vous faisait tirer les dés et manipuler des dictionnaires) :
- Dans votre compte-rendu, aux parties 3 et 4, répondez aux questions et ajoutez votre code déjà réalisé
- Passez ensuite au bonus `Lire le dictionnaire à partir d'un fichier` de ce TP.
- Vous pourrez ensuite réaliser les bonus des TP précédents.

Objectif : Tirer 5 dés et les assembler en une chaîne de caractères

1. Créer une nouvelle fonction `tirer_les_des` dans votre module `mdp`.
2. Dans cette fonction, commencer par créer une variable `dice` qui sera une chaîne de caractères vide.
3. Créer une boucle qui fera 5 itérations. Dans cette boucle :
    - Tirer au sort un nombre entre 1 et 6. Vous utiliserez pour cela [`secrets.randbelow`](https://docs.python.org/3/library/secrets.html#secrets.randbelow).
    - Lisez la documentation de `secrets.randbelow` pour comprendre son fonctionnement
    - Ajouter ce nombre sous forme de chaîne de caractères à votre variable `dice`
4. A la fin de la boucle, retourner la valeur de `dice`
5. Vérifiez que votre fonction retourne une chaîne de caractères aléatoire ressemblant à `"12345"`
- `secrets.randbelow` retourne un nombre entier. Vous aurez besoin d'une chaine de caractères. Pensez à vos conversions si vous rencontrez l'erreur suivante :
  ~~~
  TypeError: can only concatenate str (not "int") to str
  ~~~
- `secrets.randbelow(n)` retourne un nombre compris entre `0` et `n-1`. Pour avoir un nombre compris entre `1` et `n`, il vous faudra ajouter 1 au résultat.
1. Mettez le code de votre fonction `tirer_les_des`

4 - Générer une passphrase tirée aux dés

Objectif : Utiliser votre tirage de dés pour sélectionner les mots dans un dictionnaire Python

1. Créer une fonction `generer_dice_passphrase` qui sera une copie de `generer_passphrase` pour commencer
1. Remplacer l'appel à `get_word_list_from_file` par `get_word_dict` du module `eff_words`. Cette fonction retourne un dictionnaire dont les **clés** sont les tirages de dés et les **valeurs** sont les mots.
1. Modifier la boucle de `generer_dice_passphrase` pour 
    - appeler `tirer_les_des` et récupérer le jet de dés dans une variable `dice`
    - utiliser l'une des deux syntaxes possibles permettant de récupérer une valeur dans un dictionnaire à l'aide de la clé. Votre but est de récupérer le mot à partir du tirage de dés
    - Ajoutez ce mot à votre liste `passphrase_words`
6. Remplacez l'appel à `generer_passphrase` dans votre menu par un appel à `generer_dice_passphrase` et validez que votre programme fonctionne toujours correctement.
1. Donnez le code de votre nouvelle fonction `generer_dice_passphrase`
1. Quelle est la différence entre la syntaxe `mon_dict[key]` et `mon_dict.get(key)`, sachant que `mon_dict` est un dictionnaire python ?

Pour aller plus loin

Bonus facile - Docstrings au format Numpy

Objectif: Documenter votre code en ajoutant des docstrings au format Numpy

Bonus - Lire les dictionnaires à partir d'un fichier

Objectif : Lire le dictionnaire utilisé dans votre fonction generer_dice_passphrase depuis le fichier bonus_wordlist_fr.txt

1. Dans votre module `mdp`, créer une nouvelle fonction `get_word_dict_from_file`. Cette fonction prendra un paramètre `filename` et la valeur par défaut `"wordlist_fr.txt"`.
1. Ouvrir le fichier à l'aide d'un **gestionnaire de contexte**
1. Lire toutes les lignes du fichier, et les stocker dans la variable `lines`
1. Il va falloir maintenant construire votre dictionnaire à partir des lignes lues. Chaque ligne est composée de la manière suivante : `12345 mot\n` (5 chiffres représentant le tirage de dés, un espace puis un mot, et enfin un retour à la ligne)
1. Retournez votre dictionnaire
1. Remplacer l'appel à la fonction `get_word_dict` dans votre fonction `generer_dice_passphrase` par votre nouvelle fonction.
1. Vérifier que votre programme génère désormais des passphrases en français.
- Pour séparer une chaine de caractères, on peut utiliser la méthode [`split`](https://docs.python.org/3/library/stdtypes.html#str.split) sur la chaîne de caractères.
- Pour retirer les espaces et autres passages à la ligne autour des mots, on peut utiliser la méthode [`strip`](https://docs.python.org/3/library/stdtypes.html#str.strip)
- Pour construire votre dictionnaire, vous devrez boucler sur vos lignes, séparer les deux mots et les nettoyer. Puis vous pourrez les ajouter au dictionnaire avec la syntaxe `mon_dict[key] = value`. N'oubliez pas d'initialiser un dictionnaire vide (`{}`) avant votre boucle.

Ressources