# 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 ```admonish travail 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. ``` ```admonish help title="Aide" 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. ``` ```admonish note title="Dans le compte rendu" 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 ```admonish travail 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. ``` ```admonish note title="Dans le compte rendu" 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 ```admonish warning title="Attention" 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 ```admonish travail 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"` ``` ```admonish help title="Aide" - `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. ``` ```admonish note title="Dans le compte rendu" 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 ```admonish travail 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. ``` ```admonish note title="Dans le compte rendu" 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` ```admonish travail 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. ``` ```admonish help title="Aide" - 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 - [Liste de mots diceware en français](https://github.com/mbelivo/diceware-wordlists-fr/tree/master) - [xkcd - Password Strength](https://xkcd.com/936/)