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 | from django.conf import settings | ||||||
|  |  | ||||||
|  |  | ||||||
| class GnuPG(object): | class GnuPG: | ||||||
|     """ |     """ | ||||||
|     A handy singleton to use when handling encrypted files. |     A handy singleton to use when handling encrypted files. | ||||||
|     """ |     """ | ||||||
| @@ -11,15 +11,22 @@ class GnuPG(object): | |||||||
|     gpg = gnupg.GPG(gnupghome=settings.GNUPG_HOME) |     gpg = gnupg.GPG(gnupghome=settings.GNUPG_HOME) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def decrypted(cls, file_handle): |     def decrypted(cls, file_handle, passphrase=None): | ||||||
|         return cls.gpg.decrypt_file( |  | ||||||
|             file_handle, passphrase=settings.PASSPHRASE).data |         if not passphrase: | ||||||
|  |             passphrase = settings.PASSPHRASE | ||||||
|  |  | ||||||
|  |         return cls.gpg.decrypt_file(file_handle, passphrase=passphrase).data | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def encrypted(cls, file_handle): |     def encrypted(cls, file_handle, passphrase=None): | ||||||
|  |  | ||||||
|  |         if not passphrase: | ||||||
|  |             passphrase = settings.PASSPHRASE | ||||||
|  |  | ||||||
|         return cls.gpg.encrypt_file( |         return cls.gpg.encrypt_file( | ||||||
|             file_handle, |             file_handle, | ||||||
|             recipients=None, |             recipients=None, | ||||||
|             passphrase=settings.PASSPHRASE, |             passphrase=passphrase, | ||||||
|             symmetric=True |             symmetric=True | ||||||
|         ).data |         ).data | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Daniel Quinn
					Daniel Quinn