From cda01fcbf8f1c8c71e035db1e3485c6ca5df8e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Tue, 26 Jun 2018 16:29:04 +0200 Subject: [PATCH] misc: expose session identifier in substitution variables (#24778) --- tests/test_form_pages.py | 2 -- tests/test_sessions.py | 40 ++++++++++++++++++++++++++++++++++++++++ wcs/forms/root.py | 5 +++++ wcs/qommon/sessions.py | 10 ++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py index eeb93707..a16346d8 100644 --- a/tests/test_form_pages.py +++ b/tests/test_form_pages.py @@ -4239,14 +4239,12 @@ def test_session_cookie_flags(pub): formdef = create_formdef() app = get_app(pub) resp = app.get('/test/', status=200) - resp = resp.form.submit('submit') assert resp.headers['Set-Cookie'].startswith('wcs-') assert 'httponly' in resp.headers['Set-Cookie'] assert not 'secure' in resp.headers['Set-Cookie'] app = get_app(pub, https=True) resp = app.get('/test/', status=200) - resp = resp.form.submit('submit') assert resp.headers['Set-Cookie'].startswith('wcs-') assert 'httponly' in resp.headers['Set-Cookie'] assert 'secure' in resp.headers['Set-Cookie'] diff --git a/tests/test_sessions.py b/tests/test_sessions.py index cd8dccf1..790dd9eb 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -7,6 +7,8 @@ from quixote import cleanup from wcs.qommon.ident.password_accounts import PasswordAccount from wcs.qommon.http_request import HTTPRequest +from wcs.formdef import FormDef +from wcs import fields from utilities import create_temporary_pub, clean_temporary_pub, get_app, login @@ -159,3 +161,41 @@ def test_session_do_not_reuse_id(pub, user, app): resp = login_form.submit() assert resp.status_int == 302 assert pub.session_manager.session_class.count() == 2 + +def test_session_substitution_variables(pub, user, app): + pub.session_manager.session_class.wipe() + resp = app.get('/') + + formdef = FormDef() + formdef.name = 'foobar' + formdef.fields = [fields.CommentField(id='7', label='Hello [session_var_id]', type='comment')] + formdef.store() + + resp = app.get('/foobar/') + assert pub.session_manager.session_class.count() == 1 + session_id = pub.session_manager.session_class.select()[0].id + assert 'Hello %s' % session_id in resp.body + + login(app, username='foo', password='foo') + assert pub.session_manager.session_class.count() == 2 + session_id = [x for x in pub.session_manager.session_class.select() if x.id != session_id][0].id + resp = app.get('/foobar/') + assert 'Hello %s' % session_id in resp.body + +def test_session_substitution_variables_1st_page_condition(pub, user, app): + pub.session_manager.session_class.wipe() + resp = app.get('/') + + formdef = FormDef() + formdef.name = 'foobar' + formdef.fields = [fields.PageField(id='0', label='1st PAGE', type='page', + condition={'type': 'python', 'value': 'vars().get("session_var_id") is not None'}), + fields.CommentField(id='7', label='COM1 [session_var_id]', type='comment'), + fields.PageField(id='8', label='2nd PAGE', type='page'), + fields.CommentField(id='9', label='COM2 [session_var_id]', type='comment')] + formdef.store() + + resp = app.get('/foobar/') + assert pub.session_manager.session_class.count() == 1 + session_id = pub.session_manager.session_class.select()[0].id + assert 'COM1' in resp.body diff --git a/wcs/forms/root.py b/wcs/forms/root.py index 1d5ef693..198b11a7 100644 --- a/wcs/forms/root.py +++ b/wcs/forms/root.py @@ -516,6 +516,11 @@ class FormPage(Directory, FormTemplateMixin): return redirect(self.check_disabled()) session = get_session() + if not session.id: + # force session to be written down, this is required so + # [session_var_id] is available on the first page. + session.force() + if self.formdef.enable_tracking_codes: if get_request().form.get('_ajax_form_token'): # _ajax_form_token is immediately removed, this prevents diff --git a/wcs/qommon/sessions.py b/wcs/qommon/sessions.py index b37d838f..987268ee 100644 --- a/wcs/qommon/sessions.py +++ b/wcs/qommon/sessions.py @@ -89,11 +89,19 @@ class Session(QommonSession, CaptchaSession, StorableObject): jsonp_display_values = None extra_variables = None expire = None + forced = False # should only be overwritten by authentication methods extra_user_variables = None username = None # only set on password authentication + def force(self): + # add some data in the session, it will force a cookie to be set, this + # is used so we get a session identifier fixed even on the first page + # of a form. + self.forced = True + get_session_manager().maintain_session(self) + def set_expire(self, expire): self.expire = expire @@ -126,6 +134,7 @@ class Session(QommonSession, CaptchaSession, StorableObject): CaptchaSession.has_info(self) or \ self.expire or \ self.extra_user_variables or \ + self.forced or \ QuixoteSession.has_info(self) is_dirty = has_info @@ -315,6 +324,7 @@ class Session(QommonSession, CaptchaSession, StorableObject): def get_substitution_variables(self, prefix='session_var_'): d = {} + d[prefix + 'id'] = self.id if self.extra_variables: for k, v in self.extra_variables.items(): d[prefix + k] = v -- 2.17.1