mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Add script to (de|en)crypt all documents
This commit is contained in:
		
							
								
								
									
										119
									
								
								src/documents/management/commands/change_storage_type.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/documents/management/commands/change_storage_type.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| import os | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.core.management.base import BaseCommand, CommandError | ||||
| from termcolor import colored as coloured | ||||
|  | ||||
| from documents.models import Document | ||||
| from paperless.db import GnuPG | ||||
|  | ||||
|  | ||||
| class Command(BaseCommand): | ||||
|  | ||||
|     help = ( | ||||
|         "This is how you migrate your stored documents from an encrypted " | ||||
|         "state to an unencrypted one (or vice-versa)" | ||||
|     ) | ||||
|  | ||||
|     def add_arguments(self, parser): | ||||
|  | ||||
|         parser.add_argument( | ||||
|             "from", | ||||
|             choices=("gpg", "unencrypted"), | ||||
|             help="The state you want to change your documents from" | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             "to", | ||||
|             choices=("gpg", "unencrypted"), | ||||
|             help="The state you want to change your documents to" | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             "--passphrase", | ||||
|             help="If PAPERLESS_PASSPHRASE isn't set already, you need to " | ||||
|                  "specify it here" | ||||
|         ) | ||||
|  | ||||
|     def handle(self, *args, **options): | ||||
|  | ||||
|         try: | ||||
|             print(coloured( | ||||
|                 "\n\nWARNING: This script is going to work directly on your " | ||||
|                 "document originals, so\nWARNING: you probably shouldn't run " | ||||
|                 "this unless you've got a recent backup\nWARNING: handy.  It " | ||||
|                 "*should* work without a hitch, but be safe and backup your\n" | ||||
|                 "WARNING: stuff first.\n\nHit Ctrl+C to exit now, or Enter to " | ||||
|                 "continue.\n\n", | ||||
|                 "yellow", | ||||
|                 attrs=("bold",) | ||||
|             )) | ||||
|             __ = input() | ||||
|         except KeyboardInterrupt: | ||||
|             return | ||||
|  | ||||
|         if options["from"] == options["to"]: | ||||
|             raise CommandError( | ||||
|                 'The "from" and "to" values can\'t be the same.' | ||||
|             ) | ||||
|  | ||||
|         passphrase = options["passphrase"] or settings.PASSPHRASE | ||||
|         if not passphrase: | ||||
|             raise CommandError( | ||||
|                 "Passphrase not defined.  Please set it with --passphrase or " | ||||
|                 "by declaring it in your environment or your config." | ||||
|             ) | ||||
|  | ||||
|         if options["from"] == "gpg" and options["to"] == "unencrypted": | ||||
|             self.__gpg_to_unencrypted(passphrase) | ||||
|         elif options["from"] == "unencrypted" and options["to"] == "gpg": | ||||
|             self.__unencrypted_to_gpg(passphrase) | ||||
|  | ||||
|     @staticmethod | ||||
|     def __gpg_to_unencrypted(passphrase): | ||||
|  | ||||
|         encrypted_files = Document.objects.filter( | ||||
|             storage_type=Document.STORAGE_TYPE_GPG) | ||||
|  | ||||
|         for document in encrypted_files: | ||||
|  | ||||
|             print(coloured("🔓 Decrypting {}".format(document), "green")) | ||||
|  | ||||
|             old_paths = [document.source_path, document.thumbnail_path] | ||||
|             raw_document = GnuPG.decrypted(document.source_file, passphrase) | ||||
|             raw_thumb = GnuPG.decrypted(document.thumbnail_file, passphrase) | ||||
|  | ||||
|             document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED | ||||
|  | ||||
|             with open(document.source_path, "wb") as f: | ||||
|                 f.write(raw_document) | ||||
|  | ||||
|             with open(document.thumbnail_path, "wb") as f: | ||||
|                 f.write(raw_thumb) | ||||
|  | ||||
|             document.save(update_fields=("storage_type",)) | ||||
|  | ||||
|             for path in old_paths: | ||||
|                 os.unlink(path) | ||||
|  | ||||
|     @staticmethod | ||||
|     def __unencrypted_to_gpg(passphrase): | ||||
|  | ||||
|         unencrypted_files = Document.objects.filter( | ||||
|             storage_type=Document.STORAGE_TYPE_UNENCRYPTED) | ||||
|  | ||||
|         for document in unencrypted_files: | ||||
|  | ||||
|             print(coloured("🔒 Encrypting {}".format(document), "green")) | ||||
|  | ||||
|             old_paths = [document.source_path, document.thumbnail_path] | ||||
|             with open(document.source_path, "rb") as raw_document: | ||||
|                 with open(document.thumbnail_path, "rb") as raw_thumb: | ||||
|                     document.storage_type = Document.STORAGE_TYPE_GPG | ||||
|                     with open(document.source_path, "wb") as f: | ||||
|                         f.write(GnuPG.encrypted(raw_document, passphrase)) | ||||
|                     with open(document.thumbnail_path, "wb") as f: | ||||
|                         f.write(GnuPG.encrypted(raw_thumb, passphrase)) | ||||
|  | ||||
|             document.save(update_fields=("storage_type",)) | ||||
|  | ||||
|             for path in old_paths: | ||||
|                 os.unlink(path) | ||||
| @@ -3,7 +3,7 @@ import gnupg | ||||
| from django.conf import settings | ||||
|  | ||||
|  | ||||
| class GnuPG(object): | ||||
| class GnuPG: | ||||
|     """ | ||||
|     A handy singleton to use when handling encrypted files. | ||||
|     """ | ||||
| @@ -11,15 +11,22 @@ class GnuPG(object): | ||||
|     gpg = gnupg.GPG(gnupghome=settings.GNUPG_HOME) | ||||
|  | ||||
|     @classmethod | ||||
|     def decrypted(cls, file_handle): | ||||
|         return cls.gpg.decrypt_file( | ||||
|             file_handle, passphrase=settings.PASSPHRASE).data | ||||
|     def decrypted(cls, file_handle, passphrase=None): | ||||
|  | ||||
|         if not passphrase: | ||||
|             passphrase = settings.PASSPHRASE | ||||
|  | ||||
|         return cls.gpg.decrypt_file(file_handle, passphrase=passphrase).data | ||||
|  | ||||
|     @classmethod | ||||
|     def encrypted(cls, file_handle): | ||||
|     def encrypted(cls, file_handle, passphrase=None): | ||||
|  | ||||
|         if not passphrase: | ||||
|             passphrase = settings.PASSPHRASE | ||||
|  | ||||
|         return cls.gpg.encrypt_file( | ||||
|             file_handle, | ||||
|             recipients=None, | ||||
|             passphrase=settings.PASSPHRASE, | ||||
|             passphrase=passphrase, | ||||
|             symmetric=True | ||||
|         ).data | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Daniel Quinn
					Daniel Quinn