diff --git a/gluon/tools.py b/gluon/tools.py index 790c172e..6d6c42fe 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -60,7 +60,7 @@ except ImportError: # fallback to pure-Python module import gluon.contrib.simplejson as json_parser -__all__ = ['Mail', 'Auth', 'Recaptcha', 'Crud', 'Service', 'Wiki', +__all__ = ['Mail', 'Auth', 'Recaptcha', 'Recaptcha2', 'Crud', 'Service', 'Wiki', 'PluginManager', 'fetch', 'geocode', 'reverse_geocode', 'prettydate'] ### mind there are two loggers here (logger and crud.settings.logger)! @@ -965,7 +965,142 @@ class Recaptcha(DIV): return XML(captcha).xml() -# this should only be used for catcha and perhaps not even for that +class Recaptcha2(DIV): + """ + Experimental: + Creates a DIV holding the newer Recaptcha from Google (v2) + + Args: + request : the request. If not passed, uses current request + public_key : the public key Google gave you + private_key : the private key Google gave you + error_message : the error message to show if verification fails + label : the label to use + options (dict) : takes these parameters + + - hl + - theme + - type + - tabindex + - callback + - expired-callback + + see https://developers.google.com/recaptcha/docs/display for docs about those + + comment : the comment + + Examples: + Use as:: + + form = FORM(Recaptcha2(public_key='...',private_key='...')) + + or:: + + form = SQLFORM(...) + form.append(Recaptcha2(public_key='...',private_key='...')) + + to protect the login page instead, use:: + + from gluon.tools import Recaptcha2 + auth.settings.captcha = Recaptcha2(request, public_key='...',private_key='...') + + """ + + API_URI = 'https://www.google.com/recaptcha/api.js' + VERIFY_SERVER = 'https://www.google.com/recaptcha/api/siteverify' + + def __init__(self, + request=None, + public_key='', + private_key='', + error_message='invalid', + label='Verify:', + options=None, + comment='', + ): + request = request or current.request + self.request_vars = request and request.vars or current.request.vars + self.remote_addr = request.env.remote_addr + self.public_key = public_key + self.private_key = private_key + self.errors = Storage() + self.error_message = error_message + self.components = [] + self.attributes = {} + self.label = label + self.options = options or {} + self.comment = comment + + def _validate(self): + recaptcha_response_field = self.request_vars.pop('g-recaptcha-response', None) + remoteip = self.remote_addr + if not recaptcha_response_field: + self.errors['captcha'] = self.error_message + return False + params = urllib.urlencode({ + 'secret': self.private_key, + 'remoteip': remoteip, + 'response': recaptcha_response_field, + }) + request = urllib2.Request( + url=self.VERIFY_SERVER, + data=params, + headers={'Content-type': 'application/x-www-form-urlencoded', + 'User-agent': 'reCAPTCHA Python'}) + httpresp = urllib2.urlopen(request) + content = httpresp.read() + httpresp.close() + try: + response_dict = json_parser.loads(content) + except: + self.errors['captcha'] = self.error_message + return False + if response_dict.get('success', False): + self.request_vars.captcha = '' + return True + else: + self.errors['captcha'] = self.error_message + return False + + def xml(self): + api_uri = self.API_URI + hl = self.options.pop('hl', None) + if hl: + api_uri = self.API_URI + '?hl=%s' % hl + public_key = self.public_key + self.options['sitekey'] = public_key + captcha = DIV( + SCRIPT(_src=api_uri, _async='', _defer=''), + DIV(_class="g-recaptcha", data=self.options), + TAG.noscript(XML(""" +