Ignore:
Timestamp:
29 Nov 2018, 16:24:00 (6 years ago)
Author:
Henrik Bettermann
Message:

Use the 'new' recaptcha v2.

The old one was turned off in March.

Location:
main/waeup.ikoba/trunk/src/waeup/ikoba/browser
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.ikoba/trunk/src/waeup/ikoba/browser/captcha.py

    r11954 r15256  
    2121"""
    2222import grok
     23import json
    2324import urllib
    2425import urllib2
     
    163164
    164165##
    165 ## ReCaptcha
    166 ##
    167 API_SSL_SERVER = "https://www.google.com/recaptcha/api"
    168 VERIFY_SERVER = "https://www.google.com/recaptcha/api"
     166## ReCaptcha (v2)
     167##
    169168
    170169class ReCaptcha(StaticCaptcha):
     
    172171
    173172    This is the Ikoba implementation to support captchas as provided by
    174     http://www.google.com/recaptcha.
     173    http://www.google.com/recaptcha (v2).
    175174
    176175    ReCaptcha is widely used and adopted in web applications. See the
     
    220219    This captcha is available at runtime as a global utility named
    221220    ``'ReCaptcha'``.
     221
     222    Shortcomings:
     223        - no support for selecting theme
     224        - no support for selecting box layout (normal, compact)
     225        - no support for non-javascript users
     226        - Google will get the data of our clients
     227        - Google will track all activity of our clients
    222228    """
    223229
    224230    grok.implements(ICaptcha)
    225231
    226     #: name of solution field in HTTP request
    227     sol_field = 'recaptcha_response_field'
    228     #: name of challenge field in HTTP request
    229     chal_field = 'recaptcha_challenge_field'
     232    #: name of response token field in HTTP request
     233    token_field = 'g-recaptcha-response'
    230234
    231235    # Do not use the following keys in productive environments!  As
     
    235239    PRIVATE_KEY = "6Lc0y8oSAAAAAMHVbMrGWLLjw2pm8v2Uprwm9AbR"
    236240
     241    VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'
     242
    237243    def verify(self, request):
    238244        """Grab challenge/solution from HTTP request and verify it.
    239245
    240246        Verification happens against recaptcha remote API servers. It
    241         only happens, when really a solution was sent with the
     247        only happens, when a repsonse token was sent with the
    242248        request.
    243249
     
    246252        """
    247253        form = getattr(request, 'form', {})
    248         solution=form.get(self.sol_field, None)
    249         challenge=form.get(self.chal_field, None)
    250         if not challenge or not solution:
    251             # Might be first-time display of the captcha: not a valid
    252             # solution but no error code to prevent any error
    253             # messages. Skip further verification.
    254             return CaptchaResponse(
    255                 is_valid=False)
     254        token = form.get(self.token_field, None)
     255        if not token:
     256            # on first-time display, we won't get a token
     257            return CaptchaResponse(is_valid=False)
    256258        params = urllib.urlencode(
    257259            {
    258                 'privatekey': self.PRIVATE_KEY,
     260                'secret': self.PRIVATE_KEY,
     261                'response': token,
    259262                'remoteip': '127.0.0.1',
    260                 'challenge': challenge,
    261                 'response': solution,
    262263                })
    263264        request = urllib2.Request(
    264             url = "%s/verify" % VERIFY_SERVER,
     265            url = self.VERIFY_URL,
    265266            data = params,
    266267            headers = {
    267268                "Content-type": "application/x-www-form-urlencoded",
    268                 "User-agent": "reCAPTCHA Python Ikoba",
     269                "User-agent": "reCAPTCHA Python Kofa",
    269270                }
    270271            )
    271         resp = urllib2.urlopen(request)
    272         ret_vals = resp.read().splitlines()
    273         resp.close()
    274         ret_code, err_code = ret_vals
    275 
    276         if ret_code == "true":
     272        conn = urllib2.urlopen(request)
     273        ret_vals = json.loads(conn.read())
     274        conn.close()
     275        if ret_vals.get('success', False) is True:
    277276            return CaptchaResponse(is_valid=True)
    278         return CaptchaResponse(is_valid=False, error_code=ret_vals[1])
     277        return CaptchaResponse(
     278            is_valid=False, error_code="%s" % ret_vals['error-codes'])
    279279
    280280    def display(self, error_code=None):
    281         """Display challenge and input field for solution as HTML.
     281        """Display captcha widget snippet.
    282282
    283283        Returns the HTML code to be placed inside an existing ``<form>``
     
    297297        html = (
    298298            u'<script type="text/javascript" '
    299             u'src="%(ApiServer)s/challenge?k=%(PublicKey)s%(ErrorParam)s">'
     299            u'src="%(ApiServer)s" async defer>'
    300300            u'</script>'
    301             u''
    302             u'<noscript>'
    303             u'<iframe'
    304             u'    src="%(ApiServer)s/noscript?k=%(PublicKey)s%(ErrorParam)s"'
    305             u'    height="300" width="500" frameborder="0"></iframe><br />'
    306             u'<textarea name="recaptcha_challenge_field"'
    307             u'          rows="3" cols="40"></textarea>'
    308             u'<input type="hidden" name="recaptcha_response_field"'
    309             u'       value="manual_challenge" />'
    310             u'</noscript>' % {
    311                 'ApiServer' : API_SSL_SERVER,
    312                 'PublicKey' : self.PUBLIC_KEY,
    313                 'ErrorParam' : error_param,
     301            u'<div class="g-recaptcha" data-sitekey="%(SiteKey)s"></div>' % {
     302                'ApiServer': "https://www.google.com/recaptcha/api.js",
     303                'SiteKey': self.PUBLIC_KEY,
    314304                }
    315             )
     305        )
    316306        return html
     307
    317308
    318309grok.global_utility(ReCaptcha, name=u'ReCaptcha')
  • main/waeup.ikoba/trunk/src/waeup/ikoba/browser/tests/test_captcha.py

    r11949 r15256  
    171171        result = captcha.display()
    172172        self.assertMatches(
    173             '<script type="text/javascript" src="..."></script>'
    174             '<noscript>'
    175             '<iframe src="..." height="300" width="500" '
    176             '        frameborder="0"></iframe><br />'
    177             '<textarea name="recaptcha_challenge_field" rows="3" '
    178             '          cols="40"></textarea>'
    179             '<input type="hidden" name="recaptcha_response_field" '
    180             '       value="manual_challenge" /></noscript>',
     173            '<script type="text/javascript" '
     174            'src="..." async defer></script>'
     175            '<div class="g-recaptcha" data-sitekey="..."></div>',
    181176            result)
    182177        return
Note: See TracChangeset for help on using the changeset viewer.