001
014
015 package com.liferay.portal.captcha.simplecaptcha;
016
017 import com.liferay.portal.kernel.captcha.Captcha;
018 import com.liferay.portal.kernel.captcha.CaptchaException;
019 import com.liferay.portal.kernel.captcha.CaptchaTextException;
020 import com.liferay.portal.kernel.log.Log;
021 import com.liferay.portal.kernel.log.LogFactoryUtil;
022 import com.liferay.portal.kernel.security.RandomUtil;
023 import com.liferay.portal.kernel.upload.UploadPortletRequest;
024 import com.liferay.portal.kernel.util.ContentTypes;
025 import com.liferay.portal.kernel.util.InstancePool;
026 import com.liferay.portal.kernel.util.ParamUtil;
027 import com.liferay.portal.kernel.util.Validator;
028 import com.liferay.portal.kernel.util.WebKeys;
029 import com.liferay.portal.util.PropsValues;
030
031 import java.io.IOException;
032
033 import javax.portlet.PortletRequest;
034 import javax.portlet.PortletSession;
035 import javax.portlet.ResourceRequest;
036 import javax.portlet.ResourceResponse;
037
038 import javax.servlet.http.HttpServletRequest;
039 import javax.servlet.http.HttpServletResponse;
040 import javax.servlet.http.HttpSession;
041
042 import nl.captcha.backgrounds.BackgroundProducer;
043 import nl.captcha.gimpy.GimpyRenderer;
044 import nl.captcha.noise.NoiseProducer;
045 import nl.captcha.servlet.CaptchaServletUtil;
046 import nl.captcha.text.producer.TextProducer;
047 import nl.captcha.text.renderer.WordRenderer;
048
049
053 public class SimpleCaptchaImpl implements Captcha {
054
055 public SimpleCaptchaImpl() {
056 initBackgroundProducers();
057 initGimpyRenderers();
058 initNoiseProducers();
059 initTextProducers();
060 initWordRenderers();
061 }
062
063 @Override
064 public void check(HttpServletRequest request) throws CaptchaException {
065 if (!isEnabled(request)) {
066 return;
067 }
068
069 if (!validateChallenge(request)) {
070 throw new CaptchaTextException();
071 }
072 else {
073 incrementCounter(request);
074 }
075
076 if (_log.isDebugEnabled()) {
077 _log.debug("CAPTCHA text is valid");
078 }
079 }
080
081 @Override
082 public void check(PortletRequest portletRequest) throws CaptchaException {
083 if (!isEnabled(portletRequest)) {
084 return;
085 }
086
087 if (!validateChallenge(portletRequest)) {
088 throw new CaptchaTextException();
089 }
090 else {
091 incrementCounter(portletRequest);
092 }
093
094 if (_log.isDebugEnabled()) {
095 _log.debug("CAPTCHA text is valid");
096 }
097 }
098
099 @Override
100 public String getTaglibPath() {
101 return _TAGLIB_PATH;
102 }
103
104 @Override
105 public boolean isEnabled(HttpServletRequest request) {
106 if (isExceededMaxChallenges(request)) {
107 return false;
108 }
109
110 if (PropsValues.CAPTCHA_MAX_CHALLENGES >= 0) {
111 return true;
112 }
113 else {
114 return false;
115 }
116 }
117
118 @Override
119 public boolean isEnabled(PortletRequest portletRequest) {
120 if (isExceededMaxChallenges(portletRequest)) {
121 return false;
122 }
123
124 if (PropsValues.CAPTCHA_MAX_CHALLENGES >= 0) {
125 return true;
126 }
127 else {
128 return false;
129 }
130 }
131
132 @Override
133 public void serveImage(
134 HttpServletRequest request, HttpServletResponse response)
135 throws IOException {
136
137 HttpSession session = request.getSession();
138
139 nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
140
141 session.setAttribute(WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
142
143 response.setContentType(ContentTypes.IMAGE_PNG);
144
145 CaptchaServletUtil.writeImage(
146 response.getOutputStream(), simpleCaptcha.getImage());
147 }
148
149 @Override
150 public void serveImage(
151 ResourceRequest resourceRequest, ResourceResponse resourceResponse)
152 throws IOException {
153
154 PortletSession portletSession = resourceRequest.getPortletSession();
155
156 nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
157
158 portletSession.setAttribute(
159 WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
160
161 resourceResponse.setContentType(ContentTypes.IMAGE_PNG);
162
163 CaptchaServletUtil.writeImage(
164 resourceResponse.getPortletOutputStream(),
165 simpleCaptcha.getImage());
166 }
167
168 protected BackgroundProducer getBackgroundProducer() {
169 if (_backgroundProducers.length == 1) {
170 return _backgroundProducers[0];
171 }
172
173 int pos = RandomUtil.nextInt(_backgroundProducers.length);
174
175 return _backgroundProducers[pos];
176 }
177
178 protected GimpyRenderer getGimpyRenderer() {
179 if (_gimpyRenderers.length == 1) {
180 return _gimpyRenderers[0];
181 }
182
183 int pos = RandomUtil.nextInt(_gimpyRenderers.length);
184
185 return _gimpyRenderers[pos];
186 }
187
188 protected int getHeight() {
189 return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_HEIGHT;
190 }
191
192 protected NoiseProducer getNoiseProducer() {
193 if (_noiseProducers.length == 1) {
194 return _noiseProducers[0];
195 }
196
197 int pos = RandomUtil.nextInt(_noiseProducers.length);
198
199 return _noiseProducers[pos];
200 }
201
202 protected nl.captcha.Captcha getSimpleCaptcha() {
203 nl.captcha.Captcha.Builder captchaBuilder =
204 new nl.captcha.Captcha.Builder(getWidth(), getHeight());
205
206 captchaBuilder.addText(getTextProducer(), getWordRenderer());
207 captchaBuilder.addBackground(getBackgroundProducer());
208 captchaBuilder.gimp(getGimpyRenderer());
209 captchaBuilder.addNoise(getNoiseProducer());
210 captchaBuilder.addBorder();
211
212 return captchaBuilder.build();
213 }
214
215 protected TextProducer getTextProducer() {
216 if (_textProducers.length == 1) {
217 return _textProducers[0];
218 }
219
220 int pos = RandomUtil.nextInt(_textProducers.length);
221
222 return _textProducers[pos];
223 }
224
225 protected int getWidth() {
226 return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WIDTH;
227 }
228
229 protected WordRenderer getWordRenderer() {
230 if (_wordRenderers.length == 1) {
231 return _wordRenderers[0];
232 }
233
234 int pos = RandomUtil.nextInt(_wordRenderers.length);
235
236 return _wordRenderers[pos];
237 }
238
239 protected void incrementCounter(HttpServletRequest request) {
240 if ((PropsValues.CAPTCHA_MAX_CHALLENGES > 0) &&
241 Validator.isNotNull(request.getRemoteUser())) {
242
243 HttpSession session = request.getSession();
244
245 Integer count = (Integer)session.getAttribute(
246 WebKeys.CAPTCHA_COUNT);
247
248 session.setAttribute(
249 WebKeys.CAPTCHA_COUNT, incrementCounter(count));
250 }
251 }
252
253 protected Integer incrementCounter(Integer count) {
254 if (count == null) {
255 count = Integer.valueOf(1);
256 }
257 else {
258 count = Integer.valueOf(count.intValue() + 1);
259 }
260
261 return count;
262 }
263
264 protected void incrementCounter(PortletRequest portletRequest) {
265 if ((PropsValues.CAPTCHA_MAX_CHALLENGES > 0) &&
266 Validator.isNotNull(portletRequest.getRemoteUser())) {
267
268 PortletSession portletSession = portletRequest.getPortletSession();
269
270 Integer count = (Integer)portletSession.getAttribute(
271 WebKeys.CAPTCHA_COUNT);
272
273 portletSession.setAttribute(
274 WebKeys.CAPTCHA_COUNT, incrementCounter(count));
275 }
276 }
277
278 protected void initBackgroundProducers() {
279 String[] backgroundProducerClassNames =
280 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_BACKGROUND_PRODUCERS;
281
282 _backgroundProducers = new BackgroundProducer[
283 backgroundProducerClassNames.length];
284
285 for (int i = 0; i < backgroundProducerClassNames.length; i++) {
286 String backgroundProducerClassName =
287 backgroundProducerClassNames[i];
288
289 _backgroundProducers[i] = (BackgroundProducer)InstancePool.get(
290 backgroundProducerClassName);
291 }
292 }
293
294 protected void initGimpyRenderers() {
295 String[] gimpyRendererClassNames =
296 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_GIMPY_RENDERERS;
297
298 _gimpyRenderers = new GimpyRenderer[gimpyRendererClassNames.length];
299
300 for (int i = 0; i < gimpyRendererClassNames.length; i++) {
301 String gimpyRendererClassName = gimpyRendererClassNames[i];
302
303 _gimpyRenderers[i] = (GimpyRenderer)InstancePool.get(
304 gimpyRendererClassName);
305 }
306 }
307
308 protected void initNoiseProducers() {
309 String[] noiseProducerClassNames =
310 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_NOISE_PRODUCERS;
311
312 _noiseProducers = new NoiseProducer[noiseProducerClassNames.length];
313
314 for (int i = 0; i < noiseProducerClassNames.length; i++) {
315 String noiseProducerClassName = noiseProducerClassNames[i];
316
317 _noiseProducers[i] = (NoiseProducer)InstancePool.get(
318 noiseProducerClassName);
319 }
320 }
321
322 protected void initTextProducers() {
323 String[] textProducerClassNames =
324 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_TEXT_PRODUCERS;
325
326 _textProducers = new TextProducer[textProducerClassNames.length];
327
328 for (int i = 0; i < textProducerClassNames.length; i++) {
329 String textProducerClassName = textProducerClassNames[i];
330
331 _textProducers[i] = (TextProducer)InstancePool.get(
332 textProducerClassName);
333 }
334 }
335
336 protected void initWordRenderers() {
337 String[] wordRendererClassNames =
338 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WORD_RENDERERS;
339
340 _wordRenderers = new WordRenderer[wordRendererClassNames.length];
341
342 for (int i = 0; i < wordRendererClassNames.length; i++) {
343 String wordRendererClassName = wordRendererClassNames[i];
344
345 _wordRenderers[i] = (WordRenderer)InstancePool.get(
346 wordRendererClassName);
347 }
348 }
349
350 protected boolean isExceededMaxChallenges(HttpServletRequest request) {
351 if (PropsValues.CAPTCHA_MAX_CHALLENGES > 0) {
352 HttpSession session = request.getSession();
353
354 Integer count = (Integer)session.getAttribute(
355 WebKeys.CAPTCHA_COUNT);
356
357 return isExceededMaxChallenges(count);
358 }
359
360 return false;
361 }
362
363 protected boolean isExceededMaxChallenges(Integer count) {
364 if ((count != null) && (count >= PropsValues.CAPTCHA_MAX_CHALLENGES)) {
365 return true;
366 }
367
368 return false;
369 }
370
371 protected boolean isExceededMaxChallenges(PortletRequest portletRequest) {
372 if (PropsValues.CAPTCHA_MAX_CHALLENGES > 0) {
373 PortletSession portletSession = portletRequest.getPortletSession();
374
375 Integer count = (Integer)portletSession.getAttribute(
376 WebKeys.CAPTCHA_COUNT);
377
378 return isExceededMaxChallenges(count);
379 }
380
381 return false;
382 }
383
384 protected boolean validateChallenge(HttpServletRequest request)
385 throws CaptchaException {
386
387 HttpSession session = request.getSession();
388
389 String captchaText = (String)session.getAttribute(WebKeys.CAPTCHA_TEXT);
390
391 if (request instanceof UploadPortletRequest) {
392 UploadPortletRequest uploadPortletRequest =
393 (UploadPortletRequest)request;
394
395 PortletRequest portletRequest =
396 uploadPortletRequest.getPortletRequest();
397
398 PortletSession portletSession = portletRequest.getPortletSession();
399
400 captchaText = (String)portletSession.getAttribute(
401 WebKeys.CAPTCHA_TEXT);
402 }
403
404 if (captchaText == null) {
405 _log.error(
406 "CAPTCHA text is null. User " + request.getRemoteUser() +
407 " may be trying to circumvent the CAPTCHA.");
408
409 throw new CaptchaTextException();
410 }
411
412 boolean valid = captchaText.equals(
413 ParamUtil.getString(request, "captchaText"));
414
415 if (valid) {
416 if (request instanceof UploadPortletRequest) {
417 UploadPortletRequest uploadPortletRequest =
418 (UploadPortletRequest)request;
419
420 PortletRequest portletRequest =
421 uploadPortletRequest.getPortletRequest();
422
423 PortletSession portletSession =
424 portletRequest.getPortletSession();
425
426 portletSession.removeAttribute(WebKeys.CAPTCHA_TEXT);
427 }
428 else {
429 session.removeAttribute(WebKeys.CAPTCHA_TEXT);
430 }
431 }
432
433 return valid;
434 }
435
436 protected boolean validateChallenge(PortletRequest portletRequest)
437 throws CaptchaException {
438
439 PortletSession portletSession = portletRequest.getPortletSession();
440
441 String captchaText = (String)portletSession.getAttribute(
442 WebKeys.CAPTCHA_TEXT);
443
444 if (captchaText == null) {
445 _log.error(
446 "CAPTCHA text is null. User " + portletRequest.getRemoteUser() +
447 " may be trying to circumvent the CAPTCHA.");
448
449 throw new CaptchaTextException();
450 }
451
452 boolean valid = captchaText.equals(
453 ParamUtil.getString(portletRequest, "captchaText"));
454
455 if (valid) {
456 portletSession.removeAttribute(WebKeys.CAPTCHA_TEXT);
457 }
458
459 return valid;
460 }
461
462 private static final String _TAGLIB_PATH =
463 "/html/taglib/ui/captcha/simplecaptcha.jsp";
464
465 private static final Log _log = LogFactoryUtil.getLog(
466 SimpleCaptchaImpl.class);
467
468 private BackgroundProducer[] _backgroundProducers;
469 private GimpyRenderer[] _gimpyRenderers;
470 private NoiseProducer[] _noiseProducers;
471 private TextProducer[] _textProducers;
472 private WordRenderer[] _wordRenderers;
473
474 }