Commit c2ebd539 authored by Jérome Perrin's avatar Jérome Perrin

CaptchaField: enable haproxy sticky cookie when viewing a captcha

Currently portal_sessions is only in RAM, so captcha only work when the client
is lucky enough to hit the same zope when submitting the captcha for valiation.

This workaround improves things a little bit because it forces the balancer to
set cookie. Unfortunately, it does not really work because it's too late for
the current request, so basically it will work on the next request.

Unless we can fix portal_sessions, a better workaround is to use an intermediate
script before the page containing the captcha, to set the cookie and redirect
to the page with captcha.
parent e8647603
Pipeline #16286 failed with stage
in 0 seconds
......@@ -1190,6 +1190,30 @@ class TestCaptchaField(ERP5TypeTestCase):
'__captcha_field_test__': hashlib.md5(b'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa').hexdigest()
})
def test_captcha_haproxy_sticky_cookie(self):
# When viewing a captcha, we tell haproxy to stick the client to this
# zope node, as a workaround for portal sessions not being distributed.
skin_folder = self.portal.portal_skins.custom
skin_folder.manage_addProduct['ERP5Form'].addERP5Form(
'Base_viewTestCaptcha',
'View')
form = skin_folder._getOb('Base_viewTestCaptcha', None)
form.manage_addField('your_captcha', 'Captcha Field', 'CaptchaField')
form.your_captcha.values['captcha_type'] = 'text'
form.your_captcha.values['captcha_dot_net_client'] = 'demo'
form.your_captcha.values['captcha_dot_net_secret'] = 'secret'
form.your_captcha.values['captcha_dot_net_use_ssl'] = True
self.commit()
resp = self.publish(
form.getPath(),
env={
# haproxy set X-Balancer-Current-Cookie header which becomes
# HTTP_X_BALANCER_CURRENT_COOKIE in environment
'HTTP_X_BALANCER_CURRENT_COOKIE': 'SERVERID'
})
self.assertIn('SERVERID', resp.cookies)
def makeDummyOid():
import time, random
......
......@@ -193,13 +193,13 @@ class CaptchaWidget(Widget.TextWidget):
"""
Render editor
"""
portal = field.getPortalObject()
captcha_key = None
captcha_field = None
captcha_type = field.get_value("captcha_type")
provider = CaptchaProviderFactory.getProvider(captcha_type)
(captcha_key, captcha_answer) = provider.generate(field)
portal_sessions = field.getPortalObject().portal_sessions
while not self.add_captcha(portal_sessions, md5(captcha_key).hexdigest(), captcha_answer):
while not self.add_captcha(portal.portal_sessions, md5(captcha_key).hexdigest(), captcha_answer):
(captcha_key, captcha_answer) = provider.generate(field)
captcha_field = provider.getHTML(field, captcha_key)
......@@ -215,6 +215,17 @@ class CaptchaWidget(Widget.TextWidget):
size=10)
# HTML page having a captcha field should never be cached.
REQUEST.RESPONSE.setHeader('Cache-Control', 'max-age=0, no-store')
# set haproxy balancer cookie, as a temporary workaround because portal_sessions
# does not work with memcached yet. See CookieCrumbler for details about
# haproxy sticky cookie mechanism
balancer_cookie = REQUEST.get('HTTP_X_BALANCER_CURRENT_COOKIE')
if balancer_cookie:
if balancer_cookie not in REQUEST.cookies:
REQUEST.RESPONSE.setCookie(
balancer_cookie,
'anything',
path=portal.absolute_url_path())
return captcha_field + key_field + splitter + answer
def render_view(self, field, value, REQUEST=None, render_prefix=None):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment