set -eu
-for command in decrypt_documents \
- document_archiver \
+for command in document_archiver \
document_exporter \
document_importer \
mail_fetcher \
+++ /dev/null
-#!/command/with-contenv /usr/bin/bash
-# shellcheck shell=bash
-
-set -e
-
-cd "${PAPERLESS_SRC_DIR}"
-
-if [[ $(id -u) == 0 ]]; then
- s6-setuidgid paperless python3 manage.py decrypt_documents "$@"
-elif [[ $(id -un) == "paperless" ]]; then
- python3 manage.py decrypt_documents "$@"
-else
- echo "Unknown user."
-fi
documents, such as encrypted PDF documents. The archiver will skip over
these documents each time it sees them.
-### Managing encryption {#encryption}
-
-!!! warning
-
- Encryption was removed in [paperless-ng 0.9](changelog.md#paperless-ng-090)
- because it did not really provide any additional security, the passphrase
- was stored in a configuration file on the same system as the documents.
- Furthermore, the entire text content of the documents is stored plain in
- the database, even if your documents are encrypted. Filenames are not
- encrypted as well. Finally, the web server provides transparent access to
- your encrypted documents.
-
- Consider running paperless on an encrypted filesystem instead, which
- will then at least provide security against physical hardware theft.
-
-#### Enabling encryption
-
-Enabling encryption is no longer supported.
-
-#### Disabling encryption
-
-Basic usage to disable encryption of your document store:
-
-(Note: If `PAPERLESS_PASSPHRASE` isn't set already, you need to specify
-it here)
-
-```
-decrypt_documents [--passphrase SECR3TP4SSPHRA$E]
-```
-
### Detecting duplicates {#fuzzy_duplicate}
Paperless already catches and prevents upload of exactly matching documents,
| `CONSUMER_POLLING_RETRY_COUNT` | _Removed_ | Automatic with stability tracking |
| `CONSUMER_IGNORE_PATTERNS` | [`CONSUMER_IGNORE_PATTERNS`](configuration.md#PAPERLESS_CONSUMER_IGNORE_PATTERNS) | **Now regex, not fnmatch**; user patterns are added to (not replacing) default ones |
| _New_ | [`CONSUMER_IGNORE_DIRS`](configuration.md#PAPERLESS_CONSUMER_IGNORE_DIRS) | Additional directories to ignore; user entries are added to (not replacing) defaults |
+
+## Encryption Support
+
+Document and thumbnail encryption is no longer supported. This was previously deprecated in [paperless-ng 0.9.3](https://github.com/paperless-ngx/paperless-ngx/blob/dev/docs/changelog.md#paperless-ng-093)
+
+Users must decrypt their document using the `decrypt_documents` command before upgrading.
# this is here so that django finds the checks.
-from documents.checks import changed_password_check
from documents.checks import parser_check
-__all__ = ["changed_password_check", "parser_check"]
+__all__ = ["parser_check"]
"added",
"modified",
"mime_type",
- "storage_type",
"filename",
"checksum",
"archive_filename",
-import textwrap
-
from django.conf import settings
from django.core.checks import Error
from django.core.checks import Warning
from django.core.checks import register
-from django.core.exceptions import FieldError
-from django.db.utils import OperationalError
-from django.db.utils import ProgrammingError
from documents.signals import document_consumer_declaration
from documents.templating.utils import convert_format_str_to_template_format
-@register()
-def changed_password_check(app_configs, **kwargs):
- from documents.models import Document
- from paperless.db import GnuPG
-
- try:
- encrypted_doc = (
- Document.objects.filter(
- storage_type=Document.STORAGE_TYPE_GPG,
- )
- .only("pk", "storage_type")
- .first()
- )
- except (OperationalError, ProgrammingError, FieldError):
- return [] # No documents table yet
-
- if encrypted_doc:
- if not settings.PASSPHRASE:
- return [
- Error(
- "The database contains encrypted documents but no password is set.",
- ),
- ]
-
- if not GnuPG.decrypted(encrypted_doc.source_file):
- return [
- Error(
- textwrap.dedent(
- """
- The current password doesn't match the password of the
- existing documents.
-
- If you intend to change your password, you must first export
- all of the old documents, start fresh with the new password
- and then re-import them."
- """,
- ),
- ),
- ]
-
- return []
-
-
@register()
def parser_check(app_configs, **kwargs):
parsers = []
Cache should be (slightly?) faster than filesystem
"""
try:
- doc = Document.objects.only("storage_type").get(pk=pk)
+ doc = Document.objects.only("pk").get(pk=pk)
if not doc.thumbnail_path.exists():
return None
doc_key = get_thumbnail_modified_key(pk)
create_source_path_directory(document.source_path)
self._write(
- document.storage_type,
self.unmodified_original
if self.unmodified_original is not None
else self.working_copy,
)
self._write(
- document.storage_type,
thumbnail,
document.thumbnail_path,
)
)
create_source_path_directory(document.archive_path)
self._write(
- document.storage_type,
archive_path,
document.archive_path,
)
)
self.log.debug(f"Creation date from st_mtime: {create_date}")
- storage_type = Document.STORAGE_TYPE_UNENCRYPTED
-
if self.metadata.filename:
title = Path(self.metadata.filename).stem
else:
checksum=hashlib.md5(file_for_checksum.read_bytes()).hexdigest(),
created=create_date,
modified=create_date,
- storage_type=storage_type,
page_count=page_count,
original_filename=self.filename,
)
}
CustomFieldInstance.objects.create(**args) # adds to document
- def _write(self, storage_type, source, target):
+ def _write(self, source, target):
with (
Path(source).open("rb") as read_file,
Path(target).open("wb") as write_file,
doc: Document,
*,
counter=0,
- append_gpg=True,
archive_filename=False,
) -> Path:
base_path: Path | None = None
final_filename = f"{doc.pk:07}{counter_str}{filetype_str}"
full_path = Path(final_filename)
- # Add GPG extension if needed
- if append_gpg and doc.storage_type == doc.STORAGE_TYPE_GPG:
- full_path = full_path.with_suffix(full_path.suffix + ".gpg")
-
return full_path
+++ /dev/null
-from pathlib import Path
-
-from django.conf import settings
-from django.core.management.base import BaseCommand
-from django.core.management.base import CommandError
-
-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) -> None:
- parser.add_argument(
- "--passphrase",
- help=(
- "If PAPERLESS_PASSPHRASE isn't set already, you need to specify it here"
- ),
- )
-
- def handle(self, *args, **options) -> None:
- try:
- self.stdout.write(
- self.style.WARNING(
- "\n\n"
- "WARNING: This script is going to work directly on your "
- "document originals, so\n"
- "WARNING: you probably shouldn't run "
- "this unless you've got a recent backup\n"
- "WARNING: handy. It "
- "*should* work without a hitch, but be safe and backup your\n"
- "WARNING: stuff first.\n\n"
- "Hit Ctrl+C to exit now, or Enter to "
- "continue.\n\n",
- ),
- )
- _ = input()
- except KeyboardInterrupt:
- return
-
- 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.",
- )
-
- self.__gpg_to_unencrypted(passphrase)
-
- def __gpg_to_unencrypted(self, passphrase: str) -> None:
- encrypted_files = Document.objects.filter(
- storage_type=Document.STORAGE_TYPE_GPG,
- )
-
- for document in encrypted_files:
- self.stdout.write(f"Decrypting {document}")
-
- old_paths = [document.source_path, document.thumbnail_path]
-
- with document.source_file as file_handle:
- raw_document = GnuPG.decrypted(file_handle, passphrase)
- with document.thumbnail_file as file_handle:
- raw_thumb = GnuPG.decrypted(file_handle, passphrase)
-
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
-
- ext: str = Path(document.filename).suffix
-
- if not ext == ".gpg":
- raise CommandError(
- f"Abort: encrypted file {document.source_path} does not "
- f"end with .gpg",
- )
-
- document.filename = Path(document.filename).stem
-
- with document.source_path.open("wb") as f:
- f.write(raw_document)
-
- with document.thumbnail_path.open("wb") as f:
- f.write(raw_thumb)
-
- Document.objects.filter(id=document.id).update(
- storage_type=document.storage_type,
- filename=document.filename,
- )
-
- for path in old_paths:
- path.unlink()
import os
import shutil
import tempfile
-import time
from pathlib import Path
from typing import TYPE_CHECKING
from documents.settings import EXPORTER_THUMBNAIL_NAME
from documents.utils import copy_file_with_basic_stats
from paperless import version
-from paperless.db import GnuPG
from paperless.models import ApplicationConfiguration
from paperless_mail.models import MailAccount
from paperless_mail.models import MailRule
total=len(document_manifest),
disable=self.no_progress_bar,
):
- # 3.1. store files unencrypted
- document_dict["fields"]["storage_type"] = Document.STORAGE_TYPE_UNENCRYPTED
-
document = document_map[document_dict["pk"]]
- # 3.2. generate a unique filename
+ # 3.1. generate a unique filename
base_name = self.generate_base_name(document)
- # 3.3. write filenames into manifest
+ # 3.2. write filenames into manifest
original_target, thumbnail_target, archive_target = (
self.generate_document_targets(document, base_name, document_dict)
)
- # 3.4. write files to target folder
+ # 3.3. write files to target folder
if not self.data_only:
self.copy_document_files(
document,
base_name = generate_filename(
document,
counter=filename_counter,
- append_gpg=False,
)
else:
base_name = document.get_public_filename(counter=filename_counter)
If the document is encrypted, the files are decrypted before copying them to the target location.
"""
- if document.storage_type == Document.STORAGE_TYPE_GPG:
- t = int(time.mktime(document.created.timetuple()))
-
- original_target.parent.mkdir(parents=True, exist_ok=True)
- with document.source_file as out_file:
- original_target.write_bytes(GnuPG.decrypted(out_file))
- os.utime(original_target, times=(t, t))
-
- if thumbnail_target:
- thumbnail_target.parent.mkdir(parents=True, exist_ok=True)
- with document.thumbnail_file as out_file:
- thumbnail_target.write_bytes(GnuPG.decrypted(out_file))
- os.utime(thumbnail_target, times=(t, t))
-
- if archive_target:
- archive_target.parent.mkdir(parents=True, exist_ok=True)
- if TYPE_CHECKING:
- assert isinstance(document.archive_path, Path)
- with document.archive_path as out_file:
- archive_target.write_bytes(GnuPG.decrypted(out_file))
- os.utime(archive_target, times=(t, t))
- else:
- self.check_and_copy(
- document.source_path,
- document.checksum,
- original_target,
- )
+ self.check_and_copy(
+ document.source_path,
+ document.checksum,
+ original_target,
+ )
- if thumbnail_target:
- self.check_and_copy(document.thumbnail_path, None, thumbnail_target)
+ if thumbnail_target:
+ self.check_and_copy(document.thumbnail_path, None, thumbnail_target)
- if archive_target:
- if TYPE_CHECKING:
- assert isinstance(document.archive_path, Path)
- self.check_and_copy(
- document.archive_path,
- document.archive_checksum,
- archive_target,
- )
+ if archive_target:
+ if TYPE_CHECKING:
+ assert isinstance(document.archive_path, Path)
+ self.check_and_copy(
+ document.archive_path,
+ document.archive_checksum,
+ archive_target,
+ )
def check_and_write_json(
self,
else:
archive_path = None
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
-
with FileLock(settings.MEDIA_LOCK):
if Path(document.source_path).is_file():
raise FileExistsError(document.source_path)
--- /dev/null
+# Generated by Django 5.2.9 on 2026-01-24 23:05
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("documents", "0003_workflowaction_order"),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name="document",
+ name="storage_type",
+ ),
+ ]
class Document(SoftDeleteModel, ModelWithOwner):
- STORAGE_TYPE_UNENCRYPTED = "unencrypted"
- STORAGE_TYPE_GPG = "gpg"
- STORAGE_TYPES = (
- (STORAGE_TYPE_UNENCRYPTED, _("Unencrypted")),
- (STORAGE_TYPE_GPG, _("Encrypted with GNU Privacy Guard")),
- )
-
correspondent = models.ForeignKey(
Correspondent,
blank=True,
db_index=True,
)
- storage_type = models.CharField(
- _("storage type"),
- max_length=11,
- choices=STORAGE_TYPES,
- default=STORAGE_TYPE_UNENCRYPTED,
- editable=False,
- )
-
added = models.DateTimeField(
_("added"),
default=timezone.now,
@property
def source_path(self) -> Path:
- if self.filename:
- fname = str(self.filename)
- else:
- fname = f"{self.pk:07}{self.file_type}"
- if self.storage_type == self.STORAGE_TYPE_GPG:
- fname += ".gpg" # pragma: no cover
+ fname = str(self.filename) if self.filename else f"{self.pk:07}{self.file_type}"
return (settings.ORIGINALS_DIR / Path(fname)).resolve()
@property
def thumbnail_path(self) -> Path:
webp_file_name = f"{self.pk:07}.webp"
- if self.storage_type == self.STORAGE_TYPE_GPG:
- webp_file_name += ".gpg"
webp_file_path = settings.THUMBNAIL_DIR / Path(webp_file_name)
page_count=5,
created=timezone.now(),
modified=timezone.now(),
- storage_type=Document.STORAGE_TYPE_UNENCRYPTED,
added=timezone.now(),
filename="/dummy/filename.pdf",
archive_filename="/dummy/archive_filename.pdf",
-import textwrap
from unittest import mock
from django.core.checks import Error
from django.test import TestCase
from django.test import override_settings
-from documents.checks import changed_password_check
from documents.checks import filename_format_check
from documents.checks import parser_check
-from documents.models import Document
-from documents.tests.factories import DocumentFactory
class TestDocumentChecks(TestCase):
- def test_changed_password_check_empty_db(self):
- self.assertListEqual(changed_password_check(None), [])
-
- def test_changed_password_check_no_encryption(self):
- DocumentFactory.create(storage_type=Document.STORAGE_TYPE_UNENCRYPTED)
- self.assertListEqual(changed_password_check(None), [])
-
- def test_encrypted_missing_passphrase(self):
- DocumentFactory.create(storage_type=Document.STORAGE_TYPE_GPG)
- msgs = changed_password_check(None)
- self.assertEqual(len(msgs), 1)
- msg_text = msgs[0].msg
- self.assertEqual(
- msg_text,
- "The database contains encrypted documents but no password is set.",
- )
-
- @override_settings(
- PASSPHRASE="test",
- )
- @mock.patch("paperless.db.GnuPG.decrypted")
- @mock.patch("documents.models.Document.source_file")
- def test_encrypted_decrypt_fails(self, mock_decrypted, mock_source_file):
- mock_decrypted.return_value = None
- mock_source_file.return_value = b""
-
- DocumentFactory.create(storage_type=Document.STORAGE_TYPE_GPG)
-
- msgs = changed_password_check(None)
-
- self.assertEqual(len(msgs), 1)
- msg_text = msgs[0].msg
- self.assertEqual(
- msg_text,
- textwrap.dedent(
- """
- The current password doesn't match the password of the
- existing documents.
-
- If you intend to change your password, you must first export
- all of the old documents, start fresh with the new password
- and then re-import them."
- """,
- ),
- )
-
def test_parser_check(self):
self.assertEqual(parser_check(None), [])
def test_generate_source_filename(self):
document = Document()
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
self.assertEqual(generate_filename(document), Path(f"{document.pk:07d}.pdf"))
- document.storage_type = Document.STORAGE_TYPE_GPG
- self.assertEqual(
- generate_filename(document),
- Path(f"{document.pk:07d}.pdf.gpg"),
- )
-
@override_settings(FILENAME_FORMAT="{correspondent}/{correspondent}")
def test_file_renaming(self):
document = Document()
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
# Test default source_path
# Ensure that filename is properly generated
self.assertEqual(document.filename, Path("none/none.pdf"))
- # Enable encryption and check again
- document.storage_type = Document.STORAGE_TYPE_GPG
- document.filename = generate_filename(document)
- self.assertEqual(document.filename, Path("none/none.pdf.gpg"))
-
document.save()
# test that creating dirs for the source_path creates the correct directory
settings.ORIGINALS_DIR / "none",
)
self.assertIsFile(
- settings.ORIGINALS_DIR / "test" / "test.pdf.gpg",
+ settings.ORIGINALS_DIR / "test" / "test.pdf",
)
@override_settings(FILENAME_FORMAT="{correspondent}/{correspondent}")
def test_file_renaming_missing_permissions(self):
document = Document()
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+
document.save()
# Ensure that filename is properly generated
def test_file_renaming_database_error(self):
Document.objects.create(
mime_type="application/pdf",
- storage_type=Document.STORAGE_TYPE_UNENCRYPTED,
checksum="AAAAA",
)
document = Document()
document.mime_type = "application/pdf"
document.checksum = "BBBBB"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+
document.save()
# Ensure that filename is properly generated
def test_document_delete(self):
document = Document()
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+
document.save()
# Ensure that filename is properly generated
def test_document_delete_trash_dir(self):
document = Document()
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+
document.save()
# Ensure that filename is properly generated
# Create an identical document and ensure it is trashed under a new name
document = Document()
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+
document.save()
document.filename = generate_filename(document)
document.save()
def test_document_delete_nofile(self):
document = Document()
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+
document.save()
document.delete()
def test_directory_not_empty(self):
document = Document()
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+
document.save()
# Ensure that filename is properly generated
def test_nested_directory_cleanup(self):
document = Document()
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+
document.save()
# Ensure that filename is properly generated
document = Document()
document.pk = 1
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
self.assertEqual(generate_filename(document), Path("0000001.pdf"))
document = Document()
document.pk = 1
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
self.assertEqual(generate_filename(document), Path("0000001.pdf"))
document = Document()
document.pk = 1
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
self.assertEqual(generate_filename(document), Path("0000001.pdf"))
document = Document()
document.pk = 1
document.mime_type = "application/pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
self.assertEqual(generate_filename(document), Path("0000001.pdf"))
title="doc1",
mime_type="application/pdf",
)
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+
document.save()
# Ensure that filename is properly generated
document = DocumentFactory.create(
title="My Document",
mime_type="application/pdf",
- storage_type=Document.STORAGE_TYPE_UNENCRYPTED,
created=self.TEST_DATE, # 2023-10-26 (which is a Thursday)
)
with override_settings(FILENAME_FORMAT=filename_format):
import filecmp
-import hashlib
import shutil
-import tempfile
from io import StringIO
from pathlib import Path
from unittest import mock
self.assertEqual(doc2.archive_filename, "document_01.pdf")
-class TestDecryptDocuments(FileSystemAssertsMixin, TestCase):
- @mock.patch("documents.management.commands.decrypt_documents.input")
- def test_decrypt(self, m):
- media_dir = tempfile.mkdtemp()
- originals_dir = Path(media_dir) / "documents" / "originals"
- thumb_dir = Path(media_dir) / "documents" / "thumbnails"
- originals_dir.mkdir(parents=True, exist_ok=True)
- thumb_dir.mkdir(parents=True, exist_ok=True)
-
- with override_settings(
- ORIGINALS_DIR=originals_dir,
- THUMBNAIL_DIR=thumb_dir,
- PASSPHRASE="test",
- FILENAME_FORMAT=None,
- ):
- doc = Document.objects.create(
- checksum="82186aaa94f0b98697d704b90fd1c072",
- title="wow",
- filename="0000004.pdf.gpg",
- mime_type="application/pdf",
- storage_type=Document.STORAGE_TYPE_GPG,
- )
-
- shutil.copy(
- (
- Path(__file__).parent
- / "samples"
- / "documents"
- / "originals"
- / "0000004.pdf.gpg"
- ),
- originals_dir / "0000004.pdf.gpg",
- )
- shutil.copy(
- (
- Path(__file__).parent
- / "samples"
- / "documents"
- / "thumbnails"
- / "0000004.webp.gpg"
- ),
- thumb_dir / f"{doc.id:07}.webp.gpg",
- )
-
- call_command("decrypt_documents")
-
- doc.refresh_from_db()
-
- self.assertEqual(doc.storage_type, Document.STORAGE_TYPE_UNENCRYPTED)
- self.assertEqual(doc.filename, "0000004.pdf")
- self.assertIsFile(Path(originals_dir) / "0000004.pdf")
- self.assertIsFile(doc.source_path)
- self.assertIsFile(Path(thumb_dir) / f"{doc.id:07}.webp")
- self.assertIsFile(doc.thumbnail_path)
-
- with doc.source_file as f:
- checksum: str = hashlib.md5(f.read()).hexdigest()
- self.assertEqual(checksum, doc.checksum)
-
-
class TestMakeIndex(TestCase):
@mock.patch("documents.management.commands.document_index.index_reindex")
def test_reindex(self, m):
content="Content",
checksum="82186aaa94f0b98697d704b90fd1c072",
title="wow_dec",
- filename="0000004.pdf.gpg",
+ filename="0000004.pdf",
mime_type="application/pdf",
- storage_type=Document.STORAGE_TYPE_GPG,
)
self.note = Note.objects.create(
checksum = hashlib.md5(f.read()).hexdigest()
self.assertEqual(checksum, element["fields"]["checksum"])
- self.assertEqual(
- element["fields"]["storage_type"],
- Document.STORAGE_TYPE_UNENCRYPTED,
- )
-
if document_exporter.EXPORTER_ARCHIVE_NAME in element:
fname = (
self.target / element[document_exporter.EXPORTER_ARCHIVE_NAME]
Document.objects.create(
checksum="AAAAAAAAAAAAAAAAA",
title="wow",
- filename="0000004.pdf",
+ filename="0000010.pdf",
mime_type="application/pdf",
)
self.assertRaises(FileNotFoundError, call_command, "document_exporter", target)
from paperless.celery import app as celery_app
from paperless.config import AIConfig
from paperless.config import GeneralConfig
-from paperless.db import GnuPG
from paperless.models import ApplicationConfiguration
from paperless.serialisers import GroupSerializer
from paperless.serialisers import UserSerializer
doc,
):
return HttpResponseForbidden("Insufficient permissions")
- if doc.storage_type == Document.STORAGE_TYPE_GPG:
- handle = GnuPG.decrypted(doc.thumbnail_file)
- else:
- handle = doc.thumbnail_file
+
+ handle = doc.thumbnail_file
return HttpResponse(handle, content_type="image/webp")
except (FileNotFoundError, Document.DoesNotExist):
if mime_type in {"application/csv", "text/csv"} and disposition == "inline":
mime_type = "text/plain"
- if doc.storage_type == Document.STORAGE_TYPE_GPG:
- file_handle = GnuPG.decrypted(file_handle)
-
response = HttpResponse(file_handle, content_type=mime_type)
# Firefox is not able to handle unicode characters in filename field
# RFC 5987 addresses this issue
+++ /dev/null
-import gnupg
-from django.conf import settings
-
-
-class GnuPG:
- """
- A handy singleton to use when handling encrypted files.
- """
-
- gpg = gnupg.GPG(gnupghome=settings.GNUPG_HOME)
-
- @classmethod
- def decrypted(cls, file_handle, passphrase=None):
- if not passphrase:
- passphrase = settings.PASSPHRASE
-
- return cls.gpg.decrypt_file(file_handle, passphrase=passphrase).data
1, # MailRule.PdfLayout.TEXT_HTML but that can't be imported here
)
-# Pre-2.x versions of Paperless stored your documents locally with GPG
-# encryption, but that is no longer the default. This behaviour is still
-# available, but it must be explicitly enabled by setting
-# `PAPERLESS_PASSPHRASE` in your environment or config file. The default is to
-# store these files unencrypted.
-#
-# Translation:
-# * If you're a new user, you can safely ignore this setting.
-# * If you're upgrading from 1.x, this must be set, OR you can run
-# `./manage.py change_storage_type gpg unencrypted` to decrypt your files,
-# after which you can unset this value.
-PASSPHRASE = os.getenv("PAPERLESS_PASSPHRASE")
-
# Trigger a script after every successful document consumption?
PRE_CONSUME_SCRIPT = os.getenv("PAPERLESS_PRE_CONSUME_SCRIPT")
POST_CONSUME_SCRIPT = os.getenv("PAPERLESS_POST_CONSUME_SCRIPT")