Learning by doing: Reading books and trying to understand the (code) examples
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
4.0 KiB

  1. # python modules
  2. import sys
  3. import pickle
  4. import base64
  5. import getpass
  6. # own modules
  7. from notebook import Notebook, Note
  8. # cryptography module
  9. from cryptography.fernet import Fernet, InvalidToken
  10. from cryptography.hazmat.primitives import hashes
  11. from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
  12. from cryptography.hazmat.backends import default_backend
  13. class Menu:
  14. '''Display a menu and respond to choices when run.'''
  15. def __init__(self):
  16. self.salt = "a683c64de226677703f56e6b6ead94bbc3690ec5293c3de3ffdc"
  17. self.savefile = 'notebook.safe'
  18. self.notebook = Notebook()
  19. self.choices = {
  20. "1": self.show_notes,
  21. "2": self.search_notes,
  22. "3": self.add_note,
  23. "4": self.modify_note,
  24. "5": self.load_notes,
  25. "6": self.save_notes,
  26. "7": self.quit
  27. }
  28. def display_menu(self):
  29. print("""
  30. Notebook Menu
  31. 1. Show all Notes
  32. 2. Search Notes
  33. 3. Add Note
  34. 4. Modify Note
  35. 5. Load Notes
  36. 6. Save Notes
  37. 7. Quit """)
  38. def run(self):
  39. '''Display the menu and respond to choices.'''
  40. while True:
  41. self.display_menu()
  42. choice = input("Enter an option: ")
  43. action = self.choices.get(choice)
  44. try:
  45. action()
  46. except TypeError:
  47. print("{0} is not a valid choice".format(choice))
  48. def show_notes(self, notes=None):
  49. '''Display all notes stored in notebook object'''
  50. if not notes:
  51. notes = self.notebook.notes
  52. for note in notes:
  53. print("{0}: {1}\n{2}".format(note.id, note.tags, note.memo))
  54. def search_notes(self):
  55. '''Search for a note containing given string'''
  56. filter = input("Search for: ")
  57. notes = self.notebook.search(filter)
  58. self.show_notes(notes)
  59. def add_note(self):
  60. '''Add a given not to notebook object'''
  61. memo = input("Enter a memo: ")
  62. self.notebook.new_note(memo)
  63. print("Your note has been added.")
  64. def modify_note(self):
  65. '''Modify tag and memo of note with given id'''
  66. id = input("Enter a note id: ")
  67. memo = input("Enter a memo: ")
  68. tags = input("Enter tags: ")
  69. if memo:
  70. if not self.notebook.modify_memo(id, memo):
  71. print("Note with id {0} doesn't exist.".format(id))
  72. return
  73. if tags:
  74. if not self.notebook.modify_tags(id, tags):
  75. print("Note with id {0} doesn't exist.".format(id))
  76. def load_notes(self):
  77. '''Decrypt notebook safe file and load it into notebook object'''
  78. try:
  79. f = open(self.savefile, 'rb')
  80. except IOError:
  81. print("Could not open file")
  82. else:
  83. cipher = f.read()
  84. f.close()
  85. crypt = Fernet(self._get_password())
  86. try:
  87. plain = crypt.decrypt(cipher)
  88. except InvalidToken:
  89. print("Wrong password")
  90. else:
  91. self.notebook = pickle.loads(plain)
  92. def save_notes(self):
  93. '''Encrypt notebook object and store it into notebook safe file'''
  94. plain = pickle.dumps(self.notebook, pickle.HIGHEST_PROTOCOL)
  95. crypt = Fernet(self._get_password())
  96. cipher = crypt.encrypt(plain)
  97. try:
  98. f = open(self.savefile, 'wb')
  99. except IOError:
  100. print("Could not open file")
  101. else:
  102. f.write(cipher)
  103. f.close()
  104. def _get_password(self):
  105. '''Request passphrase and derive key from it'''
  106. passphrase = getpass.getpass()
  107. kdf = PBKDF2HMAC(
  108. algorithm = hashes.SHA256(),
  109. length = 32,
  110. salt = self.salt.encode('utf-8'),
  111. iterations = 10000,
  112. backend = default_backend()
  113. )
  114. return base64.urlsafe_b64encode(kdf.derive(passphrase.encode('utf-8')))
  115. def quit(self):
  116. '''Quit application'''
  117. print("Thank you for using your notebook today.")
  118. sys.exit(0)
  119. if __name__ == "__main__":
  120. Menu().run()