From 90771c805c81966c70efbf658eae1fed9df245c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Fri, 29 Apr 2016 11:04:47 +0200 Subject: [PATCH] api: allow wscall output as formdata/submit input (#10661) --- tests/test_api.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- wcs/api.py | 28 ++++++++++++++++++--- wcs/formdata.py | 4 +++ 3 files changed, 100 insertions(+), 7 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index 0e2d5e6..9db2c0d 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,4 +1,5 @@ import pytest +import json import shutil import os import hmac @@ -19,7 +20,7 @@ from wcs.categories import Category from wcs.data_sources import NamedDataSource from wcs.workflows import Workflow from wcs.wf.jump import JumpWorkflowStatusItem -from wcs import fields +from wcs import fields, qommon from wcs.api_utils import sign_url from utilities import get_app, create_temporary_pub, clean_temporary_pub @@ -363,7 +364,7 @@ def test_formdef_submit(pub, local_user): resp = get_app(pub).post_json('/api/formdefs/test/submit', {'data': {}}, status=403) assert resp.json['err'] == 1 - assert resp.json['err_desc'] == 'no user' + assert resp.json['err_desc'] == 'unsigned API call' signed_url = sign_url('http://example.net/api/formdefs/test/submit' + '?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), '1234') @@ -448,7 +449,7 @@ def test_formdef_submit_with_varname(pub, local_user): resp = get_app(pub).post_json('/api/formdefs/test/submit', {'data': {}}, status=403) assert resp.json['err'] == 1 - assert resp.json['err_desc'] == 'no user' + assert resp.json['err_desc'] == 'unsigned API call' signed_url = sign_url('http://example.net/api/formdefs/test/submit' + '?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), '1234') @@ -503,6 +504,74 @@ def test_formdef_submit_with_varname(pub, local_user): data_class.wipe() + +def test_formdef_submit_from_wscall(pub, local_user): + test_formdef_submit_with_varname(pub, local_user) + formdef = FormDef.select()[0] + + formdata = formdef.data_class()() + formdata.just_created() + + upload = PicklableUpload('test.txt', 'text/plain', 'ascii') + upload.receive(['test']) + + formdata.data = { + '0': 'xxx', + '1': '1', + '1_display': '1', + '1_structured': { + 'id': '1', + 'text': 'foo', + 'more': 'XXX', + }, + '2': '10', + '2_display': 'bar', + '3': time.strptime('1970-01-01', '%Y-%m-%d'), + '4': upload, + '5': '1.5;2.25', + } + formdata.just_created() + formdata.evolution[-1].status = 'wf-new' + formdata.store() + + payload = json.loads( + json.dumps(formdata.get_json_export_dict(), + cls=qommon.misc.JSONEncoder, + encoding=get_publisher().site_charset)) + signed_url = sign_url('http://example.net/api/formdefs/test/submit?orig=coucou', '1234') + url = signed_url[len('http://example.net'):] + + resp = get_app(pub).post_json(url, payload) + assert resp.json['err'] == 0 + new_formdata = formdef.data_class().get(resp.json['data']['id']) + assert new_formdata.data['0'] == formdata.data['0'] + assert new_formdata.data['1'] == formdata.data['1'] + assert new_formdata.data['1_display'] == formdata.data['1_display'] + assert new_formdata.data['1_structured'] == formdata.data['1_structured'] + assert new_formdata.data['2'] == formdata.data['2'] + assert new_formdata.data['2_display'] == formdata.data['2_display'] + assert new_formdata.data['3'] == formdata.data['3'] + assert new_formdata.data['4'].get_content() == formdata.data['4'].get_content() + assert new_formdata.data['5'] == formdata.data['5'] + assert new_formdata.user_id is None + + # add user + formdata.user_id = local_user.id + formdata.store() + + payload = json.loads( + json.dumps(formdata.get_json_export_dict(), + cls=qommon.misc.JSONEncoder, + encoding=get_publisher().site_charset)) + signed_url = sign_url('http://example.net/api/formdefs/test/submit?orig=coucou', '1234') + url = signed_url[len('http://example.net'):] + + resp = get_app(pub).post_json(url, payload) + assert resp.json['err'] == 0 + new_formdata = formdef.data_class().get(resp.json['data']['id']) + assert str(new_formdata.user_id) == str(local_user.id) + + def test_categories(pub): FormDef.wipe() Category.wipe() diff --git a/wcs/api.py b/wcs/api.py index 18ab6c2..dd45e84 100644 --- a/wcs/api.py +++ b/wcs/api.py @@ -113,13 +113,19 @@ class ApiFormdefDirectory(Directory): # } # } get_response().set_content_type('application/json') - user = get_user_from_api_query_string() - if not user: - raise AccessForbiddenError('no user') if self.formdef.is_disabled(): raise AccessForbiddenError('disabled form') + if not is_url_signed(): + raise AccessForbiddenError('unsigned API call') + user = get_user_from_api_query_string() json_input = get_request().json formdata = self.formdef.data_class()() + + if 'fields' in json_input and not 'data' in json_input: + # this maps json output produced by wf/wscall.py to the json + # formated expected as input. + json_input['data'] = json_input['fields'] + data = json_input['data'] # remap fields for field in self.formdef.fields: @@ -146,12 +152,26 @@ class ApiFormdefDirectory(Directory): formdata.data = data meta = json_input.get('meta') or {} if meta.get('backoffice-submission'): + if not user: + raise AccessForbiddenError('no user set for backoffice submission') if not self.formdef.backoffice_submission_roles: raise AccessForbiddenError('no backoffice submission roles') if not set(user.roles or []).intersection(self.formdef.backoffice_submission_roles): raise AccessForbiddenError('not cleared for backoffice submit') formdata.backoffice_submission = True - else: + elif 'user' in json_input: + formdata_user = None + for name_id in json_input['user'].get('NameID'): + formdata_user = get_publisher().user_class.get_users_with_name_identifier(name_id) + if formdata_user: + break + else: + if json_input['user'].get('email'): + formdata_user = get_publisher().user_class.get_users_with_email( + json_input['user'].get('email')) + if formdata_user: + formdata.user_id = formdata_user[0].id + elif user: formdata.user_id = user.id if json_input.get('context'): formdata.submission_context = json_input['context'] diff --git a/wcs/formdata.py b/wcs/formdata.py index a0a8fd9..b78143e 100644 --- a/wcs/formdata.py +++ b/wcs/formdata.py @@ -700,6 +700,10 @@ class FormData(StorableObject): break if user: data['user'] = {'id': user.id, 'name': user.display_name} + if user.email: + data['user']['email'] = user.email + if user.name_identifiers: + data['user']['NameID'] = user.name_identifiers data['fields'] = get_json_dict(self.formdef.fields, self.data, include_files=include_files) -- 2.8.1