diff --git a/HISTORY.rst b/HISTORY.rst index e324c7c9f0..71b8464bb9 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,6 +4,9 @@ (@fsheedy) for the report. * Add PostgreSQL troubleshooting entry. Closes GitLab issues #523 and #602 +* Use YAML SafeDumper to avoid adding YAML datatype tags. + Closes GitLab issue #599. Thanks to Frédéric Sheedy + (@fsheedy) for the report and debug information. 3.2 (2019-06-13) ================ diff --git a/docs/releases/3.2.1.rst b/docs/releases/3.2.1.rst index 7c4c790c0e..b598fbd408 100644 --- a/docs/releases/3.2.1.rst +++ b/docs/releases/3.2.1.rst @@ -8,7 +8,13 @@ Released: June 14, 2019 Changes ------- -- Fix sub cabinet creation view. +- Fix sub cabinet creation view. Thanks to Frédéric Sheedy + (@fsheedy) for the report. +- Add PostgreSQL troubleshooting entry. Closes GitLab + issues #523 and #602 +- Use YAML SafeDumper to avoid adding YAML datatype tags. + Closes GitLab issue #599. Thanks to Frédéric Sheedy + (@fsheedy) for the report and debug information. Removals @@ -99,6 +105,7 @@ Bugs fixed or issues closed --------------------------- - :gitlab-issue:`523` PostgreSQL error about insufficient connections +- :gitlab-issue:`599` Settings display !!python/unicode with values since 3.2 - :gitlab-issue:`601` Error when creating new cabinet level - :gitlab-issue:`602` System stops responding for a minute every 10 minutes or so diff --git a/mayan/apps/converter/managers.py b/mayan/apps/converter/managers.py index 392c80c7aa..5856231203 100644 --- a/mayan/apps/converter/managers.py +++ b/mayan/apps/converter/managers.py @@ -5,9 +5,9 @@ import logging import yaml try: - from yaml import CSafeLoader as SafeLoader, CDumper as Dumper + from yaml import CSafeLoader as SafeLoader, CSafeDumper as SafeDumper except ImportError: - from yaml import SafeLoader, Dumper + from yaml import SafeLoader, SafeDumper from django.contrib.contenttypes.models import ContentType from django.db import models, transaction @@ -24,7 +24,7 @@ class TransformationManager(models.Manager): self.create( content_type=content_type, object_id=obj.pk, name=transformation.name, arguments=yaml.dump( - data=arguments, Dumper=Dumper + data=arguments, Dumper=SafeDumper ) ) diff --git a/mayan/apps/smart_settings/classes.py b/mayan/apps/smart_settings/classes.py index 96c559d6b5..d5cc477095 100644 --- a/mayan/apps/smart_settings/classes.py +++ b/mayan/apps/smart_settings/classes.py @@ -9,9 +9,9 @@ import sys import yaml try: - from yaml import CSafeLoader as SafeLoader, CDumper as Dumper + from yaml import CSafeLoader as SafeLoader, CSafeDumper as SafeDumper except ImportError: - from yaml import SafeLoader, Dumper + from yaml import SafeLoader, SafeDumper from django.apps import apps from django.conf import settings @@ -84,11 +84,23 @@ class Setting(object): return yaml.load(stream=value, Loader=SafeLoader) @staticmethod - def serialize_value(value): - if isinstance(value, Promise): - value = force_text(value) + def express_promises(value): + """ + Walk all the elements of a value and force promises to text + """ + if isinstance(value, (list, tuple)): + return [Setting.express_promises(item) for item in value] + elif isinstance(value, Promise): + return force_text(value) + else: + return value - result = yaml.dump(data=value, allow_unicode=True, Dumper=Dumper) + @staticmethod + def serialize_value(value): + result = yaml.dump( + data=Setting.express_promises(value), allow_unicode=True, + Dumper=SafeDumper + ) # safe_dump returns bytestrings # Disregard the last 3 dots that mark the end of the YAML document if force_text(result).endswith('...\n'): @@ -103,13 +115,10 @@ class Setting(object): for setting in cls.get_all(): if (namespace and setting.namespace.name == namespace) or not namespace: if (filter_term and filter_term.lower() in setting.global_name.lower()) or not filter_term: - if isinstance(setting.value, Promise): - dictionary[setting.global_name] = force_text(setting.value) - else: - dictionary[setting.global_name] = setting.value + dictionary[setting.global_name] = Setting.express_promises(setting.value) return yaml.dump( - data=dictionary, default_flow_style=False, Dumper=Dumper + data=dictionary, default_flow_style=False, Dumper=SafeDumper ) @classmethod diff --git a/mayan/apps/smart_settings/tests/test_classes.py b/mayan/apps/smart_settings/tests/test_classes.py index 517fccacdc..b82798a7be 100644 --- a/mayan/apps/smart_settings/tests/test_classes.py +++ b/mayan/apps/smart_settings/tests/test_classes.py @@ -2,9 +2,15 @@ from __future__ import absolute_import, unicode_literals import os +from pathlib2 import Path + +from django.conf import settings + from mayan.apps.common.settings import setting_paginate_by from mayan.apps.common.tests import BaseTestCase +from ..classes import Setting + from .literals import ENVIRONMENT_TEST_NAME, ENVIRONMENT_TEST_VALUE @@ -14,3 +20,20 @@ class ClassesTestCase(BaseTestCase): 'MAYAN_{}'.format(ENVIRONMENT_TEST_NAME) ] = ENVIRONMENT_TEST_VALUE self.assertTrue(setting_paginate_by.value, ENVIRONMENT_TEST_VALUE) + + def test_config_backup_creation(self): + path_config_backup = Path(settings.CONFIGURATION_LAST_GOOD_FILEPATH) + path_config_backup.unlink() + + Setting.save_last_known_good() + self.assertTrue(path_config_backup.exists()) + + def test_config_backup_creation_no_tags(self): + path_config_backup = Path(settings.CONFIGURATION_LAST_GOOD_FILEPATH) + path_config_backup.unlink() + + Setting.save_last_known_good() + self.assertTrue(path_config_backup.exists()) + + with path_config_backup.open(mode='r') as file_object: + self.assertFalse('!!python/' in file_object.read())