/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.captcha.recaptcha; import com.liferay.captcha.configuration.CaptchaConfiguration; import com.liferay.captcha.simplecaptcha.SimpleCaptchaImpl; import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil; import com.liferay.portal.kernel.captcha.Captcha; import com.liferay.portal.kernel.captcha.CaptchaConfigurationException; import com.liferay.portal.kernel.captcha.CaptchaException; import com.liferay.portal.kernel.captcha.CaptchaTextException; import com.liferay.portal.kernel.exception.SystemException; import com.liferay.portal.kernel.json.JSONArray; import com.liferay.portal.kernel.json.JSONException; import com.liferay.portal.kernel.json.JSONFactoryUtil; import com.liferay.portal.kernel.json.JSONObject; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.util.Http; import com.liferay.portal.kernel.util.HttpUtil; import com.liferay.portal.kernel.util.ParamUtil; import com.liferay.portal.kernel.util.PortalUtil; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import java.io.IOException; import java.util.Map; import javax.portlet.PortletRequest; import javax.portlet.ResourceRequest; import javax.portlet.ResourceResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Modified; /** * @author Tagnaouti Boubker * @author Jorge Ferrer * @author Brian Wing Shun Chan * @author Daniel Sanz */ @Component( configurationPid = "com.liferay.captcha.configuration.CaptchaConfiguration", immediate = true, property = { "captcha.engine.impl=com.liferay.captcha.recaptcha.ReCaptchaImpl" }, service = Captcha.class ) public class ReCaptchaImpl extends SimpleCaptchaImpl { @Override public String getTaglibPath() { return _TAGLIB_PATH; } @Override public void serveImage( HttpServletRequest request, HttpServletResponse response) { throw new UnsupportedOperationException(); } @Override public void serveImage( ResourceRequest resourceRequest, ResourceResponse resourceResponse) { throw new UnsupportedOperationException(); } @Activate @Modified @Override protected void activate(Map<String, Object> properties) { _captchaConfiguration = ConfigurableUtil.createConfigurable( CaptchaConfiguration.class, properties); setCaptchaConfiguration(_captchaConfiguration); } @Override protected boolean validateChallenge(HttpServletRequest request) throws CaptchaException { String reCaptchaResponse = ParamUtil.getString( request, "g-recaptcha-response"); while (Validator.isBlank(reCaptchaResponse) && (request instanceof HttpServletRequestWrapper)) { HttpServletRequestWrapper httpServletRequestWrapper = (HttpServletRequestWrapper)request; request = (HttpServletRequest)httpServletRequestWrapper.getRequest(); reCaptchaResponse = ParamUtil.getString( request, "g-recaptcha-response"); } if (Validator.isBlank(reCaptchaResponse)) { _log.error( "CAPTCHA text is null. User " + request.getRemoteUser() + " may be trying to circumvent the CAPTCHA."); throw new CaptchaTextException(); } Http.Options options = new Http.Options(); options.setLocation(_captchaConfiguration.reCaptchaVerifyURL()); try { options.addPart( "secret", _captchaConfiguration.reCaptchaPrivateKey()); } catch (SystemException se) { _log.error(se, se); } options.addPart("remoteip", request.getRemoteAddr()); options.addPart("response", reCaptchaResponse); options.setPost(true); String content = null; try { content = HttpUtil.URLtoString(options); } catch (IOException ioe) { _log.error(ioe, ioe); throw new CaptchaConfigurationException(); } if (content == null) { _log.error("reCAPTCHA did not return a result"); throw new CaptchaConfigurationException(); } try { JSONObject jsonObject = JSONFactoryUtil.createJSONObject(content); String success = jsonObject.getString("success"); if (StringUtil.equalsIgnoreCase(success, "true")) { return true; } JSONArray jsonArray = jsonObject.getJSONArray("error-codes"); if ((jsonArray == null) || (jsonArray.length() == 0)) { _log.error("reCAPTCHA encountered an error"); throw new CaptchaConfigurationException(); } StringBundler sb = new StringBundler(jsonArray.length() * 2 - 1); for (int i = 0; i < jsonArray.length(); i++) { sb.append(jsonArray.getString(i)); if (i < (jsonArray.length() - 1)) { sb.append(StringPool.COMMA_AND_SPACE); } } _log.error("reCAPTCHA encountered an error: " + sb.toString()); throw new CaptchaConfigurationException(); } catch (JSONException jsone) { _log.error( "reCAPTCHA did not return a valid result: " + content, jsone); throw new CaptchaConfigurationException(); } } @Override protected boolean validateChallenge(PortletRequest portletRequest) throws CaptchaException { HttpServletRequest request = PortalUtil.getHttpServletRequest( portletRequest); return validateChallenge(request); } private static final String _TAGLIB_PATH = "/captcha/recaptcha.jsp"; private static final Log _log = LogFactoryUtil.getLog(ReCaptchaImpl.class); private volatile CaptchaConfiguration _captchaConfiguration; }