-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
188 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
document_merge_service/api/management/commands/dms_encrypt_templates.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
from django.conf import settings | ||
from django.core.exceptions import ImproperlyConfigured | ||
from django.core.files.storage import storages | ||
from django.core.management.base import BaseCommand | ||
from tqdm import tqdm | ||
|
||
from document_merge_service.api.models import Template | ||
|
||
|
||
class Command(BaseCommand): | ||
help = "Swaps plain text template content to encrypted content" | ||
|
||
def handle(self, *args, **options): | ||
if not settings.DMS_ENABLE_AT_REST_ENCRYPTION: | ||
return self.stdout.write( | ||
self.style.WARNING( | ||
"Encryption is not enabled. Skipping encryption of templates." | ||
) | ||
) | ||
|
||
failed_templates = [] | ||
|
||
# flip between default and encrypted storage to have the correct parameters in the requests | ||
encrypted_storage = storages.create_storage(settings.STORAGES["default"]) | ||
unencrypted_storage_setting = settings.STORAGES["default"] | ||
if ( | ||
"OPTIONS" not in unencrypted_storage_setting | ||
or "object_parameters" not in unencrypted_storage_setting["OPTIONS"] | ||
): | ||
raise ImproperlyConfigured( | ||
"Encryption is enabled but no object_parameters found in the storage settings." | ||
) | ||
del unencrypted_storage_setting["OPTIONS"]["object_parameters"] | ||
unencrypted_storage = storages.create_storage(unencrypted_storage_setting) | ||
|
||
query = Template.objects.all() | ||
for template in tqdm(query.iterator(50), total=query.count()): | ||
# get original template content | ||
template.template.storage = unencrypted_storage | ||
try: | ||
content = template.template.open() | ||
|
||
# overwrite with encrypted content | ||
template.template.storage = encrypted_storage | ||
template.template.save(template.template.name, content) | ||
except Exception as e: | ||
self.stdout.write( | ||
self.style.WARNING(f"Error for template {str(template.pk)}: {e}") | ||
) | ||
failed_templates.append(str(template.pk)) | ||
continue | ||
|
||
if failed_templates: | ||
self.stdout.write( | ||
self.style.WARNING(f"These templates failed:\n{failed_templates}") | ||
) | ||
self.stdout.write(self.style.SUCCESS("Encryption finished")) |
92 changes: 92 additions & 0 deletions
92
document_merge_service/api/tests/test_encrypt_templates.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
from copy import deepcopy | ||
from io import StringIO | ||
|
||
import pytest | ||
from django.core.exceptions import ImproperlyConfigured | ||
from django.core.files import File as DjangoFile | ||
from django.core.management import call_command | ||
from storages.backends.s3 import S3Storage | ||
|
||
from document_merge_service.api.data import django_file | ||
|
||
|
||
@pytest.fixture | ||
def settings_storage(settings): | ||
settings.STORAGES = deepcopy(settings.STORAGES) | ||
return settings.STORAGES | ||
|
||
|
||
def test_encrypt_templates(db, settings, settings_storage, mocker, template_factory): | ||
template_factory(template=django_file("docx-template.docx")) | ||
|
||
settings.DMS_ENABLE_AT_REST_ENCRYPTION = True | ||
settings_storage["default"] = { | ||
"BACKEND": "storages.backends.s3.S3Storage", | ||
"OPTIONS": { | ||
**settings.S3_STORAGE_OPTIONS, | ||
"object_parameters": { | ||
"SSECustomerKey": "x" * 32, | ||
"SSECustomerAlgorithm": "AES256", | ||
}, | ||
}, | ||
} | ||
|
||
mocker.patch("storages.backends.s3.S3Storage.open") | ||
mocker.patch("storages.backends.s3.S3Storage.save") | ||
S3Storage.save.return_value = "name-of-the-file" | ||
S3Storage.open.return_value = DjangoFile(open("README.md", "rb")) | ||
|
||
call_command("dms_encrypt_templates") | ||
|
||
assert S3Storage.open.call_count == 1 | ||
assert S3Storage.save.call_count == 1 | ||
|
||
|
||
def test_encrypt_templates_disabled(db, template_factory): | ||
template_factory(template=django_file("docx-template.docx")) | ||
|
||
out = StringIO() | ||
call_command("dms_encrypt_templates", stdout=out) | ||
|
||
assert ( | ||
"Encryption is not enabled. Skipping encryption of templates." in out.getvalue() | ||
) | ||
|
||
|
||
def test_encrypt_template_improperyconfigured(db, settings, template_factory): | ||
template_factory(template=django_file("docx-template.docx")) | ||
settings.DMS_ENABLE_AT_REST_ENCRYPTION = True | ||
|
||
out = StringIO() | ||
with pytest.raises(ImproperlyConfigured): | ||
call_command("dms_encrypt_templates", stdout=out) | ||
|
||
|
||
def test_encrypt_templates_failed( | ||
db, settings, settings_storage, mocker, template_factory | ||
): | ||
template_factory(template=django_file("docx-template.docx")) | ||
|
||
settings.DMS_ENABLE_AT_REST_ENCRYPTION = True | ||
settings_storage["default"] = { | ||
"BACKEND": "storages.backends.s3.S3Storage", | ||
"OPTIONS": { | ||
**settings.S3_STORAGE_OPTIONS, | ||
"object_parameters": { | ||
"SSECustomerKey": "x" * 32, | ||
"SSECustomerAlgorithm": "AES256", | ||
}, | ||
}, | ||
} | ||
|
||
mocker.patch("storages.backends.s3.S3Storage.open", side_effect=FileNotFoundError) | ||
mocker.patch("storages.backends.s3.S3Storage.save") | ||
S3Storage.save.return_value = "name-of-the-file" | ||
S3Storage.open.return_value = DjangoFile(open("README.md", "rb")) | ||
|
||
out = StringIO() | ||
call_command("dms_encrypt_templates", stdout=out) | ||
|
||
assert S3Storage.open.call_count == 1 | ||
assert S3Storage.save.call_count == 0 | ||
assert "failed" in out.getvalue() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters