Using OOTB-style Constraints in AEM on a Custom Form Component

andrewh9285432

29-06-2018

I'm trying to validate a custom recaptcha component using constraints in the same way the OOTB Form Elements do.  I'm having trouble figuring out how to connect the custom component to it's servervalidation.jsp though.

I need help getting to the point where submitting a form with the custom constraint selected causes the captcha/servervalidations.jsp to validate.  It currently allows an undone captcha to be submitted.

I have my component:

(I made a lot of edits to the captcha.jsp for brevity here since it's not really the point and I may have messed it up, so if there's syntax errors in that file don't worry about it.)

captcha.jsp

    <%@include file="/apps/codebase/global.jsp" %>

  

    <div class="captcha-wrap">

      <div id="recaptcha${properties.reCaptchaId}" class="g-recaptcha"></div>  

    <script>

  

        /* Renders captchas.  Runs @ async defer (callback on recaptcha src script in 'basepage/footer.jsp') */

        function recaptchaCallback() {

          var cC = $('.g-recaptcha').length;  // captcha count

          if ( cC >= 1 && cC <= 6) {   // recaptcha1 loads when cC is 1-6

            grecaptcha.render('recaptcha1', {

              'sitekey' : '<PUBLIC_KEY>'

            });

          }

          if ( cC >= 2 && cC <=  6) {

            grecaptcha.render('recaptcha2', {

              'sitekey' : '<PUBLIC_KEY>'

            });

          }

          /* Additional if statements for rendering 1 to 6 captchas on the same page */

  

    </script>

This script renders the captcha:

footer.jsp

    <script src="https://www.google.com/recaptcha/api.js?onload=recaptchaCallback&render=explicit" defer></script>

Here is my dialog with the OOTB captcha constraint tab included

dialog.xml

    <?xml version="1.0" encoding="UTF-8"?>

    <jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"

        jcr:primaryType="cq:Dialog"

        title="Captcha Configuration"

        xtype="dialog">

        <items

            jcr:primaryType="cq:Widget"

            xtype="tabpanel">

            <items jcr:primaryType="cq:WidgetCollection">

                <tab0

                    jcr:primaryType="cq:Panel"

                    title="Captcha">

                    <items jcr:primaryType="cq:WidgetCollection">

                        <reCaptchaId

                        jcr:primaryType="cq:Widget"

                        fieldDescription="Must be 1, 2, 3, 4, 5 or 6. Must go in order, i.e. don't only have a 1, 2, and 4 on the page or it won't work. Will not work with 7+ either."

                        fieldLabel="ReCaptcha ID"

                        name="./reCaptchaId"

                        xtype="textfield" />

                    </items>

                </tab0>

            <tab1

               jcr:primaryType="nt:unstructured"

               title="Constraints"

               xtype="panel">

               <items jcr:primaryType="cq:WidgetCollection">

                    <required

                         jcr:primaryType="cq:Widget"

                         fieldLabel="Required"

                         inputValue="true"

                         name="./required"

                         type="checkbox"

                         xtype="selection"/>

                   <requiredMessage

                         jcr:primaryType="cq:Widget"             

                         fieldLabel="Required Message"

                         name="./requiredMessage"

                         xtype="textarea"/>

                    <constraintType

                         jcr:primaryType="cq:Widget"

                         fieldLabel="Constraint"

                         name="./constraintType"

                         options="/etc/designs/codebase/options/constraints.json"

                         type="select"

                         xtype="selection"/>

                   <constraintMessage

                        jcr:primaryType="cq:Widget"

                        fieldLabel="Constraint Message"

                        name="./constraintMessage"

                        xtype="textarea"/>

                </items>

             </tab1> 

            </items>

        </items>

    </jcr:root>

And here is the json referenced:

constraints.json

    [

         {"value":"","text":"None"},

         {"value":"codebase/components/content/formelements/constraints/captcha","text":"Captcha"}

    ]

Which brings me to the constraint file itself.  My problem is that I cannot get this file to actually validate against the captcha.jsp file.  So it has not been debugged and may be full of java errors for all I know.

This file is a combination of the OOTB file found at /libs/foundation/components/form/captcha/servervalidation.jsp<br>

and the captcha validation solution found here: Server side validation for reCAPTCHA V2 or Invisible reCAPTCHA with Java (Servlet) - Code Review Sta...

codebase/components/content/formelements/constraints/captcha/servervalidation.jsp

    <%@page session="false" %><%

    %><%@page import="java.io.BufferedReader,

    java.io.InputStream;

                    java.text.ParsePosition,

        java.io.InputStreamReader,

    java.net.URL,

    java.nio.charset.Charset,

    org.json.JSONObject,

                    com.day.cq.wcm.foundation.forms.FieldHelper,

                    com.day.cq.wcm.foundation.forms.FieldDescription,

                    com.day.cq.wcm.foundation.forms.FormsHelper,

                    com.day.cq.wcm.foundation.forms.ValidationInfo"%><%

    %><%@taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0" %>

  

    <sling:defineObjects/><%

  

        // Get field description and force its name

        FieldDescription desc = FieldHelper.getConstraintFieldDescription(slingRequest);

        desc.setName(":cq:recaptcha");

  

        // Check if a value has been provided

        if (FieldHelper.checkRequired(slingRequest, desc)) {

            final String response = request.getParameter("g-recaptcha-response");

            final String secretKey = "<PRIVATE_KEY>";

  

    private static boolean isCaptchaValid(String secretKey, String response) {

        try {

            String url = "https://www.google.com/recaptcha/api/siteverify?"

                    + "secret=" + secretKey

                    + "&response=" + response;

            InputStream res = new URL(url).openStream();

            BufferedReader rd = new BufferedReader(new InputStreamReader(res, Charset.forName("UTF-8")));

  

            StringBuilder sb = new StringBuilder();

            int cp;

            while ((cp = rd.read()) != -1) {

                sb.append((char) cp);

            }

            String jsonText = sb.toString();

            res.close();

  

            JSONObject json = new JSONObject(jsonText);

            return json.getBoolean("success");

        } catch (Exception e) {

            return false;

        }

    }

            if (!isCaptchaValid("<PRIVATE_KEY>", request.getParameter("g-recaptcha-response"))) {

                ValidationInfo.addConstraintError(slingRequest, desc);

            }

        }

  

    %>

How can I get this captcha constraint `servervalidation.jsp` file to actually work? Currently the captcha can be submitted uncompleted and no validation stops it.

I do notice some form parameters in the `_cq_editConfig.xml` of the OOTB components, and that's the only thing I notice that I haven't copied to try to get this constraint to work. But that's because I'm not sure how it works.

Replies

Highlighted

andrewh9285432

09-07-2018

smacdonald2008 The issues with the example code and v2 aren't because of the environment or AEM, it's because it's not set up to verify v2 like I explained above.  The Ajax call is only triggered if capResponse has a value via .val(),  and it can only get a value that way on v1.  There are several other variables included in the call that also don't have a value in v2. 

On v2, you get the response value with grecaptcha.getResponse(); or request.getParameter("g-recaptcha-response").  You then need to send a POST to google like they say in the docs, which the example code isn't doing: Verifying the user's response  |  reCAPTCHA       |  Google Developers

The validatecaptcha.jsp in the example code is asking for a user response and a challenge field to send in the recaptcha.checkAnswer() call and sends the key via recaptcha.setPrivateKey().  With v2 you send your private key and the just the g-recaptcha-response to "https://www.google.com/recaptcha/api/siteverify", the 'user response' doesn't exist.

The call url looks like this:

String url = "https://www.google.com/recaptcha/api/siteverify?"
  
+ "secret=" + secretKey
  
+ "&response=" + response;

I'm in the process of creating one for v2.  If I'm misunderstanding and the Adobe site example does work for v2 I apologize for the long-winded explanations, I don't think the example code works though.