/**
* 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.simplecaptcha;
import com.liferay.captcha.configuration.CaptchaConfiguration;
import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil;
import com.liferay.portal.kernel.captcha.Captcha;
import com.liferay.portal.kernel.captcha.CaptchaException;
import com.liferay.portal.kernel.captcha.CaptchaTextException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.security.RandomUtil;
import com.liferay.portal.kernel.upload.UploadPortletRequest;
import com.liferay.portal.kernel.util.ContentTypes;
import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.util.WebKeys;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.portlet.PortletRequest;
import javax.portlet.PortletSession;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import nl.captcha.backgrounds.BackgroundProducer;
import nl.captcha.gimpy.GimpyRenderer;
import nl.captcha.noise.NoiseProducer;
import nl.captcha.servlet.CaptchaServletUtil;
import nl.captcha.text.producer.TextProducer;
import nl.captcha.text.renderer.WordRenderer;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
/**
* @author Brian Wing Shun Chan
* @author Daniel Sanz
*/
@Component(
configurationPid = "com.liferay.captcha.configuration.CaptchaConfiguration",
immediate = true,
property = {
"captcha.engine.impl=com.liferay.captcha.simplecaptcha.SimpleCaptchaImpl"
},
service = Captcha.class
)
public class SimpleCaptchaImpl implements Captcha {
@Override
public void check(HttpServletRequest request) throws CaptchaException {
if (!isEnabled(request)) {
return;
}
if (!validateChallenge(request)) {
throw new CaptchaTextException();
}
else {
incrementCounter(request);
}
if (_log.isDebugEnabled()) {
_log.debug("CAPTCHA text is valid");
}
}
@Override
public void check(PortletRequest portletRequest) throws CaptchaException {
if (!isEnabled(portletRequest)) {
return;
}
if (!validateChallenge(portletRequest)) {
throw new CaptchaTextException();
}
else {
incrementCounter(portletRequest);
}
if (_log.isDebugEnabled()) {
_log.debug("CAPTCHA text is valid");
}
}
@Override
public String getTaglibPath() {
return _TAGLIB_PATH;
}
@Override
public boolean isEnabled(HttpServletRequest request) {
if (isExceededMaxChallenges(request)) {
return false;
}
if (_captchaConfiguration.maxChallenges() >= 0) {
return true;
}
else {
return false;
}
}
@Override
public boolean isEnabled(PortletRequest portletRequest) {
if (isExceededMaxChallenges(portletRequest)) {
return false;
}
if (_captchaConfiguration.maxChallenges() >= 0) {
return true;
}
else {
return false;
}
}
@Override
public void serveImage(
HttpServletRequest request, HttpServletResponse response)
throws IOException {
HttpSession session = request.getSession();
nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
session.setAttribute(WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
response.setContentType(ContentTypes.IMAGE_PNG);
CaptchaServletUtil.writeImage(
response.getOutputStream(), simpleCaptcha.getImage());
}
@Override
public void serveImage(
ResourceRequest resourceRequest, ResourceResponse resourceResponse)
throws IOException {
PortletSession portletSession = resourceRequest.getPortletSession();
nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
portletSession.setAttribute(
WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
resourceResponse.setContentType(ContentTypes.IMAGE_PNG);
CaptchaServletUtil.writeImage(
resourceResponse.getPortletOutputStream(),
simpleCaptcha.getImage());
}
protected void activate() {
initBackgroundProducers();
initGimpyRenderers();
initNoiseProducers();
initTextProducers();
initWordRenderers();
}
@Activate
@Modified
protected void activate(Map<String, Object> properties) {
_captchaConfiguration = ConfigurableUtil.createConfigurable(
CaptchaConfiguration.class, properties);
activate();
}
protected BackgroundProducer getBackgroundProducer() {
if (_backgroundProducers.length == 1) {
return _backgroundProducers[0];
}
int pos = RandomUtil.nextInt(_backgroundProducers.length);
return _backgroundProducers[pos];
}
protected GimpyRenderer getGimpyRenderer() {
if (_gimpyRenderers.length == 1) {
return _gimpyRenderers[0];
}
int pos = RandomUtil.nextInt(_gimpyRenderers.length);
return _gimpyRenderers[pos];
}
protected int getHeight() {
return _captchaConfiguration.simpleCaptchaHeight();
}
protected NoiseProducer getNoiseProducer() {
if (_noiseProducers.length == 1) {
return _noiseProducers[0];
}
int pos = RandomUtil.nextInt(_noiseProducers.length);
return _noiseProducers[pos];
}
protected nl.captcha.Captcha getSimpleCaptcha() {
nl.captcha.Captcha.Builder captchaBuilder =
new nl.captcha.Captcha.Builder(getWidth(), getHeight());
captchaBuilder.addText(getTextProducer(), getWordRenderer());
captchaBuilder.addBackground(getBackgroundProducer());
captchaBuilder.gimp(getGimpyRenderer());
captchaBuilder.addNoise(getNoiseProducer());
captchaBuilder.addBorder();
return captchaBuilder.build();
}
protected TextProducer getTextProducer() {
if (_textProducers.length == 1) {
return _textProducers[0];
}
int pos = RandomUtil.nextInt(_textProducers.length);
return _textProducers[pos];
}
protected int getWidth() {
return _captchaConfiguration.simpleCaptchaWidth();
}
protected WordRenderer getWordRenderer() {
if (_wordRenderers.length == 1) {
return _wordRenderers[0];
}
int pos = RandomUtil.nextInt(_wordRenderers.length);
return _wordRenderers[pos];
}
protected void incrementCounter(HttpServletRequest request) {
if ((_captchaConfiguration.maxChallenges() > 0) &&
Validator.isNotNull(request.getRemoteUser())) {
HttpSession session = request.getSession();
Integer count = (Integer)session.getAttribute(
WebKeys.CAPTCHA_COUNT);
session.setAttribute(
WebKeys.CAPTCHA_COUNT, incrementCounter(count));
}
}
protected Integer incrementCounter(Integer count) {
if (count == null) {
count = Integer.valueOf(1);
}
else {
count = Integer.valueOf(count.intValue() + 1);
}
return count;
}
protected void incrementCounter(PortletRequest portletRequest) {
if ((_captchaConfiguration.maxChallenges() > 0) &&
Validator.isNotNull(portletRequest.getRemoteUser())) {
PortletSession portletSession = portletRequest.getPortletSession();
Integer count = (Integer)portletSession.getAttribute(
WebKeys.CAPTCHA_COUNT);
portletSession.setAttribute(
WebKeys.CAPTCHA_COUNT, incrementCounter(count));
}
}
protected void initBackgroundProducers() {
String[] backgroundProducerClassNames =
_captchaConfiguration.simpleCaptchaBackgroundProducers();
_backgroundProducers = new BackgroundProducer[
backgroundProducerClassNames.length];
for (int i = 0; i < backgroundProducerClassNames.length; i++) {
String backgroundProducerClassName =
backgroundProducerClassNames[i];
_backgroundProducers[i] = (BackgroundProducer)_getInstance(
backgroundProducerClassName);
}
}
protected void initGimpyRenderers() {
String[] gimpyRendererClassNames =
_captchaConfiguration.simpleCaptchaGimpyRenderers();
_gimpyRenderers = new GimpyRenderer[gimpyRendererClassNames.length];
for (int i = 0; i < gimpyRendererClassNames.length; i++) {
String gimpyRendererClassName = gimpyRendererClassNames[i];
_gimpyRenderers[i] = (GimpyRenderer)_getInstance(
gimpyRendererClassName);
}
}
protected void initNoiseProducers() {
String[] noiseProducerClassNames =
_captchaConfiguration.simpleCaptchaNoiseProducers();
_noiseProducers = new NoiseProducer[noiseProducerClassNames.length];
for (int i = 0; i < noiseProducerClassNames.length; i++) {
String noiseProducerClassName = noiseProducerClassNames[i];
_noiseProducers[i] = (NoiseProducer)_getInstance(
noiseProducerClassName);
}
}
protected void initTextProducers() {
String[] textProducerClassNames =
_captchaConfiguration.simpleCaptchaTextProducers();
_textProducers = new TextProducer[textProducerClassNames.length];
for (int i = 0; i < textProducerClassNames.length; i++) {
String textProducerClassName = textProducerClassNames[i];
_textProducers[i] = (TextProducer)_getInstance(
textProducerClassName);
}
}
protected void initWordRenderers() {
String[] wordRendererClassNames =
_captchaConfiguration.simpleCaptchaWordRenderers();
_wordRenderers = new WordRenderer[wordRendererClassNames.length];
for (int i = 0; i < wordRendererClassNames.length; i++) {
String wordRendererClassName = wordRendererClassNames[i];
_wordRenderers[i] = (WordRenderer)_getInstance(
wordRendererClassName);
}
}
protected boolean isExceededMaxChallenges(HttpServletRequest request) {
if (_captchaConfiguration.maxChallenges() > 0) {
HttpSession session = request.getSession();
Integer count = (Integer)session.getAttribute(
WebKeys.CAPTCHA_COUNT);
return isExceededMaxChallenges(count);
}
return false;
}
protected boolean isExceededMaxChallenges(Integer count) {
if ((count != null) &&
(count >= _captchaConfiguration.maxChallenges())) {
return true;
}
return false;
}
protected boolean isExceededMaxChallenges(PortletRequest portletRequest) {
if (_captchaConfiguration.maxChallenges() > 0) {
PortletSession portletSession = portletRequest.getPortletSession();
Integer count = (Integer)portletSession.getAttribute(
WebKeys.CAPTCHA_COUNT);
return isExceededMaxChallenges(count);
}
return false;
}
protected void setCaptchaConfiguration(
CaptchaConfiguration captchaConfiguration) {
_captchaConfiguration = captchaConfiguration;
}
protected boolean validateChallenge(HttpServletRequest request)
throws CaptchaException {
HttpSession session = request.getSession();
String captchaText = (String)session.getAttribute(WebKeys.CAPTCHA_TEXT);
if (request instanceof UploadPortletRequest) {
UploadPortletRequest uploadPortletRequest =
(UploadPortletRequest)request;
PortletRequest portletRequest =
uploadPortletRequest.getPortletRequest();
PortletSession portletSession = portletRequest.getPortletSession();
captchaText = (String)portletSession.getAttribute(
WebKeys.CAPTCHA_TEXT);
}
if (captchaText == null) {
_log.error(
"CAPTCHA text is null. User " + request.getRemoteUser() +
" may be trying to circumvent the CAPTCHA.");
throw new CaptchaTextException();
}
boolean valid = captchaText.equals(
ParamUtil.getString(request, "captchaText"));
if (valid) {
if (request instanceof UploadPortletRequest) {
UploadPortletRequest uploadPortletRequest =
(UploadPortletRequest)request;
PortletRequest portletRequest =
uploadPortletRequest.getPortletRequest();
PortletSession portletSession =
portletRequest.getPortletSession();
portletSession.removeAttribute(WebKeys.CAPTCHA_TEXT);
}
else {
session.removeAttribute(WebKeys.CAPTCHA_TEXT);
}
}
return valid;
}
protected boolean validateChallenge(PortletRequest portletRequest)
throws CaptchaException {
PortletSession portletSession = portletRequest.getPortletSession();
String captchaText = (String)portletSession.getAttribute(
WebKeys.CAPTCHA_TEXT);
if (captchaText == null) {
_log.error(
"CAPTCHA text is null. User " + portletRequest.getRemoteUser() +
" may be trying to circumvent the CAPTCHA.");
throw new CaptchaTextException();
}
boolean valid = captchaText.equals(
ParamUtil.getString(portletRequest, "captchaText"));
if (valid) {
portletSession.removeAttribute(WebKeys.CAPTCHA_TEXT);
}
return valid;
}
private Object _getInstance(String className) {
className = className.trim();
Object instance = _instances.get(className);
if (Validator.isNotNull(instance)) {
return instance;
}
try {
Class<?> clazz = _loadClass(className);
instance = clazz.newInstance();
_instances.put(className, instance);
}
catch (Exception e) {
_log.error("Unable to load " + className, e);
}
return instance;
}
private Class<?> _loadClass(String className) throws Exception {
Class<?> clazz = getClass();
ClassLoader classLoader = clazz.getClassLoader();
return classLoader.loadClass(className);
}
private static final String _TAGLIB_PATH = "/captcha/simplecaptcha.jsp";
private static final Log _log = LogFactoryUtil.getLog(
SimpleCaptchaImpl.class);
private BackgroundProducer[] _backgroundProducers;
private volatile CaptchaConfiguration _captchaConfiguration;
private GimpyRenderer[] _gimpyRenderers;
private final Map<String, Object> _instances = new ConcurrentHashMap<>();
private NoiseProducer[] _noiseProducers;
private TextProducer[] _textProducers;
private WordRenderer[] _wordRenderers;
}