From 16e33ccc345ec8b9d458a2519ebd0d596d4ee1d8 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Tue, 11 Feb 2020 12:18:34 +0100
Subject: [PATCH] forms: force authentication if using another user's tracking
 code (#38079)

---
 tests/test_form_pages.py | 19 +++++++++++++------
 wcs/forms/root.py        |  7 +++++++
 2 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py
index dd7bdb9e..0b024574 100644
--- a/tests/test_form_pages.py
+++ b/tests/test_form_pages.py
@@ -1633,26 +1633,24 @@ def test_form_tracking_code_as_user(pub, nocache):
     resp = resp.follow()
     assert resp.location == 'http://example.net/test/%s/' % formdata_id
     resp = resp.follow()
     assert 'form_comment' in resp.text # makes sure user is treated as submitter
     resp.forms[0]['comment'] = 'hello world'
     resp = resp.forms[0].submit()
     assert formdef.data_class().get(formdata_id).evolution[-1].comment == 'hello world'
 
-    # and check we can also get back to it as anonymous
+    # check we can't get back to it as anonymous
     app = get_app(pub)
     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()
-    assert 'form_comment' in resp.text # makes sure user is treated as submitter
+    assert resp.location == 'http://example.net/login/?ReturnUrl=http://example.net/test/%s' % formdata_id
 
     # and check a bot is not allowed to get it
     app = get_app(pub)
     resp = app.get('/code/%s/load' % tracking_code,
             headers={'User-agent': 'Googlebot'}, status=403)
 
 
 def test_form_empty_tracking_code(pub, nocache):
@@ -6321,28 +6319,37 @@ def test_manager_public_access(pub):
     resp = resp.follow()
     assert 'The form has been recorded' in resp.text
 
     # agent access to an unauthorized formdata
     formdef.workflow_roles = {'_receiver': None}
     formdef.store()
     resp = app.get(formdata.get_url(), status=403)
 
-    # agent access via a tracking code (stays in frontoffice)
+    # agent access via a tracking code (redirected to login and next backoffice)
     formdef.workflow_roles = {'_receiver': role.id}
     formdef.enable_tracking_codes = True
     formdef.store()
 
     code = pub.tracking_code_class()
     code.formdata = formdata
     code.store()
 
     resp = app.get('/code/%s/load' % code.id)
-    resp = resp.follow() # -> /test/1/
+    resp = resp.follow() # -> /login/?ReturnUrl=.../test/1
+    assert '<title>wcs - Login</title>' in resp.text
+    resp.form['username'] = 'admin'
+    resp.form['password'] = 'admin'
+    resp = resp.form.submit()
+    assert resp.status_int == 302
+    resp = resp.follow() # -> /test/1
     assert 'backoffice' not in resp.request.path
+    resp = resp.follow() # -> /test/1/
+    assert 'backoffice' in resp.location
+    resp = resp.follow() # -> /backoffice/management/test/1/
     assert 'The form has been recorded' in resp.text
 
     # authorized access but not backoffice access
     app = login(get_app(pub), username='admin', password='admin')  # reset session
     resp = app.get(formdata.get_url())
     assert resp.location == formdata.get_url(backoffice=True)  # check tracking code is no longer effective
     role.allows_backoffice_access = False
     role.store()
diff --git a/wcs/forms/root.py b/wcs/forms/root.py
index d6dbb8cc..374a3b00 100644
--- a/wcs/forms/root.py
+++ b/wcs/forms/root.py
@@ -169,16 +169,23 @@ class TrackingCodeDirectory(Directory):
                 raise KeyError
             formdata = tracking_code.formdata
         except KeyError:
             raise errors.TraversalError()
         if formdata.formdef.enable_tracking_codes is False:
             raise errors.TraversalError()
         if BotFilter.is_bot():
             raise errors.AccessForbiddenError()
+
+        formdata_url = formdata.get_url().rstrip('/')
+        if formdata.user_id and (not get_request().user
+                or str(get_request().user.id) != str(formdata.user_id)):
+            # asked to load a tracking code associated with another user
+            # don't load, ask for authentication instead
+            return redirect('/login/?ReturnUrl=%s' % formdata_url)
         get_session().mark_anonymous_formdata(formdata)
         return redirect(formdata.get_url())
 
 
 class TrackingCodesDirectory(Directory):
     _q_exports = ['load']
 
     def __init__(self, formdef=None):
-- 
2.25.0

