[tor-commits] [bridgedb/master] Add Mako templating and move CAPTCHA into Captcha Resource
aagbsn at torproject.org
aagbsn at torproject.org
Wed Apr 17 00:26:44 UTC 2013
commit 744b5178f67a578676197014fae04bd1641e71f3
Author: aagbsn <aagbsn at extc.org>
Date: Sun Mar 24 23:43:33 2013 +0000
Add Mako templating and move CAPTCHA into Captcha Resource
---
lib/bridgedb/HTTPServer.py | 239 +++++++++++++++++---------------------------
1 files changed, 92 insertions(+), 147 deletions(-)
diff --git a/lib/bridgedb/HTTPServer.py b/lib/bridgedb/HTTPServer.py
index b2b976c..2887ed4 100644
--- a/lib/bridgedb/HTTPServer.py
+++ b/lib/bridgedb/HTTPServer.py
@@ -12,10 +12,13 @@ import logging
import re
import textwrap
import time
+import os
from twisted.internet import reactor
import twisted.web.resource
-import twisted.web.server
+from twisted.web.server import Site, Session
+from twisted.web import static
+from twisted.web.util import redirectTo
import bridgedb.Dist
import bridgedb.I18n as I18n
@@ -27,6 +30,23 @@ from bridgedb.Filters import filterBridgesByTransport
from bridgedb.Filters import filterBridgesByNotBlockedIn
from ipaddr import IPv4Address, IPv6Address
from random import randint
+from mako.template import Template
+from mako.lookup import TemplateLookup
+from zope.interface import Interface, Attribute, implements
+from twisted.python.components import registerAdapter
+
+template_root = os.path.join(os.path.dirname(__file__),'templates')
+lookup = TemplateLookup(directories=[template_root],
+ output_encoding='utf-8')
+class ICaptchaState(Interface):
+ value = Attribute("A bool that indicates whether a Captcha has been solved")
+
+class CaptchaState(object):
+ implements(ICaptchaState)
+ def __init__(self, session):
+ self.value = False
+
+registerAdapter(CaptchaState, Session, ICaptchaState)
try:
import GeoIP
@@ -38,15 +58,63 @@ except:
geoip = None
logging.warn("GeoIP database not found")
+class WebRoot(twisted.web.resource.Resource):
+ isLeaf = True
+ def render_GET(self, request):
+ return lookup.get_template('index.html').render()
+
+class Captcha(twisted.web.resource.Resource):
+ isLeaf = True
+
+ def __init__(self, useRecaptcha=False, recaptchaPrivKey='', recaptchaPubKey=''):
+ self.recaptchaPrivKey = recaptchaPrivKey
+ self.recaptchaPubKey = recaptchaPubKey
+
+ def render_GET(self, request):
+ if not ICaptchaState(request.getSession()).value:
+ # get a captcha
+ c = Raptcha(self.recaptchaPubKey, self.recaptchaPrivKey)
+ c.get()
+
+ # TODO: this does not work for versions of IE < 8.0
+ imgstr = 'data:image/jpeg;base64,%s' % base64.b64encode(c.image)
+ return lookup.get_template('captcha.html').render()
+ else:
+ return redirectTo("", request)
+
+ def render_POST(self, request):
+ try:
+ challenge = request.args['recaptcha_challenge_field'][0]
+ response = request.args['recaptcha_response_field'][0]
+ except:
+ return redirectTo('captcha', request)
+
+ # generate a random IP for the captcha submission
+ remote_ip = '%d.%d.%d.%d' % (randint(1,255),randint(1,255),
+ randint(1,255),randint(1,255))
+
+ recaptcha_response = captcha.submit(challenge, response,
+ self.recaptchaPrivKey, remote_ip)
+ if recaptcha_response.is_valid:
+ logging.info("Valid recaptcha from %s. Parameters were %r",
+ remote_ip, request.args)
+ # set a valid captcha solved for this session!
+ captcha = ICaptchaState(request.getSession())
+ captcha.value = True
+ return redirectTo('', request)
+ else:
+ logging.info("Invalid recaptcha from %s. Parameters were %r",
+ remote_ip, request.args)
+ logging.info("Recaptcha error code: %s", recaptcha_response.error_code)
+ return redirectTo('captcha', request)
+
class WebResource(twisted.web.resource.Resource):
"""This resource is used by Twisted Web to give a web page with some
bridges in response to a request."""
isLeaf = True
def __init__(self, distributor, schedule, N=1, useForwardedHeader=False,
- includeFingerprints=True,
- useRecaptcha=False,recaptchaPrivKey='', recaptchaPubKey='',
- domains=None):
+ includeFingerprints=True, domains=None):
"""Create a new WebResource.
distributor -- an IPBasedDistributor object
schedule -- an IntervalSchedule object
@@ -64,52 +132,10 @@ class WebResource(twisted.web.resource.Resource):
if not domains: domains = []
self.domains = domains
- # recaptcha options
- self.useRecaptcha = useRecaptcha
- self.recaptchaPrivKey = recaptchaPrivKey
- self.recaptchaPubKey = recaptchaPubKey
-
def render_GET(self, request):
- if self.useRecaptcha:
- # get a captcha
- c = Raptcha(self.recaptchaPubKey, self.recaptchaPrivKey)
- c.get()
-
- # TODO: this does not work for versions of IE < 8.0
- imgstr = 'data:image/jpeg;base64,%s' % base64.b64encode(c.image)
- HTML_CAPTCHA_TEMPLATE = self.buildHTMLMessageTemplateWithCaptcha(
- getLocaleFromRequest(request), c.challenge, imgstr)
- return HTML_CAPTCHA_TEMPLATE
- else:
- return self.getBridgeRequestAnswer(request)
-
+ return self.getBridgeRequestAnswer(request)
def render_POST(self, request):
-
- # check captcha if recaptcha support is enabled
- if self.useRecaptcha:
- try:
- challenge = request.args['recaptcha_challenge_field'][0]
- response = request.args['recaptcha_response_field'][0]
-
- except:
- return self.render_GET(request)
-
- # generate a random IP for the captcha submission
- remote_ip = '%d.%d.%d.%d' % (randint(1,255),randint(1,255),
- randint(1,255),randint(1,255))
-
- recaptcha_response = captcha.submit(challenge, response,
- self.recaptchaPrivKey, remote_ip)
- if recaptcha_response.is_valid:
- logging.info("Valid recaptcha from %s. Parameters were %r",
- remote_ip, request.args)
- else:
- logging.info("Invalid recaptcha from %s. Parameters were %r",
- remote_ip, request.args)
- logging.info("Recaptcha error code: %s", recaptcha_response.error_code)
- return self.render_GET(request) # redirect back to captcha
-
return self.getBridgeRequestAnswer(request)
def getBridgeRequestAnswer(self, request):
@@ -198,95 +224,8 @@ class WebResource(twisted.web.resource.Resource):
request.setHeader("Content-Type", "text/plain")
return answer
else:
- HTML_MESSAGE_TEMPLATE = self.buildHTMLMessageTemplate(t)
- return HTML_MESSAGE_TEMPLATE % answer
-
- def buildHTMLMessageTemplate(self, t):
- """DOCDOC"""
- if self.domains:
- email_domain_list = "<ul>" \
- + "".join(("<li>%s</li>"%d for d in self.domains)) + "</ul>"
- else:
- email_domain_list = "<p>" + t.gettext(I18n.BRIDGEDB_TEXT[8]) + "</p>"
- html_msg = "<html><head>"\
- + "<meta http-equiv=\"Content-Type\" content=\"text/html;"\
- + " charset=UTF-8\"/>" \
- + "</head><body>" \
- + "<p>" + t.gettext(I18n.BRIDGEDB_TEXT[0]) \
- + "<pre id=\"bridges\">" \
- + "%s" \
- + "</pre></p>" \
- + "<p>" + t.gettext(I18n.BRIDGEDB_TEXT[1]) + "</p>" \
- + "<p>" + t.gettext(I18n.BRIDGEDB_TEXT[2]) + "</p>" \
- + "<p>" + t.gettext(I18n.BRIDGEDB_TEXT[3]) + "</p>" \
- + "<p>" + t.gettext(I18n.BRIDGEDB_TEXT[4]) + "</p>" \
- + email_domain_list \
- + "<hr /><p><a href='?ipv6=true'>" \
- + t.gettext(I18n.BRIDGEDB_TEXT[20]) + "</a></p>" \
- + "<p><a href='?transport=obfs2'>" \
- + t.gettext(I18n.BRIDGEDB_TEXT[21]) + "</a></p>" \
- + "<form method='GET'>" \
- + "<p>" + t.gettext(I18n.BRIDGEDB_TEXT[22]) + "</p>" \
- + "<input name='transport'>" \
- + "<input type='submit' value='" \
- + t.gettext(I18n.BRIDGEDB_TEXT[23]) +"'>" \
- + "</form>" \
- + "</body></html>"
- return html_msg
-
- def buildHTMLMessageTemplateWithCaptcha(self, t, challenge, img):
- """Builds a translated html response with recaptcha"""
- if self.domains:
- email_domain_list = "<ul>" \
- + "".join(("<li>%s</li>"%d for d in self.domains)) + "</ul>"
- else:
- email_domain_list = "<p>" + t.gettext(I18n.BRIDGEDB_TEXT[8]) + "</p>"
-
- recaptchaTemplate = textwrap.dedent("""\
- <form action="" method="POST">
- <input type="hidden" name="recaptcha_challenge_field"
- id="recaptcha_challenge_field"\
- value="{recaptchaChallengeField}">
- <img width="300" height="57" alt="{bridgeDBText14}"\
- src="{recaptchaImgSrc}">
- <div class="recaptcha_input_area">
- <label for="recaptcha_response_field">{bridgeDBText12}</label>
- </div>
- <div>
- <input name="recaptcha_response_field"\
- id="recaptcha_response_field"
- type="text" autocomplete="off">
- </div>
- <div>
- <input type="submit" name="submit" value="{bridgeDBText13}">
- </div>
- </form>
- """).strip()
-
- recaptchaTemplate = recaptchaTemplate.format(
- recaptchaChallengeField=challenge,
- recaptchaImgSrc=img,
- bridgeDBText12=t.gettext(I18n.BRIDGEDB_TEXT[13]),
- bridgeDBText13=t.gettext(I18n.BRIDGEDB_TEXT[14]),
- bridgeDBText14=t.gettext(I18n.BRIDGEDB_TEXT[15]))
-
- html_msg = "<html><body>" \
- + "<p>" + t.gettext(I18n.BRIDGEDB_TEXT[1]) + "</p>" \
- + "<p>" + t.gettext(I18n.BRIDGEDB_TEXT[9]) + "</p>" \
- + "<p>" + recaptchaTemplate + "</p>" \
- + "<p>" + t.gettext(I18n.BRIDGEDB_TEXT[4]) + "</p>" \
- + email_domain_list \
- + "<hr /><p><a href='?ipv6=true'>" \
- + t.gettext(I18n.BRIDGEDB_TEXT[20]) + "</a></p>" \
- + "<p><a href='?transport=obfs2'>" \
- + t.gettext(I18n.BRIDGEDB_TEXT[21]) + "</a></p>" \
- + "<form method='GET'>" \
- + "<p>" + t.gettext(I18n.BRIDGEDB_TEXT[22]) + "</p>" \
- + "<input name='transport'>" \
- + "<input name='submit' type='submit'>" \
- + "</form>" \
- + "</body></html>"
- return html_msg
+ request.setHeader("Content-Type", "text/html")
+ return lookup.get_template('bridges.html').render()
def addWebServer(cfg, dist, sched):
"""Set up a web server.
@@ -304,19 +243,26 @@ def addWebServer(cfg, dist, sched):
dist -- an IPBasedDistributor object.
sched -- an IntervalSchedule object.
"""
- Site = twisted.web.server.Site
site = None
+ httpdist = twisted.web.resource.Resource()
+ httpdist.putChild('', WebRoot())
+ httpdist.putChild('assets', static.File(os.path.join(template_root, 'assets/')))
+
+ if cfg.RECAPTCHA_ENABLED:
+ httpdist.putChild('captcha', Captcha(recaptchaPrivKey=cfg.RECAPTCHA_PRIV_KEY,
+ recaptchaPubKey=cfg.RECAPTCHA_PUB_KEY))
+ #XXX add a session guard
+
if cfg.HTTP_UNENCRYPTED_PORT:
ip = cfg.HTTP_UNENCRYPTED_BIND_IP or ""
resource = WebResource(dist, sched, cfg.HTTPS_N_BRIDGES_PER_ANSWER,
cfg.HTTP_USE_IP_FROM_FORWARDED_HEADER,
includeFingerprints=cfg.HTTPS_INCLUDE_FINGERPRINTS,
- useRecaptcha=cfg.RECAPTCHA_ENABLED,
- domains=cfg.EMAIL_DOMAINS,
- recaptchaPrivKey=cfg.RECAPTCHA_PRIV_KEY,
- recaptchaPubKey=cfg.RECAPTCHA_PUB_KEY)
- site = Site(resource)
+ domains=cfg.EMAIL_DOMAINS)
+ httpdist.putChild('bridges', resource)
+ site = Site(httpdist)
reactor.listenTCP(cfg.HTTP_UNENCRYPTED_PORT, site, interface=ip)
+
if cfg.HTTPS_PORT:
from twisted.internet.ssl import DefaultOpenSSLContextFactory
#from OpenSSL.SSL import SSLv3_METHOD
@@ -326,12 +272,11 @@ def addWebServer(cfg, dist, sched):
resource = WebResource(dist, sched, cfg.HTTPS_N_BRIDGES_PER_ANSWER,
cfg.HTTPS_USE_IP_FROM_FORWARDED_HEADER,
includeFingerprints=cfg.HTTPS_INCLUDE_FINGERPRINTS,
- domains=cfg.EMAIL_DOMAINS,
- useRecaptcha=cfg.RECAPTCHA_ENABLED,
- recaptchaPrivKey=cfg.RECAPTCHA_PRIV_KEY,
- recaptchaPubKey=cfg.RECAPTCHA_PUB_KEY)
- site = Site(resource)
+ domains=cfg.EMAIL_DOMAINS)
+ httpdist.putChild('bridges', resource)
+ site = Site(httpdist)
reactor.listenSSL(cfg.HTTPS_PORT, site, factory, interface=ip)
+
return site
def getLocaleFromRequest(request):
More information about the tor-commits
mailing list