From a41ab082b8389abdeafc12743585080880e48ebf Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Tue, 26 Nov 2019 11:03:01 +0100
Subject: [PATCH] tracking_code: add tests on formdata access (#38073)

---
 tests/test_tracking_code.py | 287 +++++++++++++++++++++++++++++++++++-
 1 file changed, 285 insertions(+), 2 deletions(-)

diff --git a/tests/test_tracking_code.py b/tests/test_tracking_code.py
index c6fc2829..611cdcf5 100644
--- a/tests/test_tracking_code.py
+++ b/tests/test_tracking_code.py
@@ -1,8 +1,15 @@
 import pytest
+import contextlib
+import re
 
+from wcs import fields
 from wcs.formdef import FormDef
+from wcs.qommon.ident.password_accounts import PasswordAccount
+from wcs.roles import Role
+
+from utilities import get_app, login, create_temporary_pub, clean_temporary_pub
+
 
-from utilities import create_temporary_pub, clean_temporary_pub
 
 def pytest_generate_tests(metafunc):
     if 'pub' in metafunc.fixturenames:
@@ -10,11 +17,73 @@ def pytest_generate_tests(metafunc):
 
 @pytest.fixture
 def pub(request):
-    return create_temporary_pub(sql_mode=(request.param == 'sql'))
+    pub = create_temporary_pub(sql_mode=(request.param == 'sql'))
+    pub.cfg['identification'] = {'methods': ['password']}
+    pub.cfg['language'] = {'language': 'en'}
+    pub.write_cfg()
+    return pub
 
 def teardown_module(module):
     clean_temporary_pub()
 
+def create_formdef():
+    FormDef.wipe()
+    formdef = FormDef()
+    formdef.name = 'test'
+    formdef.fields = []
+    formdef.store()
+    return formdef
+
+def create_users(pub):
+    def create_user(name):
+        user = pub.user_class()
+        user.email = '%s@localhost' % name
+        user.name = name
+        user.store()
+        account = PasswordAccount(id=name)
+        account.set_password(name)
+        account.user_id = user.id
+        account.store()
+        return user
+
+    pub.user_class.wipe()
+    PasswordAccount.wipe()
+    user1 = create_user('user1')
+    user2 = create_user('user2')
+    agent1 = create_user('agent1')
+    agent2 = create_user('agent2')
+    admin1 = create_user('admin1')
+    Role.wipe()
+    role1 = Role(name='Submiters')
+    role1.allows_backoffice_access = True
+    role1.store()
+    role2 = Role(name='Receivers')
+    role2.store()
+    agent1.roles = [role1.id]
+    agent1.store()
+    agent2.roles = [role2.id]
+    agent2.store()
+    admin1.is_admin = True
+    admin1.store()
+    return None, user1, user2, agent1, agent2, admin1  # None for anonymous
+
+def get_displayed_tracking_code(resp):
+    tracking_code = None
+    if 'Forms - test' in resp.text:
+        # frontoffice
+        for a_tag in resp.html.findAll('a'):
+            if 'code/' in a_tag['href']:
+                tracking_code = a_tag.text
+                break
+    elif 'Back Office of wcs - test' in resp.text:
+        # backoffice
+        for h3_tag in resp.html.findAll('h3'):
+            if h3_tag.text == 'Tracking Code':
+                tracking_code = h3_tag.next_sibling.next_element
+                break
+    assert tracking_code
+    return tracking_code
+
 def test_tracking_code(pub):
     klass = pub.tracking_code_class
     klass.wipe()
@@ -74,3 +143,217 @@ def test_tracking_code_duplicate(pub):
 
     assert marker.get('done') # makes sure we got to the real new id code
     assert klass.count() == 2
+
+def test_access_to_formdata(pub, nocache):
+    """
+    1- Direct access to ressources :
+
+    | sumitter / accesser | anonymous | user1 | user2 | agent1 | agent2 | admin1 |
+    +---------------------+-----------+-------+-------+--------+--------+--------+
+    | anonymous           |  login    | deny  | deny  | deny   | (*)    | (*)    |
+    | agent1 (submiter))  |  login    | deny  | deny  | deny   | (*)    | (*)    |
+    | user1               |  login    | allow | deny  | deny   | (*)    | (*)    |
+
+    (*) Agent2 is the receiver.
+        Redirected into backoffice for demands.
+        Access denied for drafts.
+
+    2- Access using tracking code :
+
+    All access is granted,
+    On restoring draft, the logged user become the new draft owner,
+    this affect the computed and prefill fields.
+    """
+    users = create_users(pub)
+    (anonymous, user1, user2, agent1, agent2, admin1) = users
+    tracking_code = None
+    formdata_id = None
+    is_draft = None
+    formdef = create_formdef()
+    formdef.fields = [
+        fields.StringField(id='0', label='submiter', varname='submiter'),
+        fields.CommentField(id='1', type='comment',
+            display_locations=['validation', 'summary'],
+            label='label1: {{form_user_display_name}}'),
+        fields.StringField(id='2', label='string1',
+            prefill={'type': 'string',
+                     'value': 'label2: {{form_user_display_name}}'}),
+        fields.StringField(id='3', label='string2', required=False,
+            prefill={'type': 'string',
+                     'value': 'label3: {{form_user_display_name}}'}),
+        ]
+    formdef.backoffice_submission_roles = agent1.roles[:]
+    formdef.workflow_roles = {'_receiver': agent2.roles[0]}
+    formdef.enable_tracking_codes = True
+    formdef.store()
+
+    @contextlib.contextmanager
+    def submission(user, is_frontoffice=True):
+        pub.session_manager.session_class.wipe()
+        app = get_app(pub)
+
+        if user:
+            app = login(app, username=user.name, password=user.name)
+            user_label = user.name
+        else:
+            user_label = 'anonymous'
+        if is_frontoffice:
+            resp = app.get('/test/')
+            assert '<h3>Tracking code</h3>' in resp.text
+        else:
+            resp = app.get('/backoffice/submission/test/')
+            assert '<h3>Tracking Code</h3>' in resp.text
+
+        formdef.data_class().wipe()
+        resp.form['f0'] = user_label
+        resp.form['f3'] = ''  #  to check new prefilled value on retored draft
+        resp = resp.form.submit('submit')
+
+        tracking_code = get_displayed_tracking_code(resp)
+        assert 'Check values then click submit.' in resp.text
+        if not is_draft:
+            resp = resp.form.submit('submit')
+        assert formdef.data_class().count() == 1
+        formdata = formdef.data_class().select()[0]
+        assert formdata.is_draft() == is_draft
+        assert formdata.tracking_code == tracking_code
+        assert user_label in formdata.data['0']
+        yield (tracking_code, formdata.id)
+
+    def check_direct_access(user, expected=None):
+        """direct access from the URLs"""
+        pub.session_manager.session_class.wipe()
+        app = get_app(pub)
+        if user:
+            app = login(app, username=user.name, password=user.name)
+
+        if is_draft:
+            if expected == 'forbidden':
+                resp = app.get('/test/%s' % formdata_id, status=403)
+                return
+            resp = app.get('/test/%s' % formdata_id)
+            if expected == 'login':
+                assert resp.location.startswith('http://example.net/login/?next=')
+            elif expected == 'frontoffice':
+                assert 'http://example.net/test/?mt=' in resp.location
+                resp = resp.follow()
+                assert '<title>Forms - test</title>' in resp.text
+                assert get_displayed_tracking_code(resp) == tracking_code
+            else:
+                assert expected in ('login', 'forbidden', 'frontoffice')
+        else:
+            resp = app.get('/test/%s' % formdata_id)
+            assert resp.location == 'http://example.net/test/%s/' % formdata_id
+            if expected == 'forbidden':
+                resp = resp.follow(status=403)
+            elif expected == 'login':
+                resp = resp.follow()
+                assert resp.location.startswith('http://example.net/login/?next=')
+            elif expected == 'frontoffice':
+                resp = resp.follow()
+                assert '<title>Forms - test</title>' in resp.text
+                assert get_displayed_tracking_code(resp) == tracking_code
+            elif expected == 'backoffice':
+                resp = resp.follow()
+                assert resp.location == 'http://example.net/backoffice/management/test/%s/' % formdata_id
+                resp = resp.follow()
+                assert ' <title>Back Office of wcs - test - %s</title>' % formdata_id in resp.text
+            else:
+                assert expected in ('login', 'forbidden', 'frontoffice',  'backoffice')
+
+    def check_tracking_code_access(user, owner=None, new_owner=None):
+        """load the formdata using the tracking code"""
+        pub.session_manager.session_class.wipe()
+        app = get_app(pub)
+        if user:
+            app = login(app, username=user.name, password=user.name)
+        resp = app.get('/')
+        resp.forms[0]['code'] = tracking_code
+        resp = resp.forms[0].submit()
+        assert resp.location == 'http://example.net/code/%s/load' % tracking_code
+        resp = resp.follow()
+        assert resp.location == 'http://example.net/test/%s' % formdata_id
+        resp = resp.follow()
+        if is_draft:
+            assert 'http://example.net/test/?mt=' in resp.location
+            resp = resp.follow()
+            assert 'Check values then click submit.' in resp.text
+        else:
+            assert resp.location == 'http://example.net/test/%s/' % formdata_id
+            resp = resp.follow()
+            assert 'The form has been recorded' in resp.text
+        assert '<title>Forms - test</title>' in resp.text
+
+        if is_draft:
+            resp = resp.forms[1].submit('previous')
+            resp = resp.forms[1].submit('submit')
+            assert 'Check values then click submit.' in resp.text
+
+        regex1 = re.search('[>"]label1: ([^<"]*)', resp.text) #  comment
+        regex2 = re.search('[>"]label2: ([^<"]*)', resp.text) #  prefilled
+        regex3 = re.search('[>"]label3: ([^<"]*)', resp.text) #  prefill updated
+
+        formdata = formdef.data_class().select()[0]
+        formdata_user = getattr(formdata.user, 'name', '')
+        expected_owner = getattr(owner, 'name', '')
+        expected_new_owner = getattr(new_owner, 'name', '')
+        if is_draft:
+            if new_owner:
+                assert formdata_user == expected_new_owner
+            else:
+                assert formdata_user == expected_owner
+            assert regex1.group(1) == expected_new_owner
+            assert regex2.group(1) == expected_owner
+            assert regex3.group(1) == expected_new_owner
+        else:
+            assert formdata_user == expected_owner
+            assert regex1.group(1) == expected_owner
+            assert regex2.group(1) == expected_owner
+
+    # direct access to formdata
+    is_draft = False  # demands
+    with submission(anonymous, is_frontoffice=True) as (tracking_code, formdata_id):
+        expected = ('login', 'forbidden', 'forbidden', 'forbidden', 'backoffice', 'backoffice')
+        for i in range(len(users)):
+            check_direct_access(users[i], expected[i])
+    with submission(agent1, is_frontoffice=False) as (tracking_code, formdata_id):
+        expected = ('login', 'forbidden', 'forbidden', 'forbidden', 'backoffice', 'backoffice')
+        for i in range(len(users)):
+            check_direct_access(users[i], expected[i])
+    with submission(user1, is_frontoffice=True) as (tracking_code, formdata_id):
+        expected = ('login', 'frontoffice', 'forbidden', 'forbidden', 'backoffice', 'backoffice')
+        for i in range(len(users)):
+            check_direct_access(users[i], expected[i])
+
+    is_draft = True  # drafts
+    with submission(anonymous, is_frontoffice=True) as (tracking_code, formdata_id):
+        expected = ('login', 'forbidden', 'forbidden', 'forbidden', 'forbidden', 'forbidden')
+        for i in range(len(users)):
+            check_direct_access(users[i], expected[i])
+    with submission(agent1, is_frontoffice=False) as (tracking_code, formdata_id):
+        expected = ('login', 'forbidden', 'forbidden', 'forbidden', 'forbidden', 'forbidden')
+        for i in range(len(users)):
+            check_direct_access(users[i], expected[i])
+    with submission(user1, is_frontoffice=True) as (tracking_code, formdata_id):
+        expected = ('login', 'frontoffice', 'forbidden', 'forbidden', 'forbidden', 'forbidden')
+        for i in range(len(users)):
+            check_direct_access(users[i], expected[i])
+
+    # access to formdata using the tracking code
+    is_draft = False  # demands
+    for user in users:
+        with submission(anonymous, is_frontoffice=True) as (tracking_code, formdata_id):
+            check_tracking_code_access(user, owner=anonymous)
+        with submission(agent1, is_frontoffice=False) as (tracking_code, formdata_id):
+            check_tracking_code_access(user, owner=anonymous)
+        with submission(user1, is_frontoffice=True) as (tracking_code, formdata_id):
+            check_tracking_code_access(user, owner=user1)
+
+    is_draft = True  # drafts
+    for user in users:
+        with submission(anonymous, is_frontoffice=True) as (tracking_code, formdata_id):
+            check_tracking_code_access(user, owner=anonymous, new_owner=user)
+        with submission(agent1, is_frontoffice=False) as (tracking_code, formdata_id):
+            check_tracking_code_access(user, owner=anonymous, new_owner=user)
+        with submission(user1, is_frontoffice=True) as (tracking_code, formdata_id):
+            check_tracking_code_access(user, owner=user1, new_owner=user)
-- 
2.24.0

