001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.captcha.recaptcha;
016    
017    import com.liferay.portal.captcha.simplecaptcha.SimpleCaptchaImpl;
018    import com.liferay.portal.kernel.captcha.CaptchaConfigurationException;
019    import com.liferay.portal.kernel.captcha.CaptchaException;
020    import com.liferay.portal.kernel.captcha.CaptchaTextException;
021    import com.liferay.portal.kernel.exception.SystemException;
022    import com.liferay.portal.kernel.json.JSONArray;
023    import com.liferay.portal.kernel.json.JSONException;
024    import com.liferay.portal.kernel.json.JSONFactoryUtil;
025    import com.liferay.portal.kernel.json.JSONObject;
026    import com.liferay.portal.kernel.log.Log;
027    import com.liferay.portal.kernel.log.LogFactoryUtil;
028    import com.liferay.portal.kernel.util.Http;
029    import com.liferay.portal.kernel.util.HttpUtil;
030    import com.liferay.portal.kernel.util.ParamUtil;
031    import com.liferay.portal.kernel.util.PortalUtil;
032    import com.liferay.portal.kernel.util.PropsKeys;
033    import com.liferay.portal.kernel.util.StringBundler;
034    import com.liferay.portal.kernel.util.StringPool;
035    import com.liferay.portal.kernel.util.StringUtil;
036    import com.liferay.portal.kernel.util.Validator;
037    import com.liferay.portal.util.PrefsPropsUtil;
038    import com.liferay.portal.util.PropsValues;
039    
040    import java.io.IOException;
041    
042    import javax.portlet.PortletRequest;
043    import javax.portlet.ResourceRequest;
044    import javax.portlet.ResourceResponse;
045    
046    import javax.servlet.http.HttpServletRequest;
047    import javax.servlet.http.HttpServletRequestWrapper;
048    import javax.servlet.http.HttpServletResponse;
049    
050    /**
051     * @author Tagnaouti Boubker
052     * @author Jorge Ferrer
053     * @author Brian Wing Shun Chan
054     * @author Daniel Sanz
055     */
056    public class ReCaptchaImpl extends SimpleCaptchaImpl {
057    
058            @Override
059            public String getTaglibPath() {
060                    return _TAGLIB_PATH;
061            }
062    
063            @Override
064            public void serveImage(
065                    HttpServletRequest request, HttpServletResponse response) {
066    
067                    throw new UnsupportedOperationException();
068            }
069    
070            @Override
071            public void serveImage(
072                    ResourceRequest resourceRequest, ResourceResponse resourceResponse) {
073    
074                    throw new UnsupportedOperationException();
075            }
076    
077            @Override
078            protected boolean validateChallenge(HttpServletRequest request)
079                    throws CaptchaException {
080    
081                    String reCaptchaResponse = ParamUtil.getString(
082                            request, "g-recaptcha-response");
083    
084                    while (Validator.isBlank(reCaptchaResponse) &&
085                               (request instanceof HttpServletRequestWrapper)) {
086    
087                            HttpServletRequestWrapper httpServletRequestWrapper =
088                                    (HttpServletRequestWrapper)request;
089    
090                            request =
091                                    (HttpServletRequest)httpServletRequestWrapper.getRequest();
092    
093                            reCaptchaResponse = ParamUtil.getString(
094                                    request, "g-recaptcha-response");
095                    }
096    
097                    if (Validator.isBlank(reCaptchaResponse)) {
098                            _log.error(
099                                    "CAPTCHA text is null. User " + request.getRemoteUser() +
100                                            " may be trying to circumvent the CAPTCHA.");
101    
102                            throw new CaptchaTextException();
103                    }
104    
105                    Http.Options options = new Http.Options();
106    
107                    try {
108                            options.addPart(
109                                    "secret",
110                                    PrefsPropsUtil.getString(
111                                            PropsKeys.CAPTCHA_ENGINE_RECAPTCHA_KEY_PRIVATE,
112                                            PropsValues.CAPTCHA_ENGINE_RECAPTCHA_KEY_PRIVATE));
113                    }
114                    catch (SystemException se) {
115                            _log.error(se, se);
116                    }
117    
118                    options.addPart("remoteip", request.getRemoteAddr());
119                    options.addPart("response", reCaptchaResponse);
120                    options.setLocation(PropsValues.CAPTCHA_ENGINE_RECAPTCHA_URL_VERIFY);
121                    options.setPost(true);
122    
123                    String content = null;
124    
125                    try {
126                            content = HttpUtil.URLtoString(options);
127                    }
128                    catch (IOException ioe) {
129                            _log.error(ioe, ioe);
130    
131                            throw new CaptchaConfigurationException();
132                    }
133    
134                    if (content == null) {
135                            _log.error("reCAPTCHA did not return a result");
136    
137                            throw new CaptchaConfigurationException();
138                    }
139    
140                    try {
141                            JSONObject jsonObject = JSONFactoryUtil.createJSONObject(content);
142    
143                            String success = jsonObject.getString("success");
144    
145                            if (StringUtil.equalsIgnoreCase(success, "true")) {
146                                    return true;
147                            }
148    
149                            JSONArray jsonArray = jsonObject.getJSONArray("error-codes");
150    
151                            if ((jsonArray == null) || (jsonArray.length() == 0)) {
152                                    _log.error("reCAPTCHA encountered an error");
153    
154                                    throw new CaptchaConfigurationException();
155                            }
156    
157                            StringBundler sb = new StringBundler(jsonArray.length() * 2 - 1);
158    
159                            for (int i = 0; i < jsonArray.length(); i++) {
160                                    sb.append(jsonArray.getString(i));
161    
162                                    if (i < (jsonArray.length() - 1)) {
163                                            sb.append(StringPool.COMMA_AND_SPACE);
164                                    }
165                            }
166    
167                            _log.error("reCAPTCHA encountered an error: " + sb.toString());
168    
169                            throw new CaptchaConfigurationException();
170                    }
171                    catch (JSONException jsone) {
172                            _log.error("reCAPTCHA did not return a valid result: " + content);
173    
174                            throw new CaptchaConfigurationException();
175                    }
176            }
177    
178            @Override
179            protected boolean validateChallenge(PortletRequest portletRequest)
180                    throws CaptchaException {
181    
182                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
183                            portletRequest);
184    
185                    return validateChallenge(request);
186            }
187    
188            private static final String _TAGLIB_PATH =
189                    "/html/taglib/ui/captcha/recaptcha.jsp";
190    
191            private static final Log _log = LogFactoryUtil.getLog(ReCaptchaImpl.class);
192    
193    }