pythonpython-2.7recaptchazopedtml

Validating reCaptcha2 with Zope2


Using Zope 2.13.29 and Python 2.7.15, I am trying to validate Google's reCaptcha2 using a Python script being called from a DTML document. The DTML document contains the form, and the same DTML document uses a dtml-if statement to determines whether to process the form once a user has clicked submit. Within that dtml-if statement is a dtml-in that calls to the python script in order to validate the reCaptcha and return the value of success (True or False). The issue is that I cannot seem to obtain the value of success.

While I know some of you may be inclined to talk about RestrictedPython and how I won't be able to import the python modules for use by Zope, that portion has already been taken care of. It all works, it just doesn't do what I need it to do.

This is the dtml-doc that shows the contact form, as well as determines whether submit has been clicked, which in turns calls to the python script to finally determine the returned value of the Google success variable from reCaptcha.

<dtml-if submit>

<dtml-with recaptcha-validate>

<dtml-if expr="'success' == True">

<dtml-sendmail mailhost="MailHost">
To: info@foo.com
From: &dtml.lower-emailaddr;
Subject: Web Request

&dtml-message;

Sincerely,

&dtml-realname;
&dtml-emailaddr;
&dtml-phone;
&dtml-location;

</dtml-sendmail>

<dtml-else>

<dtml-sendmail mailhost="MailHost">
To: info@foo.com
From: &dtml.lower-emailaddr;
Subject: SPAM: Web Request

&dtml-message;

Sincerely,

&dtml-realname;
&dtml-emailaddr;
&dtml-phone;
&dtml-location;

</dtml-sendmail>

</dtml-if>
</dtml-with>

<dtml-else submit>

<dtml-var "foo.sitefiles.doctype(_.None, _)">
<html>
<head>
<title>Contact Us</title>

<script src="https://www.foo.com/javascript/jquery/jquery-min.js" type="text/javascript"></script>
<script src="https://www.foo.com/javascript/parsley/parsley.min.js" type="text/javascript"></script>

<script src="https://www.foo.com/javascript/misc/placeholder.js" type="text/javascript"></script>

<script src="https://www.google.com/recaptcha/api.js"></script>

</head>

<body>

<form data-parsley-validate id="contact-form" method="post" action=<dtml-var URL0> novalidate="" parsley-validate="" parsley-focus="none" data-parsley-errors-messages-disabled data-parsley-excluded="input[name=g-recaptcha-response], input[id=recaptcha-token]">
<fieldset>
<label>NAME</label>
<input class="parsley-validated" type="text" name="realname" placeholder="Enter your name" value="" required="">
<label>E-MAIL</label>
<input class="parsley-validated" type="email" name="emailaddr" placeholder="Enter your e-mail" value="" required="" pattern="^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$">
<label>PHONE NUMBER</label>
<input class="parsley-validated" type="text" name="phone" placeholder="Enter your contact phone number" value="" required="" pattern="^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$">
<label>LOCATION</label>
<input class="parsley-validated" type="text" name="location" placeholder="Enter your city, state" value="">
<label>MESSAGE</label>
<textarea class="parsley-validated" name="message" placeholder="Enter your message here" value="" required="" rows="8"></textarea>
<input id="myField" data-parsley-errors-container="#errorContainer" data-parsley-required="true" value="" type="text" style="display:none;">
<div id="contactrecaptcha">
<div id="recaptchatoken" class="g-recaptcha" data-sitekey="XXXXXXXXXX" data-        callback="recaptchaCallback"></div>
</div>
<span id='errorContainer'></span>
</fieldset>
<input type="submit" name="submit" value="SUBMIT">
</form>

<script type="text/javascript">
$('#contact-form').parsley();
function recaptchaCallback() {
    document.getElementById('myField').value = 'nonEmpty';
}
</script>

</body>
</html>

</dtml-if>

This is the python script (named recaptcha-validate):

import urllib
import urllib2
import json

REQUEST = container.REQUEST

URIReCaptcha = 'https://www.google.com/recaptcha/api/siteverify'
secret = 'XXXXXXXXXXXXXXXXXXX'
recaptcha_response = context.REQUEST['g-recaptcha-response']
remoteip = REQUEST.HTTP_X_FORWARDED_FOR

params = urllib.urlencode({
'secret': secret,
'response': recaptcha_response,
'remoteip': remoteip,
})

#print params
from urllib2 import urlopen
req = urllib2.Request(URIReCaptcha, params)
response = urllib2.urlopen(req)
result = json.load(response)
success = result.get('success', None)
return{'success': success}

When the script is run from the ZMI via the test tab, where I expect the result to be false, I get back

{'success': False}

However, I cannot seem to call on the success variable from the DTML-doc.

The form should be submitted, the python script should be run and validate the recaptcha, return the value of success (True/False) and then the dtml-if should decide how to proceed with processing the form.


Solution

  • After quite a bit of work, and speaking with a number of different people, someone very kind on the Plone forums showed me what I was doing wrong. I was making it too difficult, but was very close.

    Here's the python script (named recaptcha_validate):

    import urllib
    import urllib2
    import json
    
    REQUEST = container.REQUEST
    
    URIReCaptcha = 'https://www.google.com/recaptcha/api/siteverify'
    secret = 'XXXXXXXXXXX'
    recaptcha_response = context.REQUEST['g-recaptcha-response']
    remoteip = REQUEST.HTTP_X_FORWARDED_FOR
    
    params = urllib.urlencode({
    'secret': secret,
    'response': recaptcha_response,
    'remoteip': remoteip,
    })
    
    #print params
    from urllib2 import urlopen
    req = urllib2.Request(URIReCaptcha, params)
    response = urllib2.urlopen(req)
    result = json.load(response)
    success = result.get('success', None)
    return success
    

    Here is top portion of the dtml-doc that checks if submit was clicked and chooses how to process the form:

    <dtml-if submit>
    
    <dtml-if recaptcha_validate>
    
    <dtml-sendmail mailhost="MailHost">
    To: info@foo.com
    From: &dtml.lower-emailaddr;
    Subject: Web Request
    
    &dtml-message;
    
    Sincerely,
    
    &dtml-realname;
    &dtml-emailaddr;
    &dtml-phone;
    &dtml-location;
    
    </dtml-sendmail>
    
    <dtml-else>
    
    <dtml-sendmail mailhost="MailHost">
    To: info@foo.com
    From: &dtml.lower-emailaddr;
    Subject: SPAM: Web Request
    
    &dtml-message;
    
    Sincerely,
    
    &dtml-realname;
    &dtml-emailaddr;
    &dtml-phone;
    &dtml-location;
    
    </dtml-sendmail>
    
    </dtml-if>
    

    Doing a simple

    <dtml-if recaptcha_validate>
    

    runs the python script and checks whether the result is true. No need for any variables or expressions in this case. It's really that simple.

    Note: Using the

    <dtml-sendmail>
    

    tag, and only wanting to change the subject, I tried surrounding the Subject: Web Request line with the

    <dtml-if recaptcha_validate>
    

    statement. That does not work. You must have two completely separate

    <dtml-sendmail>
    

    stanzas in order for this to work correctly.