From 1f5823adc7e1c0d05e91e6c151cd5d9155c6751d Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Thu, 12 Dec 2024 15:17:46 +0000 Subject: [PATCH] Prevent importing of flows with a spec version that is ahead of the engine --- media/test_flows/too_old.json | 58 ----------------------------------- temba/orgs/tests.py | 22 ++++++++----- temba/orgs/views/views.py | 5 +++ 3 files changed, 20 insertions(+), 65 deletions(-) delete mode 100644 media/test_flows/too_old.json diff --git a/media/test_flows/too_old.json b/media/test_flows/too_old.json deleted file mode 100644 index ce8daaadbbf..00000000000 --- a/media/test_flows/too_old.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "campaigns": [], - "version": 2, - "site": "http://rapidpro.io", - "flows": [ - { - "definition": { - "rule_sets": [], - "entr": "d2ed3c4c-c4d0-4f54-b0b0-6eaa5afd33f8", - "action_sets": [ - { - "y": 0, - "x": 100, - "destination": null, - "uuid": "d2ed3c4c-c4d0-4f54-b0b0-6eaa5afd33f8", - "actions": [ - { - "msg": "You've been registered as a new mother, congratulations on having sex at least once. Your CHW is @extra.contact.phone", - "type": "reply" - }, - { - "field": "chw", - "type": "save", - "value": "@extra.contact.tel_e164", - "label": "CHW" - }, - { - "field": "expected_delivery_date", - "type": "save", - "value": "@extra.flow.edd", - "label": "Expected Delivery Date" - }, - { - "field": "name", - "type": "save", - "value": "@extra.flow.name", - "label": "Contact Name" - }, - { - "type": "add_group", - "groups": [ - "Expecting Mothers" - ] - } - ] - } - ], - "metadata": { - "notes": [] - } - }, - "flow_type": "F", - "name": "New Mother", - "id": 1500 - } - ], - "triggers": [] -} diff --git a/temba/orgs/tests.py b/temba/orgs/tests.py index b880896cb7c..acb8efa86aa 100644 --- a/temba/orgs/tests.py +++ b/temba/orgs/tests.py @@ -1227,19 +1227,27 @@ def test_import_voice_flows_expiration_time(self): self.assertEqual(voice_flow.expires_after_minutes, 5) def test_import(self): - self.login(self.admin) + create_url = reverse("orgs.orgimport_create") - OrgImport.objects.all().delete() + self.login(self.admin) - post_data = dict(file=open("%s/test_flows/too_old.json" % settings.MEDIA_ROOT, "rb")) - response = self.client.post(reverse("orgs.orgimport_create"), post_data) + # try to import a file with a version that's too old + response = self.client.post(create_url, {"file": io.BytesIO(b'{"version":"2","flows":[]}')}) self.assertFormError( response.context["form"], "file", "This file is no longer valid. Please export a new version and try again." ) + # try to import a file with a flow with a version that's too new + response = self.client.post( + create_url, {"file": io.BytesIO(b'{"version":"13","flows":[{"spec_version": "1324.3.0"}]}')} + ) + self.assertFormError( + response.context["form"], "file", "This file contains flows with a version that is too new." + ) + # try a file which can be migrated forwards response = self.client.post( - reverse("orgs.orgimport_create"), + create_url, {"file": open("%s/test_flows/favorites_v4.json" % settings.MEDIA_ROOT, "rb")}, ) self.assertEqual(302, response.status_code) @@ -1260,12 +1268,12 @@ def test_import(self): # test import using data that is not parsable junk_binary_data = io.BytesIO(b"\x00!\x00b\xee\x9dh^\x01\x00\x00\x04\x00\x02[Content_Types].xml \xa2\x04\x02(") post_data = dict(file=junk_binary_data) - response = self.client.post(reverse("orgs.orgimport_create"), post_data) + response = self.client.post(create_url, post_data) self.assertFormError(response.context["form"], "file", "This file is not a valid flow definition file.") junk_json_data = io.BytesIO(b'{"key": "data') post_data = dict(file=junk_json_data) - response = self.client.post(reverse("orgs.orgimport_create"), post_data) + response = self.client.post(create_url, post_data) self.assertFormError(response.context["form"], "file", "This file is not a valid flow definition file.") def test_import_errors(self): diff --git a/temba/orgs/views/views.py b/temba/orgs/views/views.py index fd9534ae907..9ea6dbc6429 100644 --- a/temba/orgs/views/views.py +++ b/temba/orgs/views/views.py @@ -2340,6 +2340,11 @@ def clean_file(self): if Version(str(json_data.get("version", 0))) < Version(Org.EARLIEST_IMPORT_VERSION): raise ValidationError(_("This file is no longer valid. Please export a new version and try again.")) + for flow in json_data.get("flows", []): + spec = flow.get("spec_version") + if spec and Version(spec) > Version(Flow.CURRENT_SPEC_VERSION): + raise ValidationError(_("This file contains flows with a version that is too new.")) + return self.cleaned_data["file"] class Meta: