Artem Egorkine avatar Artem Egorkine committed 8c9d29e

Better handle the situations when reCaptcha keys are not configured.
* Don't render the recaptcha field in the comment form if recaptcha
is not configured;
* Don't check recaptcha respose (assume pass) if recaptcha is not
configured;
* If recaptcha _is_ configured, but challenge/response fields are
missing from the request, do not crash the app, but consider
recaptcha check failed!
* If recaptcha-client package is not installed, throw a better SetupError,
hinting which package to install.

Comments (0)

Files changed (1)

         label=_(u'Skip Authenticated')
     )
 
+class CaptchaFailedMockup:
+    """
+    A mock reCAPTCHA response object signifying that the captcha check
+    failed (due to invalid parameters).
+    """
+    is_valid = False
+    error_code = 'verify-params-incorrect'
+
+class CaptchaPassedMockup:
+    """
+    A mock reCAPTCHA response object signifying that the captcha check passed.
+    """
+    is_valid = True
+
 def check_captcha(req):
+    """
+    Perform the captcha check and return the response object. If reCAPTCHA
+    is configured (public/private keys set) bit the challenge and response
+    fields are missing from the request, consider the captcha check failed
+    as a security measure. If reCAPTCHA is not configured, consider the
+    check passed!
+    """
+
+    disabled = (
+            req.app.cfg['recaptcha_spam_filter/pubkey'] is None or
+            req.app.cfg['recaptcha_spam_filter/pubkey'] == '' or
+            req.app.cfg['recaptcha_spam_filter/privkey'] is None or
+            req.app.cfg['recaptcha_spam_filter/privkey'] == ''
+            )
+    if disabled:
+        return CaptchaPassedMockup()
+
+    try:
+        challenge = req.values['recaptcha_challenge_field']
+        response = req.values['recaptcha_response_field']
+    except req.values.KeyError:
+        return CaptchaFailedMockup()
+
     return captcha.submit(
-        recaptcha_challenge_field=req.values['recaptcha_challenge_field'],
-        recaptcha_response_field=req.values['recaptcha_response_field'],
+        recaptcha_challenge_field=challenge,
+        recaptcha_response_field=response,
         private_key=req.app.cfg['recaptcha_spam_filter/privkey'],
         remoteip=req.remote_addr
     )
     app = get_application()
     req = get_request()
 
+    disabled = (
+            app.cfg['recaptcha_spam_filter/pubkey'] is None or
+            app.cfg['recaptcha_spam_filter/pubkey'] == '' or
+            app.cfg['recaptcha_spam_filter/privkey'] is None or
+            app.cfg['recaptcha_spam_filter/privkey'] == ''
+            )
+    if disabled:
+        return ''
+
     if req.user.is_somebody and app.cfg['recaptcha_spam_filter/auth_skip']:
         return ''
 
 
 def setup(app, plugin):
     if not have_recaptcha:
-        raise SetupError("The reCAPTCHA plugin requires the recaptcha python library")
+        raise SetupError("The reCAPTCHA plugin requires the recaptcha python library. Please install the 'recaptcha-client' package.")
     app.add_config_var('recaptcha_spam_filter/pubkey',
                        ConfigurationForm.pubkey)
     app.add_config_var('recaptcha_spam_filter/privkey',
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.