diff --git a/passerelle/utils/forms.py b/passerelle/utils/forms.py index 8e9a6570..a5b2b4fc 100644 --- a/passerelle/utils/forms.py +++ b/passerelle/utils/forms.py @@ -15,8 +15,34 @@ # along with this program. If not, see . from django import forms -from django.core import validators +from django.core import exceptions, validators +from django.template import Template, TemplateSyntaxError +from django.utils.translation import gettext_lazy as _ class LDAPURLField(forms.URLField): default_validators = [validators.URLValidator(schemes=['ldap', 'ldaps'])] + + +def validate_condition_template(condition_template): + real_template = f'{{% if {condition_template} %}}OK{{% endif %}}' + try: + Template(real_template) + except (TemplateSyntaxError, OverflowError) as e: + raise exceptions.ValidationError(_('syntax error: %s') % e, code='syntax-error') + + +class ConditionField(forms.CharField): + default_validators = [validate_condition_template] + + +def validate_template(template): + try: + Template(template) + except (TemplateSyntaxError, OverflowError) as e: + raise exceptions.ValidationError(_('syntax error: %s') % e, code='syntax-error') + + +class TemplateField(forms.CharField): + default_validators = [validate_template] + widget = forms.Textarea diff --git a/passerelle/utils/templates.py b/passerelle/utils/templates.py index 1deefad9..373fe2cc 100644 --- a/passerelle/utils/templates.py +++ b/passerelle/utils/templates.py @@ -20,7 +20,7 @@ Disable autoescaping. ''' from django.core.exceptions import ValidationError -from django.template import TemplateSyntaxError +from django.template import Context, Template, TemplateSyntaxError from django.template.backends.django import DjangoTemplates from django.utils.translation import gettext as _ @@ -46,3 +46,15 @@ def validate_template(template_string): make_template(template_string) except TemplateSyntaxError as e: raise ValidationError(_('Invalid template: %s') % e) + + +def evaluate_condition(condition_template, context_dict): + template = Template(f'{{% if {condition_template} %}}OK{{% endif %}}') + context = Context(context_dict) + return template.render(context) == 'OK' + + +def evaluate_template(template, context_dict): + template = Template(template) + context = Context(context_dict) + return template.render(context) diff --git a/tests/test_utils_forms.py b/tests/test_utils_forms.py new file mode 100644 index 00000000..4525dbdb --- /dev/null +++ b/tests/test_utils_forms.py @@ -0,0 +1,34 @@ +# passerelle - uniform access to multiple data sources and services +# Copyright (C) 2023 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import pytest +from django.core.exceptions import ValidationError + +from passerelle.utils.forms import ConditionField, TemplateField + + +def test_condition_field(): + field = ConditionField() + with pytest.raises(ValidationError): + field.clean('x ==') + assert field.clean('x == 1') == 'x == 1' + + +def test_template_field(): + field = TemplateField() + with pytest.raises(ValidationError): + field.clean('{% if foo %}bar') + assert field.clean('{% if foo %}bar{% endif %}') == '{% if foo %}bar{% endif %}' diff --git a/tests/test_utils_templates.py b/tests/test_utils_templates.py new file mode 100644 index 00000000..1897f2d1 --- /dev/null +++ b/tests/test_utils_templates.py @@ -0,0 +1,26 @@ +# passerelle - uniform access to multiple data sources and services +# Copyright (C) 2023 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from passerelle.utils.templates import evaluate_condition, evaluate_template + + +def test_evaluate_condition(): + assert evaluate_condition('x == 1', {'x': 1}) is True + assert evaluate_condition('x == 0', {'x': 1}) is False + + +def test_evaluate_template(): + assert evaluate_template('{% if foo %}bar{% endif %}', {'foo': True}) == 'bar'